//! Integration tests for SqliteSyncAccountRepository. mod common; use goingson_core::SyncAccountRepository; use goingson_db_sqlite::SqliteSyncAccountRepository; #[tokio::test] async fn create_applies_schema_defaults() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let acct = repo .create(user_id, "google", "Work Calendar", Some("me@example.com")) .await .unwrap(); assert_eq!(acct.provider, "google"); assert_eq!(acct.account_name, "Work Calendar"); assert_eq!(acct.email.as_deref(), Some("me@example.com")); // Defaults from migration 037. assert!(acct.sync_calendars); assert!(acct.sync_contacts); assert!(acct.enabled); assert_eq!(acct.sync_interval_minutes, 15); assert!( acct.calendar_ids.is_empty(), "calendar_ids defaults to '[]'" ); assert!(acct.last_calendar_sync.is_none()); } #[tokio::test] async fn create_allows_null_email() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let acct = repo .create(user_id, "caldav", "Personal", None) .await .unwrap(); assert!(acct.email.is_none()); let fetched = repo.get_by_id(acct.id, user_id).await.unwrap().unwrap(); assert!(fetched.email.is_none()); } #[tokio::test] async fn get_by_id_missing_is_none() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let missing = goingson_core::SyncAccountId::new(); assert!(repo.get_by_id(missing, user_id).await.unwrap().is_none()); } #[tokio::test] async fn update_changes_fields_and_returns_row() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let acct = repo .create(user_id, "google", "Old Name", None) .await .unwrap(); let updated = repo .update(acct.id, user_id, "New Name", false, false, false) .await .unwrap() .expect("existing account updates to Some"); assert_eq!(updated.account_name, "New Name"); assert!(!updated.sync_calendars); assert!(!updated.sync_contacts); assert!(!updated.enabled); assert_eq!(updated.id, acct.id); } #[tokio::test] async fn update_unknown_id_returns_none() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let missing = goingson_core::SyncAccountId::new(); let result = repo .update(missing, user_id, "x", true, true, true) .await .unwrap(); assert!(result.is_none(), "no rows affected -> None"); } #[tokio::test] async fn delete_removes_row_and_reports_hit() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let acct = repo.create(user_id, "google", "Temp", None).await.unwrap(); assert!( repo.delete(acct.id, user_id).await.unwrap(), "first delete hits" ); assert!(repo.get_by_id(acct.id, user_id).await.unwrap().is_none()); assert!( !repo.delete(acct.id, user_id).await.unwrap(), "second delete is a miss" ); } #[tokio::test] async fn list_all_scoped_per_user_and_ordered_by_created_at() { let pool = common::setup_test_db().await; let user_a = common::create_test_user(&pool).await; let user_b = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let first = repo.create(user_a, "google", "First", None).await.unwrap(); let second = repo.create(user_a, "caldav", "Second", None).await.unwrap(); repo.create(user_b, "google", "Other User", None) .await .unwrap(); let a = repo.list_all(user_a).await.unwrap(); assert_eq!(a.len(), 2, "only user_a's accounts"); // Ordered by created_at ASC (first created first). assert_eq!(a[0].id, first.id); assert_eq!(a[1].id, second.id); } #[tokio::test] async fn cross_user_access_is_denied() { let pool = common::setup_test_db().await; let owner = common::create_test_user(&pool).await; let intruder = common::create_test_user(&pool).await; let repo = SqliteSyncAccountRepository::new(pool.clone()); let acct = repo.create(owner, "google", "Owned", None).await.unwrap(); // Another user cannot read, update, or delete it. assert!(repo.get_by_id(acct.id, intruder).await.unwrap().is_none()); assert!( repo.update(acct.id, intruder, "hijack", true, true, true) .await .unwrap() .is_none() ); assert!(!repo.delete(acct.id, intruder).await.unwrap()); // Still intact for the owner. assert!(repo.get_by_id(acct.id, owner).await.unwrap().is_some()); }