1mod buffer_tests;
2mod db_tests;
3mod feature_flag_tests;
4
5use super::*;
6use gpui::executor::Background;
7use parking_lot::Mutex;
8use sea_orm::ConnectionTrait;
9use sqlx::migrate::MigrateDatabase;
10use std::sync::Arc;
11
12pub struct TestDb {
13 pub db: Option<Arc<Database>>,
14 pub connection: Option<sqlx::AnyConnection>,
15}
16
17impl TestDb {
18 pub fn sqlite(background: Arc<Background>) -> Self {
19 let url = format!("sqlite::memory:");
20 let runtime = tokio::runtime::Builder::new_current_thread()
21 .enable_io()
22 .enable_time()
23 .build()
24 .unwrap();
25
26 let mut db = runtime.block_on(async {
27 let mut options = ConnectOptions::new(url);
28 options.max_connections(5);
29 let db = Database::new(options, Executor::Deterministic(background))
30 .await
31 .unwrap();
32 let sql = include_str!(concat!(
33 env!("CARGO_MANIFEST_DIR"),
34 "/migrations.sqlite/20221109000000_test_schema.sql"
35 ));
36 db.pool
37 .execute(sea_orm::Statement::from_string(
38 db.pool.get_database_backend(),
39 sql.into(),
40 ))
41 .await
42 .unwrap();
43 db
44 });
45
46 db.runtime = Some(runtime);
47
48 Self {
49 db: Some(Arc::new(db)),
50 connection: None,
51 }
52 }
53
54 pub fn postgres(background: Arc<Background>) -> Self {
55 static LOCK: Mutex<()> = Mutex::new(());
56
57 let _guard = LOCK.lock();
58 let mut rng = StdRng::from_entropy();
59 let url = format!(
60 "postgres://postgres@localhost/zed-test-{}",
61 rng.gen::<u128>()
62 );
63 let runtime = tokio::runtime::Builder::new_current_thread()
64 .enable_io()
65 .enable_time()
66 .build()
67 .unwrap();
68
69 let mut db = runtime.block_on(async {
70 sqlx::Postgres::create_database(&url)
71 .await
72 .expect("failed to create test db");
73 let mut options = ConnectOptions::new(url);
74 options
75 .max_connections(5)
76 .idle_timeout(Duration::from_secs(0));
77 let db = Database::new(options, Executor::Deterministic(background))
78 .await
79 .unwrap();
80 let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations");
81 db.migrate(Path::new(migrations_path), false).await.unwrap();
82 db
83 });
84
85 db.runtime = Some(runtime);
86
87 Self {
88 db: Some(Arc::new(db)),
89 connection: None,
90 }
91 }
92
93 pub fn db(&self) -> &Arc<Database> {
94 self.db.as_ref().unwrap()
95 }
96}
97
98#[macro_export]
99macro_rules! test_both_dbs {
100 ($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => {
101 #[gpui::test]
102 async fn $postgres_test_name() {
103 let test_db = crate::db::TestDb::postgres(
104 gpui::executor::Deterministic::new(0).build_background(),
105 );
106 $test_name(test_db.db()).await;
107 }
108
109 #[gpui::test]
110 async fn $sqlite_test_name() {
111 let test_db =
112 crate::db::TestDb::sqlite(gpui::executor::Deterministic::new(0).build_background());
113 $test_name(test_db.db()).await;
114 }
115 };
116}
117
118impl Drop for TestDb {
119 fn drop(&mut self) {
120 let db = self.db.take().unwrap();
121 if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() {
122 db.runtime.as_ref().unwrap().block_on(async {
123 use util::ResultExt;
124 let query = "
125 SELECT pg_terminate_backend(pg_stat_activity.pid)
126 FROM pg_stat_activity
127 WHERE
128 pg_stat_activity.datname = current_database() AND
129 pid <> pg_backend_pid();
130 ";
131 db.pool
132 .execute(sea_orm::Statement::from_string(
133 db.pool.get_database_backend(),
134 query.into(),
135 ))
136 .await
137 .log_err();
138 sqlx::Postgres::drop_database(db.options.get_url())
139 .await
140 .log_err();
141 })
142 }
143 }
144}