Detailed changes
@@ -1,56 +1,47 @@
-mod access_token;
-mod channel;
-mod channel_member;
-mod channel_path;
-mod contact;
-mod follower;
-mod language_server;
-mod project;
-mod project_collaborator;
-mod room;
-mod room_participant;
-mod server;
-mod signup;
#[cfg(test)]
-mod tests;
-mod user;
-mod worktree;
-mod worktree_diagnostic_summary;
-mod worktree_entry;
-mod worktree_repository;
-mod worktree_repository_statuses;
-mod worktree_settings_file;
+mod db_tests;
+mod ids;
+mod tables;
+#[cfg(test)]
+pub mod test_db;
use crate::executor::Executor;
use crate::{Error, Result};
use anyhow::anyhow;
use collections::{BTreeMap, HashMap, HashSet};
-pub use contact::Contact;
use dashmap::DashMap;
use futures::StreamExt;
use hyper::StatusCode;
use rand::prelude::StdRng;
use rand::{Rng, SeedableRng};
use rpc::{proto, ConnectionId};
-use sea_orm::Condition;
-pub use sea_orm::ConnectOptions;
use sea_orm::{
- entity::prelude::*, ActiveValue, ConnectionTrait, DatabaseConnection, DatabaseTransaction,
- DbErr, FromQueryResult, IntoActiveModel, IsolationLevel, JoinType, QueryOrder, QuerySelect,
- Statement, TransactionTrait,
+ entity::prelude::*, ActiveValue, Condition, ConnectionTrait, DatabaseConnection,
+ DatabaseTransaction, DbErr, FromQueryResult, IntoActiveModel, IsolationLevel, JoinType,
+ QueryOrder, QuerySelect, Statement, TransactionTrait,
};
use sea_query::{Alias, Expr, OnConflict, Query};
use serde::{Deserialize, Serialize};
-pub use signup::{Invite, NewSignup, WaitlistSummary};
-use sqlx::migrate::{Migrate, Migration, MigrationSource};
-use sqlx::Connection;
-use std::fmt::Write as _;
-use std::ops::{Deref, DerefMut};
-use std::path::Path;
-use std::time::Duration;
-use std::{future::Future, marker::PhantomData, rc::Rc, sync::Arc};
+use sqlx::{
+ migrate::{Migrate, Migration, MigrationSource},
+ Connection,
+};
+use std::{
+ fmt::Write as _,
+ future::Future,
+ marker::PhantomData,
+ ops::{Deref, DerefMut},
+ path::Path,
+ rc::Rc,
+ sync::Arc,
+ time::Duration,
+};
+use tables::*;
use tokio::sync::{Mutex, OwnedMutexGuard};
-pub use user::Model as User;
+
+pub use ids::*;
+pub use sea_orm::ConnectOptions;
+pub use tables::user::Model as User;
pub struct Database {
options: ConnectOptions,
@@ -4083,6 +4074,60 @@ impl<T> RoomGuard<T> {
}
}
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Contact {
+ Accepted {
+ user_id: UserId,
+ should_notify: bool,
+ busy: bool,
+ },
+ Outgoing {
+ user_id: UserId,
+ },
+ Incoming {
+ user_id: UserId,
+ should_notify: bool,
+ },
+}
+
+impl Contact {
+ pub fn user_id(&self) -> UserId {
+ match self {
+ Contact::Accepted { user_id, .. } => *user_id,
+ Contact::Outgoing { user_id } => *user_id,
+ Contact::Incoming { user_id, .. } => *user_id,
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
+pub struct Invite {
+ pub email_address: String,
+ pub email_confirmation_code: String,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct NewSignup {
+ pub email_address: String,
+ pub platform_mac: bool,
+ pub platform_windows: bool,
+ pub platform_linux: bool,
+ pub editor_features: Vec<String>,
+ pub programming_languages: Vec<String>,
+ pub device_id: Option<String>,
+ pub added_to_mailing_list: bool,
+ pub created_at: Option<DateTime>,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromQueryResult)]
+pub struct WaitlistSummary {
+ pub count: i64,
+ pub linux_count: i64,
+ pub mac_count: i64,
+ pub windows_count: i64,
+ pub unknown_count: i64,
+}
+
#[derive(Debug, Serialize, Deserialize)]
pub struct NewUserParams {
pub github_login: String,
@@ -4120,139 +4165,6 @@ fn random_email_confirmation_code() -> String {
nanoid::nanoid!(64)
}
-macro_rules! id_type {
- ($name:ident) => {
- #[derive(
- Clone,
- Copy,
- Debug,
- Default,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- Serialize,
- Deserialize,
- )]
- #[serde(transparent)]
- pub struct $name(pub i32);
-
- impl $name {
- #[allow(unused)]
- pub const MAX: Self = Self(i32::MAX);
-
- #[allow(unused)]
- pub fn from_proto(value: u64) -> Self {
- Self(value as i32)
- }
-
- #[allow(unused)]
- pub fn to_proto(self) -> u64 {
- self.0 as u64
- }
- }
-
- impl std::fmt::Display for $name {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- self.0.fmt(f)
- }
- }
-
- impl From<$name> for sea_query::Value {
- fn from(value: $name) -> Self {
- sea_query::Value::Int(Some(value.0))
- }
- }
-
- impl sea_orm::TryGetable for $name {
- fn try_get(
- res: &sea_orm::QueryResult,
- pre: &str,
- col: &str,
- ) -> Result<Self, sea_orm::TryGetError> {
- Ok(Self(i32::try_get(res, pre, col)?))
- }
- }
-
- impl sea_query::ValueType for $name {
- fn try_from(v: Value) -> Result<Self, sea_query::ValueTypeErr> {
- match v {
- Value::TinyInt(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::SmallInt(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::Int(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::BigInt(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::TinyUnsigned(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::SmallUnsigned(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::Unsigned(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- Value::BigUnsigned(Some(int)) => {
- Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?))
- }
- _ => Err(sea_query::ValueTypeErr),
- }
- }
-
- fn type_name() -> String {
- stringify!($name).into()
- }
-
- fn array_type() -> sea_query::ArrayType {
- sea_query::ArrayType::Int
- }
-
- fn column_type() -> sea_query::ColumnType {
- sea_query::ColumnType::Integer(None)
- }
- }
-
- impl sea_orm::TryFromU64 for $name {
- fn try_from_u64(n: u64) -> Result<Self, DbErr> {
- Ok(Self(n.try_into().map_err(|_| {
- DbErr::ConvertFromU64(concat!(
- "error converting ",
- stringify!($name),
- " to u64"
- ))
- })?))
- }
- }
-
- impl sea_query::Nullable for $name {
- fn null() -> Value {
- Value::Int(None)
- }
- }
- };
-}
-
-id_type!(AccessTokenId);
-id_type!(ChannelId);
-id_type!(ChannelMemberId);
-id_type!(ContactId);
-id_type!(FollowerId);
-id_type!(RoomId);
-id_type!(RoomParticipantId);
-id_type!(ProjectId);
-id_type!(ProjectCollaboratorId);
-id_type!(ReplicaId);
-id_type!(ServerId);
-id_type!(SignupId);
-id_type!(UserId);
-
#[derive(Clone)]
pub struct JoinRoom {
pub room: proto::Room,
@@ -4370,130 +4282,3 @@ pub struct WorktreeSettingsFile {
enum QueryUserIds {
UserId,
}
-
-#[cfg(test)]
-pub use test::*;
-
-#[cfg(test)]
-mod test {
- use super::*;
- use gpui::executor::Background;
- use parking_lot::Mutex;
- use sea_orm::ConnectionTrait;
- use sqlx::migrate::MigrateDatabase;
- use std::sync::Arc;
-
- pub struct TestDb {
- pub db: Option<Arc<Database>>,
- pub connection: Option<sqlx::AnyConnection>,
- }
-
- impl TestDb {
- pub fn sqlite(background: Arc<Background>) -> Self {
- let url = format!("sqlite::memory:");
- let runtime = tokio::runtime::Builder::new_current_thread()
- .enable_io()
- .enable_time()
- .build()
- .unwrap();
-
- let mut db = runtime.block_on(async {
- let mut options = ConnectOptions::new(url);
- options.max_connections(5);
- let db = Database::new(options, Executor::Deterministic(background))
- .await
- .unwrap();
- let sql = include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/migrations.sqlite/20221109000000_test_schema.sql"
- ));
- db.pool
- .execute(sea_orm::Statement::from_string(
- db.pool.get_database_backend(),
- sql.into(),
- ))
- .await
- .unwrap();
- db
- });
-
- db.runtime = Some(runtime);
-
- Self {
- db: Some(Arc::new(db)),
- connection: None,
- }
- }
-
- pub fn postgres(background: Arc<Background>) -> Self {
- static LOCK: Mutex<()> = Mutex::new(());
-
- let _guard = LOCK.lock();
- let mut rng = StdRng::from_entropy();
- let url = format!(
- "postgres://postgres@localhost/zed-test-{}",
- rng.gen::<u128>()
- );
- let runtime = tokio::runtime::Builder::new_current_thread()
- .enable_io()
- .enable_time()
- .build()
- .unwrap();
-
- let mut db = runtime.block_on(async {
- sqlx::Postgres::create_database(&url)
- .await
- .expect("failed to create test db");
- let mut options = ConnectOptions::new(url);
- options
- .max_connections(5)
- .idle_timeout(Duration::from_secs(0));
- let db = Database::new(options, Executor::Deterministic(background))
- .await
- .unwrap();
- let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
- db.migrate(Path::new(migrations_path), false).await.unwrap();
- db
- });
-
- db.runtime = Some(runtime);
-
- Self {
- db: Some(Arc::new(db)),
- connection: None,
- }
- }
-
- pub fn db(&self) -> &Arc<Database> {
- self.db.as_ref().unwrap()
- }
- }
-
- impl Drop for TestDb {
- fn drop(&mut self) {
- let db = self.db.take().unwrap();
- if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() {
- db.runtime.as_ref().unwrap().block_on(async {
- use util::ResultExt;
- let query = "
- SELECT pg_terminate_backend(pg_stat_activity.pid)
- FROM pg_stat_activity
- WHERE
- pg_stat_activity.datname = current_database() AND
- pid <> pg_backend_pid();
- ";
- db.pool
- .execute(sea_orm::Statement::from_string(
- db.pool.get_database_backend(),
- query.into(),
- ))
- .await
- .log_err();
- sqlx::Postgres::drop_database(db.options.get_url())
- .await
- .log_err();
- })
- }
- }
- }
-}
@@ -1,9 +1,8 @@
use super::*;
use gpui::executor::{Background, Deterministic};
-use std::sync::Arc;
-
-#[cfg(test)]
use pretty_assertions::{assert_eq, assert_ne};
+use std::sync::Arc;
+use test_db::TestDb;
macro_rules! test_both_dbs {
($postgres_test_name:ident, $sqlite_test_name:ident, $db:ident, $body:block) => {
@@ -0,0 +1,125 @@
+use crate::Result;
+use sea_orm::DbErr;
+use sea_query::{Value, ValueTypeErr};
+use serde::{Deserialize, Serialize};
+
+macro_rules! id_type {
+ ($name:ident) => {
+ #[derive(
+ Clone,
+ Copy,
+ Debug,
+ Default,
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ Hash,
+ Serialize,
+ Deserialize,
+ )]
+ #[serde(transparent)]
+ pub struct $name(pub i32);
+
+ impl $name {
+ #[allow(unused)]
+ pub const MAX: Self = Self(i32::MAX);
+
+ #[allow(unused)]
+ pub fn from_proto(value: u64) -> Self {
+ Self(value as i32)
+ }
+
+ #[allow(unused)]
+ pub fn to_proto(self) -> u64 {
+ self.0 as u64
+ }
+ }
+
+ impl std::fmt::Display for $name {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+ }
+
+ impl From<$name> for sea_query::Value {
+ fn from(value: $name) -> Self {
+ sea_query::Value::Int(Some(value.0))
+ }
+ }
+
+ impl sea_orm::TryGetable for $name {
+ fn try_get(
+ res: &sea_orm::QueryResult,
+ pre: &str,
+ col: &str,
+ ) -> Result<Self, sea_orm::TryGetError> {
+ Ok(Self(i32::try_get(res, pre, col)?))
+ }
+ }
+
+ impl sea_query::ValueType for $name {
+ fn try_from(v: Value) -> Result<Self, sea_query::ValueTypeErr> {
+ Ok(Self(value_to_integer(v)?))
+ }
+
+ fn type_name() -> String {
+ stringify!($name).into()
+ }
+
+ fn array_type() -> sea_query::ArrayType {
+ sea_query::ArrayType::Int
+ }
+
+ fn column_type() -> sea_query::ColumnType {
+ sea_query::ColumnType::Integer(None)
+ }
+ }
+
+ impl sea_orm::TryFromU64 for $name {
+ fn try_from_u64(n: u64) -> Result<Self, DbErr> {
+ Ok(Self(n.try_into().map_err(|_| {
+ DbErr::ConvertFromU64(concat!(
+ "error converting ",
+ stringify!($name),
+ " to u64"
+ ))
+ })?))
+ }
+ }
+
+ impl sea_query::Nullable for $name {
+ fn null() -> Value {
+ Value::Int(None)
+ }
+ }
+ };
+}
+
+fn value_to_integer(v: Value) -> Result<i32, ValueTypeErr> {
+ match v {
+ Value::TinyInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::SmallInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::Int(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::BigInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::TinyUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::SmallUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::Unsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ Value::BigUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr),
+ _ => Err(ValueTypeErr),
+ }
+}
+
+id_type!(AccessTokenId);
+id_type!(ChannelId);
+id_type!(ChannelMemberId);
+id_type!(ContactId);
+id_type!(FollowerId);
+id_type!(RoomId);
+id_type!(RoomParticipantId);
+id_type!(ProjectId);
+id_type!(ProjectCollaboratorId);
+id_type!(ReplicaId);
+id_type!(ServerId);
+id_type!(SignupId);
+id_type!(UserId);
@@ -1,57 +0,0 @@
-use super::{SignupId, UserId};
-use sea_orm::{entity::prelude::*, FromQueryResult};
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "signups")]
-pub struct Model {
- #[sea_orm(primary_key)]
- pub id: SignupId,
- pub email_address: String,
- pub email_confirmation_code: String,
- pub email_confirmation_sent: bool,
- pub created_at: DateTime,
- pub device_id: Option<String>,
- pub user_id: Option<UserId>,
- pub inviting_user_id: Option<UserId>,
- pub platform_mac: bool,
- pub platform_linux: bool,
- pub platform_windows: bool,
- pub platform_unknown: bool,
- pub editor_features: Option<Vec<String>>,
- pub programming_languages: Option<Vec<String>>,
- pub added_to_mailing_list: bool,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {}
-
-impl ActiveModelBehavior for ActiveModel {}
-
-#[derive(Clone, Debug, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)]
-pub struct Invite {
- pub email_address: String,
- pub email_confirmation_code: String,
-}
-
-#[derive(Clone, Debug, Deserialize)]
-pub struct NewSignup {
- pub email_address: String,
- pub platform_mac: bool,
- pub platform_windows: bool,
- pub platform_linux: bool,
- pub editor_features: Vec<String>,
- pub programming_languages: Vec<String>,
- pub device_id: Option<String>,
- pub added_to_mailing_list: bool,
- pub created_at: Option<DateTime>,
-}
-
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromQueryResult)]
-pub struct WaitlistSummary {
- pub count: i64,
- pub linux_count: i64,
- pub mac_count: i64,
- pub windows_count: i64,
- pub unknown_count: i64,
-}
@@ -0,0 +1,20 @@
+pub mod access_token;
+pub mod channel;
+pub mod channel_member;
+pub mod channel_path;
+pub mod contact;
+pub mod follower;
+pub mod language_server;
+pub mod project;
+pub mod project_collaborator;
+pub mod room;
+pub mod room_participant;
+pub mod server;
+pub mod signup;
+pub mod user;
+pub mod worktree;
+pub mod worktree_diagnostic_summary;
+pub mod worktree_entry;
+pub mod worktree_repository;
+pub mod worktree_repository_statuses;
+pub mod worktree_settings_file;
@@ -1,4 +1,4 @@
-use super::{AccessTokenId, UserId};
+use crate::db::{AccessTokenId, UserId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ChannelId;
+use crate::db::ChannelId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
@@ -30,9 +30,3 @@ impl Related<super::room::Entity> for Entity {
Relation::Room.def()
}
}
-
-// impl Related<super::follower::Entity> for Entity {
-// fn to() -> RelationDef {
-// Relation::Follower.def()
-// }
-// }
@@ -1,6 +1,4 @@
-use crate::db::channel_member;
-
-use super::{ChannelId, ChannelMemberId, UserId};
+use crate::db::{channel_member, ChannelId, ChannelMemberId, UserId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ChannelId;
+use crate::db::ChannelId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::{ContactId, UserId};
+use crate::db::{ContactId, UserId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
@@ -30,29 +30,3 @@ pub enum Relation {
}
impl ActiveModelBehavior for ActiveModel {}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Contact {
- Accepted {
- user_id: UserId,
- should_notify: bool,
- busy: bool,
- },
- Outgoing {
- user_id: UserId,
- },
- Incoming {
- user_id: UserId,
- should_notify: bool,
- },
-}
-
-impl Contact {
- pub fn user_id(&self) -> UserId {
- match self {
- Contact::Accepted { user_id, .. } => *user_id,
- Contact::Outgoing { user_id } => *user_id,
- Contact::Incoming { user_id, .. } => *user_id,
- }
- }
-}
@@ -1,9 +1,8 @@
-use super::{FollowerId, ProjectId, RoomId, ServerId};
+use crate::db::{FollowerId, ProjectId, RoomId, ServerId};
use rpc::ConnectionId;
use sea_orm::entity::prelude::*;
-use serde::Serialize;
-#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel, Serialize)]
+#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "followers")]
pub struct Model {
#[sea_orm(primary_key)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::{ProjectId, Result, RoomId, ServerId, UserId};
+use crate::db::{ProjectId, Result, RoomId, ServerId, UserId};
use anyhow::anyhow;
use rpc::ConnectionId;
use sea_orm::entity::prelude::*;
@@ -1,4 +1,4 @@
-use super::{ProjectCollaboratorId, ProjectId, ReplicaId, ServerId, UserId};
+use crate::db::{ProjectCollaboratorId, ProjectId, ReplicaId, ServerId, UserId};
use rpc::ConnectionId;
use sea_orm::entity::prelude::*;
@@ -1,4 +1,4 @@
-use super::{ChannelId, RoomId};
+use crate::db::{ChannelId, RoomId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Default, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
+use crate::db::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ServerId;
+use crate::db::ServerId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -0,0 +1,28 @@
+use crate::db::{SignupId, UserId};
+use sea_orm::entity::prelude::*;
+
+#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
+#[sea_orm(table_name = "signups")]
+pub struct Model {
+ #[sea_orm(primary_key)]
+ pub id: SignupId,
+ pub email_address: String,
+ pub email_confirmation_code: String,
+ pub email_confirmation_sent: bool,
+ pub created_at: DateTime,
+ pub device_id: Option<String>,
+ pub user_id: Option<UserId>,
+ pub inviting_user_id: Option<UserId>,
+ pub platform_mac: bool,
+ pub platform_linux: bool,
+ pub platform_windows: bool,
+ pub platform_unknown: bool,
+ pub editor_features: Option<Vec<String>>,
+ pub programming_languages: Option<Vec<String>>,
+ pub added_to_mailing_list: bool,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {}
+
+impl ActiveModelBehavior for ActiveModel {}
@@ -1,4 +1,4 @@
-use super::UserId;
+use crate::db::UserId;
use sea_orm::entity::prelude::*;
use serde::Serialize;
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -1,4 +1,4 @@
-use super::ProjectId;
+use crate::db::ProjectId;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
@@ -0,0 +1,120 @@
+use super::*;
+use gpui::executor::Background;
+use parking_lot::Mutex;
+use sea_orm::ConnectionTrait;
+use sqlx::migrate::MigrateDatabase;
+use std::sync::Arc;
+
+pub struct TestDb {
+ pub db: Option<Arc<Database>>,
+ pub connection: Option<sqlx::AnyConnection>,
+}
+
+impl TestDb {
+ pub fn sqlite(background: Arc<Background>) -> Self {
+ let url = format!("sqlite::memory:");
+ let runtime = tokio::runtime::Builder::new_current_thread()
+ .enable_io()
+ .enable_time()
+ .build()
+ .unwrap();
+
+ let mut db = runtime.block_on(async {
+ let mut options = ConnectOptions::new(url);
+ options.max_connections(5);
+ let db = Database::new(options, Executor::Deterministic(background))
+ .await
+ .unwrap();
+ let sql = include_str!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/migrations.sqlite/20221109000000_test_schema.sql"
+ ));
+ db.pool
+ .execute(sea_orm::Statement::from_string(
+ db.pool.get_database_backend(),
+ sql.into(),
+ ))
+ .await
+ .unwrap();
+ db
+ });
+
+ db.runtime = Some(runtime);
+
+ Self {
+ db: Some(Arc::new(db)),
+ connection: None,
+ }
+ }
+
+ pub fn postgres(background: Arc<Background>) -> Self {
+ static LOCK: Mutex<()> = Mutex::new(());
+
+ let _guard = LOCK.lock();
+ let mut rng = StdRng::from_entropy();
+ let url = format!(
+ "postgres://postgres@localhost/zed-test-{}",
+ rng.gen::<u128>()
+ );
+ let runtime = tokio::runtime::Builder::new_current_thread()
+ .enable_io()
+ .enable_time()
+ .build()
+ .unwrap();
+
+ let mut db = runtime.block_on(async {
+ sqlx::Postgres::create_database(&url)
+ .await
+ .expect("failed to create test db");
+ let mut options = ConnectOptions::new(url);
+ options
+ .max_connections(5)
+ .idle_timeout(Duration::from_secs(0));
+ let db = Database::new(options, Executor::Deterministic(background))
+ .await
+ .unwrap();
+ let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
+ db.migrate(Path::new(migrations_path), false).await.unwrap();
+ db
+ });
+
+ db.runtime = Some(runtime);
+
+ Self {
+ db: Some(Arc::new(db)),
+ connection: None,
+ }
+ }
+
+ pub fn db(&self) -> &Arc<Database> {
+ self.db.as_ref().unwrap()
+ }
+}
+
+impl Drop for TestDb {
+ fn drop(&mut self) {
+ let db = self.db.take().unwrap();
+ if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() {
+ db.runtime.as_ref().unwrap().block_on(async {
+ use util::ResultExt;
+ let query = "
+ SELECT pg_terminate_backend(pg_stat_activity.pid)
+ FROM pg_stat_activity
+ WHERE
+ pg_stat_activity.datname = current_database() AND
+ pid <> pg_backend_pid();
+ ";
+ db.pool
+ .execute(sea_orm::Statement::from_string(
+ db.pool.get_database_backend(),
+ query.into(),
+ ))
+ .await
+ .log_err();
+ sqlx::Postgres::drop_database(db.options.get_url())
+ .await
+ .log_err();
+ })
+ }
+ }
+}
@@ -1,5 +1,5 @@
use crate::{
- db::{NewUserParams, TestDb, UserId},
+ db::{test_db::TestDb, NewUserParams, UserId},
executor::Executor,
rpc::{Server, CLEANUP_TIMEOUT},
AppState,