Go back to a compiling state and start running tests again

Antonio Scandurra created

Change summary

crates/collab/src/db.rs                | 108 +--
crates/collab/src/db_tests.rs          | 688 +++++++++++++--------------
crates/collab/src/integration_tests.rs |   2 
crates/collab/src/main.rs              |   5 
4 files changed, 383 insertions(+), 420 deletions(-)

Detailed changes

crates/collab/src/db.rs 🔗

@@ -12,8 +12,16 @@ use sqlx::{
 use std::{cmp, ops::Range, path::Path, time::Duration};
 use time::{OffsetDateTime, PrimitiveDateTime};
 
+#[cfg(test)]
+pub type DefaultDb = Db<sqlx::Sqlite>;
+
+#[cfg(not(test))]
+pub type DefaultDb = Db<sqlx::Postgres>;
+
 pub struct Db<D: sqlx::Database> {
     pool: sqlx::Pool<D>,
+    #[cfg(test)]
+    background: Option<std::sync::Arc<gpui::executor::Background>>,
 }
 
 macro_rules! test_support {
@@ -23,6 +31,10 @@ macro_rules! test_support {
         };
 
         if cfg!(test) {
+            #[cfg(test)]
+            if let Some(background) = $self.background.as_ref() {
+                background.simulate_random_delay().await;
+            }
             tokio::runtime::Builder::new_current_thread().enable_io().enable_time().build().unwrap().block_on(body)
         } else {
             body.await
@@ -30,7 +42,7 @@ macro_rules! test_support {
     }};
 }
 
-trait RowsAffected {
+pub trait RowsAffected {
     fn rows_affected(&self) -> u64;
 }
 
@@ -48,32 +60,37 @@ impl RowsAffected for sqlx::postgres::PgQueryResult {
 
 impl Db<sqlx::Sqlite> {
     #[cfg(test)]
-    pub async fn sqlite(url: &str, max_connections: u32) -> Result<Self> {
+    pub async fn new(url: &str, max_connections: u32) -> Result<Self> {
         let pool = sqlx::sqlite::SqlitePoolOptions::new()
             .max_connections(max_connections)
             .connect(url)
             .await?;
-        Ok(Self { pool })
+        Ok(Self {
+            pool,
+            background: None,
+        })
     }
 }
 
 impl Db<sqlx::Postgres> {
-    pub async fn postgres(url: &str, max_connections: u32) -> Result<Self> {
+    pub async fn new(url: &str, max_connections: u32) -> Result<Self> {
         let pool = sqlx::postgres::PgPoolOptions::new()
             .max_connections(max_connections)
             .connect(url)
             .await?;
-        Ok(Self { pool })
+        Ok(Self {
+            pool,
+            #[cfg(test)]
+            background: None,
+        })
     }
 }
 
 impl<D> Db<D>
 where
     D: sqlx::Database + sqlx::migrate::MigrateDatabase,
-    for<'a> <D as sqlx::database::HasArguments<'a>>::Arguments: sqlx::IntoArguments<'a, D>,
-    D: for<'a> sqlx::database::HasValueRef<'a>,
-    D: for<'a> sqlx::database::HasArguments<'a>,
     D::Connection: sqlx::migrate::Migrate,
+    for<'a> <D as sqlx::database::HasArguments<'a>>::Arguments: sqlx::IntoArguments<'a, D>,
     for<'a> &'a mut D::Connection: sqlx::Executor<'a, Database = D>,
     for<'a, 'b> &'b mut sqlx::Transaction<'a, D>: sqlx::Executor<'b, Database = D>,
     D::QueryResult: RowsAffected,
@@ -452,19 +469,18 @@ where
 
     pub async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()> {
         test_support!(self, {
+            let emails = invites
+                .iter()
+                .map(|s| s.email_address.as_str())
+                .collect::<Vec<_>>();
             sqlx::query(
                 "
                 UPDATE signups
                 SET email_confirmation_sent = TRUE
-                WHERE email_address = ANY ($1)
+                WHERE email_address IN (SELECT value from json_each($1))
                 ",
             )
-            // .bind(
-            //     &invites
-            //         .iter()
-            //         .map(|s| s.email_address.as_str())
-            //         .collect::<Vec<_>>(),
-            // )
+            .bind(&serde_json::json!(emails))
             .execute(&self.pool)
             .await?;
             Ok(())
@@ -808,7 +824,7 @@ where
                 count = excluded.count
                 ",
             );
-            query.build().execute(&self.pool).await?;
+            // query.build().execute(&self.pool).await?;
 
             Ok(())
         })
@@ -1604,24 +1620,6 @@ where
                 .await?)
         })
     }
-
-    #[cfg(test)]
-    pub async fn teardown(&self, url: &str) {
-        test_support!(self, {
-            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();
-            ";
-            sqlx::query(query).execute(&self.pool).await.log_err();
-            self.pool.close().await;
-            <sqlx::Sqlite as sqlx::migrate::MigrateDatabase>::drop_database(url)
-                .await
-                .log_err();
-        })
-    }
 }
 
 macro_rules! id_type {
@@ -1833,51 +1831,37 @@ mod test {
     use super::*;
     use gpui::executor::Background;
     use rand::prelude::*;
-    use sqlx::{migrate::MigrateDatabase, Sqlite};
+    use sqlx::migrate::MigrateDatabase;
     use std::sync::Arc;
 
     pub struct TestDb {
-        pub db: Option<Arc<Db<Sqlite>>>,
+        pub db: Option<Arc<DefaultDb>>,
         pub url: String,
     }
 
     impl TestDb {
-        #[allow(clippy::await_holding_lock)]
-        pub async fn real() -> Self {
-            todo!()
-            // eprintln!("creating database...");
-            // let start = std::time::Instant::now();
-            // let mut rng = StdRng::from_entropy();
-            // let url = format!("/tmp/zed-test-{}", rng.gen::<u128>());
-            // Sqlite::create_database(&url).await.unwrap();
-            // let db = Db::new(&url, 5).await.unwrap();
-            // db.migrate(Path::new(DEFAULT_MIGRATIONS_PATH.unwrap()), false)
-            //     .await
-            //     .unwrap();
-
-            // eprintln!("created database: {:?}", start.elapsed());
-            // Self {
-            //     db: Some(Arc::new(db)),
-            //     url,
-            // }
-        }
-
-        pub async fn fake(background: Arc<Background>) -> Self {
-            let start = std::time::Instant::now();
+        pub async fn new(background: Arc<Background>) -> Self {
             let mut rng = StdRng::from_entropy();
-            let url = format!("file:db-{}?mode=memory&cache=shared", rng.gen::<u128>());
-            let db = Db::sqlite(&url, 5).await.unwrap();
+            let url = format!("/tmp/zed-test-{}", rng.gen::<u128>());
+            sqlx::Sqlite::create_database(&url).await.unwrap();
+            let mut db = DefaultDb::new(&url, 5).await.unwrap();
+            db.background = Some(background);
             let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations.sqlite");
             db.migrate(Path::new(migrations_path), false).await.unwrap();
-
             Self {
                 db: Some(Arc::new(db)),
                 url,
             }
         }
 
-        pub fn db(&self) -> &Arc<Db<Sqlite>> {
+        pub fn db(&self) -> &Arc<DefaultDb> {
             self.db.as_ref().unwrap()
         }
     }
+
+    impl Drop for TestDb {
+        fn drop(&mut self) {
+            std::fs::remove_file(&self.url).ok();
+        }
+    }
 }

crates/collab/src/db_tests.rs 🔗

@@ -6,133 +6,125 @@ use time::OffsetDateTime;
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_get_users_by_ids() {
-    for test_db in [
-        TestDb::real().await,
-        TestDb::fake(build_background_executor()),
-    ] {
-        let db = test_db.db();
-
-        let mut user_ids = Vec::new();
-        for i in 1..=4 {
-            user_ids.push(
-                db.create_user(
-                    &format!("user{i}@example.com"),
-                    false,
-                    NewUserParams {
-                        github_login: format!("user{i}"),
-                        github_user_id: i,
-                        invite_count: 0,
-                    },
-                )
-                .await
-                .unwrap()
-                .user_id,
-            );
-        }
-
-        assert_eq!(
-            db.get_users_by_ids(user_ids.clone()).await.unwrap(),
-            vec![
-                User {
-                    id: user_ids[0],
-                    github_login: "user1".to_string(),
-                    github_user_id: Some(1),
-                    email_address: Some("user1@example.com".to_string()),
-                    admin: false,
-                    ..Default::default()
-                },
-                User {
-                    id: user_ids[1],
-                    github_login: "user2".to_string(),
-                    github_user_id: Some(2),
-                    email_address: Some("user2@example.com".to_string()),
-                    admin: false,
-                    ..Default::default()
-                },
-                User {
-                    id: user_ids[2],
-                    github_login: "user3".to_string(),
-                    github_user_id: Some(3),
-                    email_address: Some("user3@example.com".to_string()),
-                    admin: false,
-                    ..Default::default()
-                },
-                User {
-                    id: user_ids[3],
-                    github_login: "user4".to_string(),
-                    github_user_id: Some(4),
-                    email_address: Some("user4@example.com".to_string()),
-                    admin: false,
-                    ..Default::default()
-                }
-            ]
-        );
-    }
-}
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
 
-#[tokio::test(flavor = "multi_thread")]
-async fn test_get_user_by_github_account() {
-    for test_db in [
-        TestDb::real().await,
-        TestDb::fake(build_background_executor()),
-    ] {
-        let db = test_db.db();
-        let user_id1 = db
-            .create_user(
-                "user1@example.com",
-                false,
-                NewUserParams {
-                    github_login: "login1".into(),
-                    github_user_id: 101,
-                    invite_count: 0,
-                },
-            )
-            .await
-            .unwrap()
-            .user_id;
-        let user_id2 = db
-            .create_user(
-                "user2@example.com",
+    let mut user_ids = Vec::new();
+    for i in 1..=4 {
+        user_ids.push(
+            db.create_user(
+                &format!("user{i}@example.com"),
                 false,
                 NewUserParams {
-                    github_login: "login2".into(),
-                    github_user_id: 102,
+                    github_login: format!("user{i}"),
+                    github_user_id: i,
                     invite_count: 0,
                 },
             )
             .await
             .unwrap()
-            .user_id;
+            .user_id,
+        );
+    }
 
-        let user = db
-            .get_user_by_github_account("login1", None)
-            .await
-            .unwrap()
-            .unwrap();
-        assert_eq!(user.id, user_id1);
-        assert_eq!(&user.github_login, "login1");
-        assert_eq!(user.github_user_id, Some(101));
+    assert_eq!(
+        db.get_users_by_ids(user_ids.clone()).await.unwrap(),
+        vec![
+            User {
+                id: user_ids[0],
+                github_login: "user1".to_string(),
+                github_user_id: Some(1),
+                email_address: Some("user1@example.com".to_string()),
+                admin: false,
+                ..Default::default()
+            },
+            User {
+                id: user_ids[1],
+                github_login: "user2".to_string(),
+                github_user_id: Some(2),
+                email_address: Some("user2@example.com".to_string()),
+                admin: false,
+                ..Default::default()
+            },
+            User {
+                id: user_ids[2],
+                github_login: "user3".to_string(),
+                github_user_id: Some(3),
+                email_address: Some("user3@example.com".to_string()),
+                admin: false,
+                ..Default::default()
+            },
+            User {
+                id: user_ids[3],
+                github_login: "user4".to_string(),
+                github_user_id: Some(4),
+                email_address: Some("user4@example.com".to_string()),
+                admin: false,
+                ..Default::default()
+            }
+        ]
+    );
+}
 
-        assert!(db
-            .get_user_by_github_account("non-existent-login", None)
-            .await
-            .unwrap()
-            .is_none());
+#[tokio::test(flavor = "multi_thread")]
+async fn test_get_user_by_github_account() {
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
+    let user_id1 = db
+        .create_user(
+            "user1@example.com",
+            false,
+            NewUserParams {
+                github_login: "login1".into(),
+                github_user_id: 101,
+                invite_count: 0,
+            },
+        )
+        .await
+        .unwrap()
+        .user_id;
+    let user_id2 = db
+        .create_user(
+            "user2@example.com",
+            false,
+            NewUserParams {
+                github_login: "login2".into(),
+                github_user_id: 102,
+                invite_count: 0,
+            },
+        )
+        .await
+        .unwrap()
+        .user_id;
 
-        let user = db
-            .get_user_by_github_account("the-new-login2", Some(102))
-            .await
-            .unwrap()
-            .unwrap();
-        assert_eq!(user.id, user_id2);
-        assert_eq!(&user.github_login, "the-new-login2");
-        assert_eq!(user.github_user_id, Some(102));
-    }
+    let user = db
+        .get_user_by_github_account("login1", None)
+        .await
+        .unwrap()
+        .unwrap();
+    assert_eq!(user.id, user_id1);
+    assert_eq!(&user.github_login, "login1");
+    assert_eq!(user.github_user_id, Some(101));
+
+    assert!(db
+        .get_user_by_github_account("non-existent-login", None)
+        .await
+        .unwrap()
+        .is_none());
+
+    let user = db
+        .get_user_by_github_account("the-new-login2", Some(102))
+        .await
+        .unwrap()
+        .unwrap();
+    assert_eq!(user.id, user_id2);
+    assert_eq!(&user.github_login, "the-new-login2");
+    assert_eq!(user.github_user_id, Some(102));
 }
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_worktree_extensions() {
-    let test_db = TestDb::real().await;
+    let test_db = TestDb::new(build_background_executor()).await;
     let db = test_db.db();
 
     let user = db
@@ -204,7 +196,7 @@ async fn test_worktree_extensions() {
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_user_activity() {
-    let test_db = TestDb::real().await;
+    let test_db = TestDb::new(build_background_executor()).await;
     let db = test_db.db();
 
     let mut user_ids = Vec::new();
@@ -447,98 +439,90 @@ async fn test_user_activity() {
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_recent_channel_messages() {
-    for test_db in [
-        TestDb::real().await,
-        TestDb::fake(build_background_executor()),
-    ] {
-        let db = test_db.db();
-        let user = db
-            .create_user(
-                "u@example.com",
-                false,
-                NewUserParams {
-                    github_login: "u".into(),
-                    github_user_id: 1,
-                    invite_count: 0,
-                },
-            )
-            .await
-            .unwrap()
-            .user_id;
-        let org = db.create_org("org", "org").await.unwrap();
-        let channel = db.create_org_channel(org, "channel").await.unwrap();
-        for i in 0..10 {
-            db.create_channel_message(channel, user, &i.to_string(), OffsetDateTime::now_utc(), i)
-                .await
-                .unwrap();
-        }
-
-        let messages = db.get_channel_messages(channel, 5, None).await.unwrap();
-        assert_eq!(
-            messages.iter().map(|m| &m.body).collect::<Vec<_>>(),
-            ["5", "6", "7", "8", "9"]
-        );
-
-        let prev_messages = db
-            .get_channel_messages(channel, 4, Some(messages[0].id))
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
+    let user = db
+        .create_user(
+            "u@example.com",
+            false,
+            NewUserParams {
+                github_login: "u".into(),
+                github_user_id: 1,
+                invite_count: 0,
+            },
+        )
+        .await
+        .unwrap()
+        .user_id;
+    let org = db.create_org("org", "org").await.unwrap();
+    let channel = db.create_org_channel(org, "channel").await.unwrap();
+    for i in 0..10 {
+        db.create_channel_message(channel, user, &i.to_string(), OffsetDateTime::now_utc(), i)
             .await
             .unwrap();
-        assert_eq!(
-            prev_messages.iter().map(|m| &m.body).collect::<Vec<_>>(),
-            ["1", "2", "3", "4"]
-        );
     }
+
+    let messages = db.get_channel_messages(channel, 5, None).await.unwrap();
+    assert_eq!(
+        messages.iter().map(|m| &m.body).collect::<Vec<_>>(),
+        ["5", "6", "7", "8", "9"]
+    );
+
+    let prev_messages = db
+        .get_channel_messages(channel, 4, Some(messages[0].id))
+        .await
+        .unwrap();
+    assert_eq!(
+        prev_messages.iter().map(|m| &m.body).collect::<Vec<_>>(),
+        ["1", "2", "3", "4"]
+    );
 }
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_channel_message_nonces() {
-    for test_db in [
-        TestDb::real().await,
-        TestDb::fake(build_background_executor()),
-    ] {
-        let db = test_db.db();
-        let user = db
-            .create_user(
-                "user@example.com",
-                false,
-                NewUserParams {
-                    github_login: "user".into(),
-                    github_user_id: 1,
-                    invite_count: 0,
-                },
-            )
-            .await
-            .unwrap()
-            .user_id;
-        let org = db.create_org("org", "org").await.unwrap();
-        let channel = db.create_org_channel(org, "channel").await.unwrap();
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
+    let user = db
+        .create_user(
+            "user@example.com",
+            false,
+            NewUserParams {
+                github_login: "user".into(),
+                github_user_id: 1,
+                invite_count: 0,
+            },
+        )
+        .await
+        .unwrap()
+        .user_id;
+    let org = db.create_org("org", "org").await.unwrap();
+    let channel = db.create_org_channel(org, "channel").await.unwrap();
 
-        let msg1_id = db
-            .create_channel_message(channel, user, "1", OffsetDateTime::now_utc(), 1)
-            .await
-            .unwrap();
-        let msg2_id = db
-            .create_channel_message(channel, user, "2", OffsetDateTime::now_utc(), 2)
-            .await
-            .unwrap();
-        let msg3_id = db
-            .create_channel_message(channel, user, "3", OffsetDateTime::now_utc(), 1)
-            .await
-            .unwrap();
-        let msg4_id = db
-            .create_channel_message(channel, user, "4", OffsetDateTime::now_utc(), 2)
-            .await
-            .unwrap();
+    let msg1_id = db
+        .create_channel_message(channel, user, "1", OffsetDateTime::now_utc(), 1)
+        .await
+        .unwrap();
+    let msg2_id = db
+        .create_channel_message(channel, user, "2", OffsetDateTime::now_utc(), 2)
+        .await
+        .unwrap();
+    let msg3_id = db
+        .create_channel_message(channel, user, "3", OffsetDateTime::now_utc(), 1)
+        .await
+        .unwrap();
+    let msg4_id = db
+        .create_channel_message(channel, user, "4", OffsetDateTime::now_utc(), 2)
+        .await
+        .unwrap();
 
-        assert_ne!(msg1_id, msg2_id);
-        assert_eq!(msg1_id, msg3_id);
-        assert_eq!(msg2_id, msg4_id);
-    }
+    assert_ne!(msg1_id, msg2_id);
+    assert_eq!(msg1_id, msg3_id);
+    assert_eq!(msg2_id, msg4_id);
 }
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_create_access_tokens() {
-    let test_db = TestDb::real().await;
+    let test_db = TestDb::new(build_background_executor()).await;
     let db = test_db.db();
     let user = db
         .create_user(
@@ -582,14 +566,14 @@ async fn test_create_access_tokens() {
 
 #[test]
 fn test_fuzzy_like_string() {
-    assert_eq!(RealDb::fuzzy_like_string("abcd"), "%a%b%c%d%");
-    assert_eq!(RealDb::fuzzy_like_string("x y"), "%x%y%");
-    assert_eq!(RealDb::fuzzy_like_string(" z  "), "%z%");
+    assert_eq!(DefaultDb::fuzzy_like_string("abcd"), "%a%b%c%d%");
+    assert_eq!(DefaultDb::fuzzy_like_string("x y"), "%x%y%");
+    assert_eq!(DefaultDb::fuzzy_like_string(" z  "), "%z%");
 }
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_fuzzy_search_users() {
-    let test_db = TestDb::real().await;
+    let test_db = TestDb::new(build_background_executor()).await;
     let db = test_db.db();
     for (i, github_login) in [
         "California",
@@ -625,7 +609,7 @@ async fn test_fuzzy_search_users() {
         &["rhode-island", "colorado", "oregon"],
     );
 
-    async fn fuzzy_search_user_names(db: &Arc<TestDb>, query: &str) -> Vec<String> {
+    async fn fuzzy_search_user_names(db: &DefaultDb, query: &str) -> Vec<String> {
         db.fuzzy_search_users(query, 10)
             .await
             .unwrap()
@@ -637,176 +621,172 @@ async fn test_fuzzy_search_users() {
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_add_contacts() {
-    for test_db in [
-        TestDb::real().await,
-        TestDb::fake(build_background_executor()),
-    ] {
-        let db = test_db.db();
-
-        let mut user_ids = Vec::new();
-        for i in 0..3 {
-            user_ids.push(
-                db.create_user(
-                    &format!("user{i}@example.com"),
-                    false,
-                    NewUserParams {
-                        github_login: format!("user{i}"),
-                        github_user_id: i,
-                        invite_count: 0,
-                    },
-                )
-                .await
-                .unwrap()
-                .user_id,
-            );
-        }
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
 
-        let user_1 = user_ids[0];
-        let user_2 = user_ids[1];
-        let user_3 = user_ids[2];
+    let mut user_ids = Vec::new();
+    for i in 0..3 {
+        user_ids.push(
+            db.create_user(
+                &format!("user{i}@example.com"),
+                false,
+                NewUserParams {
+                    github_login: format!("user{i}"),
+                    github_user_id: i,
+                    invite_count: 0,
+                },
+            )
+            .await
+            .unwrap()
+            .user_id,
+        );
+    }
 
-        // User starts with no contacts
-        assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
+    let user_1 = user_ids[0];
+    let user_2 = user_ids[1];
+    let user_3 = user_ids[2];
 
-        // User requests a contact. Both users see the pending request.
-        db.send_contact_request(user_1, user_2).await.unwrap();
-        assert!(!db.has_contact(user_1, user_2).await.unwrap());
-        assert!(!db.has_contact(user_2, user_1).await.unwrap());
-        assert_eq!(
-            db.get_contacts(user_1).await.unwrap(),
-            &[Contact::Outgoing { user_id: user_2 }],
-        );
-        assert_eq!(
-            db.get_contacts(user_2).await.unwrap(),
-            &[Contact::Incoming {
-                user_id: user_1,
-                should_notify: true
-            }]
-        );
+    // User starts with no contacts
+    assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
 
-        // User 2 dismisses the contact request notification without accepting or rejecting.
-        // We shouldn't notify them again.
-        db.dismiss_contact_notification(user_1, user_2)
-            .await
-            .unwrap_err();
-        db.dismiss_contact_notification(user_2, user_1)
-            .await
-            .unwrap();
-        assert_eq!(
-            db.get_contacts(user_2).await.unwrap(),
-            &[Contact::Incoming {
-                user_id: user_1,
-                should_notify: false
-            }]
-        );
+    // User requests a contact. Both users see the pending request.
+    db.send_contact_request(user_1, user_2).await.unwrap();
+    assert!(!db.has_contact(user_1, user_2).await.unwrap());
+    assert!(!db.has_contact(user_2, user_1).await.unwrap());
+    assert_eq!(
+        db.get_contacts(user_1).await.unwrap(),
+        &[Contact::Outgoing { user_id: user_2 }],
+    );
+    assert_eq!(
+        db.get_contacts(user_2).await.unwrap(),
+        &[Contact::Incoming {
+            user_id: user_1,
+            should_notify: true
+        }]
+    );
 
-        // User can't accept their own contact request
-        db.respond_to_contact_request(user_1, user_2, true)
-            .await
-            .unwrap_err();
+    // User 2 dismisses the contact request notification without accepting or rejecting.
+    // We shouldn't notify them again.
+    db.dismiss_contact_notification(user_1, user_2)
+        .await
+        .unwrap_err();
+    db.dismiss_contact_notification(user_2, user_1)
+        .await
+        .unwrap();
+    assert_eq!(
+        db.get_contacts(user_2).await.unwrap(),
+        &[Contact::Incoming {
+            user_id: user_1,
+            should_notify: false
+        }]
+    );
 
-        // User accepts a contact request. Both users see the contact.
-        db.respond_to_contact_request(user_2, user_1, true)
-            .await
-            .unwrap();
-        assert_eq!(
-            db.get_contacts(user_1).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_2,
-                should_notify: true
-            }],
-        );
-        assert!(db.has_contact(user_1, user_2).await.unwrap());
-        assert!(db.has_contact(user_2, user_1).await.unwrap());
-        assert_eq!(
-            db.get_contacts(user_2).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_1,
-                should_notify: false,
-            }]
-        );
+    // User can't accept their own contact request
+    db.respond_to_contact_request(user_1, user_2, true)
+        .await
+        .unwrap_err();
 
-        // Users cannot re-request existing contacts.
-        db.send_contact_request(user_1, user_2).await.unwrap_err();
-        db.send_contact_request(user_2, user_1).await.unwrap_err();
+    // User accepts a contact request. Both users see the contact.
+    db.respond_to_contact_request(user_2, user_1, true)
+        .await
+        .unwrap();
+    assert_eq!(
+        db.get_contacts(user_1).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_2,
+            should_notify: true
+        }],
+    );
+    assert!(db.has_contact(user_1, user_2).await.unwrap());
+    assert!(db.has_contact(user_2, user_1).await.unwrap());
+    assert_eq!(
+        db.get_contacts(user_2).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_1,
+            should_notify: false,
+        }]
+    );
 
-        // Users can't dismiss notifications of them accepting other users' requests.
-        db.dismiss_contact_notification(user_2, user_1)
-            .await
-            .unwrap_err();
-        assert_eq!(
-            db.get_contacts(user_1).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_2,
-                should_notify: true,
-            }]
-        );
+    // Users cannot re-request existing contacts.
+    db.send_contact_request(user_1, user_2).await.unwrap_err();
+    db.send_contact_request(user_2, user_1).await.unwrap_err();
 
-        // Users can dismiss notifications of other users accepting their requests.
-        db.dismiss_contact_notification(user_1, user_2)
-            .await
-            .unwrap();
-        assert_eq!(
-            db.get_contacts(user_1).await.unwrap(),
-            &[Contact::Accepted {
+    // Users can't dismiss notifications of them accepting other users' requests.
+    db.dismiss_contact_notification(user_2, user_1)
+        .await
+        .unwrap_err();
+    assert_eq!(
+        db.get_contacts(user_1).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_2,
+            should_notify: true,
+        }]
+    );
+
+    // Users can dismiss notifications of other users accepting their requests.
+    db.dismiss_contact_notification(user_1, user_2)
+        .await
+        .unwrap();
+    assert_eq!(
+        db.get_contacts(user_1).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_2,
+            should_notify: false,
+        }]
+    );
+
+    // Users send each other concurrent contact requests and
+    // see that they are immediately accepted.
+    db.send_contact_request(user_1, user_3).await.unwrap();
+    db.send_contact_request(user_3, user_1).await.unwrap();
+    assert_eq!(
+        db.get_contacts(user_1).await.unwrap(),
+        &[
+            Contact::Accepted {
                 user_id: user_2,
                 should_notify: false,
-            }]
-        );
-
-        // Users send each other concurrent contact requests and
-        // see that they are immediately accepted.
-        db.send_contact_request(user_1, user_3).await.unwrap();
-        db.send_contact_request(user_3, user_1).await.unwrap();
-        assert_eq!(
-            db.get_contacts(user_1).await.unwrap(),
-            &[
-                Contact::Accepted {
-                    user_id: user_2,
-                    should_notify: false,
-                },
-                Contact::Accepted {
-                    user_id: user_3,
-                    should_notify: false
-                }
-            ]
-        );
-        assert_eq!(
-            db.get_contacts(user_3).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_1,
+            },
+            Contact::Accepted {
+                user_id: user_3,
                 should_notify: false
-            }],
-        );
+            }
+        ]
+    );
+    assert_eq!(
+        db.get_contacts(user_3).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_1,
+            should_notify: false
+        }],
+    );
 
-        // User declines a contact request. Both users see that it is gone.
-        db.send_contact_request(user_2, user_3).await.unwrap();
-        db.respond_to_contact_request(user_3, user_2, false)
-            .await
-            .unwrap();
-        assert!(!db.has_contact(user_2, user_3).await.unwrap());
-        assert!(!db.has_contact(user_3, user_2).await.unwrap());
-        assert_eq!(
-            db.get_contacts(user_2).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_1,
-                should_notify: false
-            }]
-        );
-        assert_eq!(
-            db.get_contacts(user_3).await.unwrap(),
-            &[Contact::Accepted {
-                user_id: user_1,
-                should_notify: false
-            }],
-        );
-    }
+    // User declines a contact request. Both users see that it is gone.
+    db.send_contact_request(user_2, user_3).await.unwrap();
+    db.respond_to_contact_request(user_3, user_2, false)
+        .await
+        .unwrap();
+    assert!(!db.has_contact(user_2, user_3).await.unwrap());
+    assert!(!db.has_contact(user_3, user_2).await.unwrap());
+    assert_eq!(
+        db.get_contacts(user_2).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_1,
+            should_notify: false
+        }]
+    );
+    assert_eq!(
+        db.get_contacts(user_3).await.unwrap(),
+        &[Contact::Accepted {
+            user_id: user_1,
+            should_notify: false
+        }],
+    );
 }
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_invite_codes() {
-    let postgres = TestDb::real().await;
-    let db = postgres.db();
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
     let NewUserResult { user_id: user1, .. } = db
         .create_user(
             "user1@example.com",
@@ -1000,8 +980,8 @@ async fn test_invite_codes() {
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_signups() {
-    let postgres = TestDb::real().await;
-    let db = postgres.db();
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
 
     // people sign up on the waitlist
     for i in 0..8 {
@@ -1146,8 +1126,8 @@ async fn test_signups() {
 
 #[tokio::test(flavor = "multi_thread")]
 async fn test_metrics_id() {
-    let postgres = TestDb::real().await;
-    let db = postgres.db();
+    let test_db = TestDb::new(build_background_executor()).await;
+    let db = test_db.db();
 
     let NewUserResult {
         user_id: user1,

crates/collab/src/integration_tests.rs 🔗

@@ -6104,7 +6104,7 @@ impl TestServer {
             .enable_time()
             .build()
             .unwrap()
-            .block_on(TestDb::real());
+            .block_on(TestDb::new(background.clone()));
         let live_kit_server_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
         let live_kit_server = live_kit_client::TestServer::create(
             format!("http://livekit.{}.test", live_kit_server_id),

crates/collab/src/main.rs 🔗

@@ -18,7 +18,7 @@ use serde::Deserialize;
 use std::{
     env::args,
     net::{SocketAddr, TcpListener},
-    path::PathBuf,
+    path::{Path, PathBuf},
     sync::Arc,
     time::Duration,
 };
@@ -101,8 +101,7 @@ async fn main() -> Result<()> {
             let migrations_path = config
                 .migrations_path
                 .as_deref()
-                .or(db::DEFAULT_MIGRATIONS_PATH.map(|s| s.as_ref()))
-                .ok_or_else(|| anyhow!("missing MIGRATIONS_PATH environment variable"))?;
+                .unwrap_or_else(|| Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/migrations")));
 
             let migrations = db.migrate(&migrations_path, false).await?;
             for (migration, duration) in migrations {