//! Integration tests for SqliteMonthlyReviewRepository. //! //! Focus: the goal upsert-by-position path (a transaction-guarded //! get-then-insert, since `(user_id, month, position)` has no unique key) and //! the reflection `ON CONFLICT(user_id, month)` upsert, the review-repo //! atomicity the audit flagged as untested. mod common; use goingson_core::{MonthlyGoalStatus, MonthlyReviewRepository}; use goingson_db_sqlite::SqliteMonthlyReviewRepository; const MONTH: &str = "2026-06"; #[tokio::test] async fn test_upsert_goal_by_position_replaces_in_place() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteMonthlyReviewRepository::new(pool); let first = repo .upsert_goal(user_id, MONTH, "ship v1", 1) .await .unwrap(); assert_eq!(first.text, "ship v1"); assert_eq!(first.position, 1); // Same (user, month, position) must update the existing goal, not add a second at position 1. let second = repo .upsert_goal(user_id, MONTH, "ship v2", 1) .await .unwrap(); assert_eq!( second.id, first.id, "upsert must reuse the goal at that position" ); assert_eq!(second.text, "ship v2"); let goals = repo.list_goals(user_id, MONTH).await.unwrap(); assert_eq!( goals.len(), 1, "repeated upsert at one position must not duplicate" ); } #[tokio::test] async fn test_upsert_goal_distinct_positions_coexist() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteMonthlyReviewRepository::new(pool); repo.upsert_goal(user_id, MONTH, "goal one", 1) .await .unwrap(); repo.upsert_goal(user_id, MONTH, "goal two", 2) .await .unwrap(); repo.upsert_goal(user_id, MONTH, "goal three", 3) .await .unwrap(); let goals = repo.list_goals(user_id, MONTH).await.unwrap(); assert_eq!(goals.len(), 3); assert_eq!(goals[0].position, 1); assert_eq!(goals[2].text, "goal three"); } #[tokio::test] async fn test_update_goal_status_and_delete() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteMonthlyReviewRepository::new(pool); let goal = repo .upsert_goal(user_id, MONTH, "finish audit", 1) .await .unwrap(); assert_eq!(goal.status, MonthlyGoalStatus::Active); let updated = repo .update_goal_status(goal.id, user_id, &MonthlyGoalStatus::Done) .await .unwrap() .expect("goal exists"); assert_eq!(updated.status, MonthlyGoalStatus::Done); assert!(repo.delete_goal(goal.id, user_id).await.unwrap()); assert!(repo.list_goals(user_id, MONTH).await.unwrap().is_empty()); } #[tokio::test] async fn test_upsert_reflection_is_idempotent() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteMonthlyReviewRepository::new(pool); repo.upsert_reflection(user_id, MONTH, "highlight A", "change A") .await .unwrap(); // Second upsert for the same month must overwrite, not create a second reflection row. let second = repo .upsert_reflection(user_id, MONTH, "highlight B", "change B") .await .unwrap(); assert_eq!(second.highlight_text, "highlight B"); assert_eq!(second.change_text, "change B"); let got = repo.get_reflection(user_id, MONTH).await.unwrap().unwrap(); assert_eq!(got.highlight_text, "highlight B"); assert_eq!(repo.list_all_reflections(user_id).await.unwrap().len(), 1); }