Go back to a compiling state and start running tests again

This commit is contained in:
Antonio Scandurra 2022-11-10 15:24:49 +01:00
parent 90d1d9ac82
commit 1bb41b6f54
4 changed files with 391 additions and 428 deletions

View file

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

View file

@ -6,133 +6,125 @@ use time::OffsetDateTime;
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_get_users_by_ids() { async fn test_get_users_by_ids() {
for test_db in [ let test_db = TestDb::new(build_background_executor()).await;
TestDb::real().await, let db = test_db.db();
TestDb::fake(build_background_executor()),
] {
let db = test_db.db();
let mut user_ids = Vec::new(); let mut user_ids = Vec::new();
for i in 1..=4 { for i in 1..=4 {
user_ids.push( user_ids.push(
db.create_user( db.create_user(
&format!("user{i}@example.com"), &format!("user{i}@example.com"),
false, false,
NewUserParams { NewUserParams {
github_login: format!("user{i}"), github_login: format!("user{i}"),
github_user_id: i, github_user_id: i,
invite_count: 0, 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], .await
github_login: "user2".to_string(), .unwrap()
github_user_id: Some(2), .user_id,
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_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()
}
]
);
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_get_user_by_github_account() { async fn test_get_user_by_github_account() {
for test_db in [ let test_db = TestDb::new(build_background_executor()).await;
TestDb::real().await, let db = test_db.db();
TestDb::fake(build_background_executor()), let user_id1 = db
] { .create_user(
let db = test_db.db(); "user1@example.com",
let user_id1 = db false,
.create_user( NewUserParams {
"user1@example.com", github_login: "login1".into(),
false, github_user_id: 101,
NewUserParams { invite_count: 0,
github_login: "login1".into(), },
github_user_id: 101, )
invite_count: 0, .await
}, .unwrap()
) .user_id;
.await let user_id2 = db
.unwrap() .create_user(
.user_id; "user2@example.com",
let user_id2 = db false,
.create_user( NewUserParams {
"user2@example.com", github_login: "login2".into(),
false, github_user_id: 102,
NewUserParams { invite_count: 0,
github_login: "login2".into(), },
github_user_id: 102, )
invite_count: 0, .await
}, .unwrap()
) .user_id;
.await
.unwrap()
.user_id;
let user = db let user = db
.get_user_by_github_account("login1", None) .get_user_by_github_account("login1", None)
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(user.id, user_id1); assert_eq!(user.id, user_id1);
assert_eq!(&user.github_login, "login1"); assert_eq!(&user.github_login, "login1");
assert_eq!(user.github_user_id, Some(101)); assert_eq!(user.github_user_id, Some(101));
assert!(db assert!(db
.get_user_by_github_account("non-existent-login", None) .get_user_by_github_account("non-existent-login", None)
.await .await
.unwrap() .unwrap()
.is_none()); .is_none());
let user = db let user = db
.get_user_by_github_account("the-new-login2", Some(102)) .get_user_by_github_account("the-new-login2", Some(102))
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(user.id, user_id2); assert_eq!(user.id, user_id2);
assert_eq!(&user.github_login, "the-new-login2"); assert_eq!(&user.github_login, "the-new-login2");
assert_eq!(user.github_user_id, Some(102)); assert_eq!(user.github_user_id, Some(102));
}
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_worktree_extensions() { 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 db = test_db.db();
let user = db let user = db
@ -204,7 +196,7 @@ async fn test_worktree_extensions() {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_user_activity() { 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 db = test_db.db();
let mut user_ids = Vec::new(); let mut user_ids = Vec::new();
@ -447,98 +439,90 @@ async fn test_user_activity() {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_recent_channel_messages() { async fn test_recent_channel_messages() {
for test_db in [ let test_db = TestDb::new(build_background_executor()).await;
TestDb::real().await, let db = test_db.db();
TestDb::fake(build_background_executor()), let user = db
] { .create_user(
let db = test_db.db(); "u@example.com",
let user = db false,
.create_user( NewUserParams {
"u@example.com", github_login: "u".into(),
false, github_user_id: 1,
NewUserParams { invite_count: 0,
github_login: "u".into(), },
github_user_id: 1, )
invite_count: 0, .await
}, .unwrap()
) .user_id;
.await let org = db.create_org("org", "org").await.unwrap();
.unwrap() let channel = db.create_org_channel(org, "channel").await.unwrap();
.user_id; for i in 0..10 {
let org = db.create_org("org", "org").await.unwrap(); db.create_channel_message(channel, user, &i.to_string(), OffsetDateTime::now_utc(), i)
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))
.await .await
.unwrap(); .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")] #[tokio::test(flavor = "multi_thread")]
async fn test_channel_message_nonces() { async fn test_channel_message_nonces() {
for test_db in [ let test_db = TestDb::new(build_background_executor()).await;
TestDb::real().await, let db = test_db.db();
TestDb::fake(build_background_executor()), let user = db
] { .create_user(
let db = test_db.db(); "user@example.com",
let user = db false,
.create_user( NewUserParams {
"user@example.com", github_login: "user".into(),
false, github_user_id: 1,
NewUserParams { invite_count: 0,
github_login: "user".into(), },
github_user_id: 1, )
invite_count: 0, .await
}, .unwrap()
) .user_id;
.await let org = db.create_org("org", "org").await.unwrap();
.unwrap() let channel = db.create_org_channel(org, "channel").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 let msg1_id = db
.create_channel_message(channel, user, "1", OffsetDateTime::now_utc(), 1) .create_channel_message(channel, user, "1", OffsetDateTime::now_utc(), 1)
.await .await
.unwrap(); .unwrap();
let msg2_id = db let msg2_id = db
.create_channel_message(channel, user, "2", OffsetDateTime::now_utc(), 2) .create_channel_message(channel, user, "2", OffsetDateTime::now_utc(), 2)
.await .await
.unwrap(); .unwrap();
let msg3_id = db let msg3_id = db
.create_channel_message(channel, user, "3", OffsetDateTime::now_utc(), 1) .create_channel_message(channel, user, "3", OffsetDateTime::now_utc(), 1)
.await .await
.unwrap(); .unwrap();
let msg4_id = db let msg4_id = db
.create_channel_message(channel, user, "4", OffsetDateTime::now_utc(), 2) .create_channel_message(channel, user, "4", OffsetDateTime::now_utc(), 2)
.await .await
.unwrap(); .unwrap();
assert_ne!(msg1_id, msg2_id); assert_ne!(msg1_id, msg2_id);
assert_eq!(msg1_id, msg3_id); assert_eq!(msg1_id, msg3_id);
assert_eq!(msg2_id, msg4_id); assert_eq!(msg2_id, msg4_id);
}
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_create_access_tokens() { 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 db = test_db.db();
let user = db let user = db
.create_user( .create_user(
@ -582,14 +566,14 @@ async fn test_create_access_tokens() {
#[test] #[test]
fn test_fuzzy_like_string() { fn test_fuzzy_like_string() {
assert_eq!(RealDb::fuzzy_like_string("abcd"), "%a%b%c%d%"); assert_eq!(DefaultDb::fuzzy_like_string("abcd"), "%a%b%c%d%");
assert_eq!(RealDb::fuzzy_like_string("x y"), "%x%y%"); assert_eq!(DefaultDb::fuzzy_like_string("x y"), "%x%y%");
assert_eq!(RealDb::fuzzy_like_string(" z "), "%z%"); assert_eq!(DefaultDb::fuzzy_like_string(" z "), "%z%");
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_fuzzy_search_users() { 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(); let db = test_db.db();
for (i, github_login) in [ for (i, github_login) in [
"California", "California",
@ -625,7 +609,7 @@ async fn test_fuzzy_search_users() {
&["rhode-island", "colorado", "oregon"], &["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) db.fuzzy_search_users(query, 10)
.await .await
.unwrap() .unwrap()
@ -637,176 +621,172 @@ async fn test_fuzzy_search_users() {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_add_contacts() { async fn test_add_contacts() {
for test_db in [ let test_db = TestDb::new(build_background_executor()).await;
TestDb::real().await, let db = test_db.db();
TestDb::fake(build_background_executor()),
] {
let db = test_db.db();
let mut user_ids = Vec::new(); let mut user_ids = Vec::new();
for i in 0..3 { for i in 0..3 {
user_ids.push( user_ids.push(
db.create_user( db.create_user(
&format!("user{i}@example.com"), &format!("user{i}@example.com"),
false, false,
NewUserParams { NewUserParams {
github_login: format!("user{i}"), github_login: format!("user{i}"),
github_user_id: i, github_user_id: i,
invite_count: 0, invite_count: 0,
},
)
.await
.unwrap()
.user_id,
);
}
let user_1 = user_ids[0];
let user_2 = user_ids[1];
let user_3 = user_ids[2];
// User starts with no contacts
assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
// 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 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 can't accept their own contact request
db.respond_to_contact_request(user_1, user_2, true)
.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 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'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,
}, },
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 .await
.unwrap(); .unwrap()
assert!(!db.has_contact(user_2, user_3).await.unwrap()); .user_id,
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
}],
); );
} }
let user_1 = user_ids[0];
let user_2 = user_ids[1];
let user_3 = user_ids[2];
// User starts with no contacts
assert_eq!(db.get_contacts(user_1).await.unwrap(), &[]);
// 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 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 can't accept their own contact request
db.respond_to_contact_request(user_1, user_2, true)
.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 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'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,
},
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
}],
);
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_invite_codes() { async fn test_invite_codes() {
let postgres = TestDb::real().await; let test_db = TestDb::new(build_background_executor()).await;
let db = postgres.db(); let db = test_db.db();
let NewUserResult { user_id: user1, .. } = db let NewUserResult { user_id: user1, .. } = db
.create_user( .create_user(
"user1@example.com", "user1@example.com",
@ -1000,8 +980,8 @@ async fn test_invite_codes() {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_signups() { async fn test_signups() {
let postgres = TestDb::real().await; let test_db = TestDb::new(build_background_executor()).await;
let db = postgres.db(); let db = test_db.db();
// people sign up on the waitlist // people sign up on the waitlist
for i in 0..8 { for i in 0..8 {
@ -1146,8 +1126,8 @@ async fn test_signups() {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_metrics_id() { async fn test_metrics_id() {
let postgres = TestDb::real().await; let test_db = TestDb::new(build_background_executor()).await;
let db = postgres.db(); let db = test_db.db();
let NewUserResult { let NewUserResult {
user_id: user1, user_id: user1,

View file

@ -6104,7 +6104,7 @@ impl TestServer {
.enable_time() .enable_time()
.build() .build()
.unwrap() .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_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
let live_kit_server = live_kit_client::TestServer::create( let live_kit_server = live_kit_client::TestServer::create(
format!("http://livekit.{}.test", live_kit_server_id), format!("http://livekit.{}.test", live_kit_server_id),

View file

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