//! Integration tests for subtask methods on SqliteTaskRepository. mod common; use goingson_core::TaskRepository; use goingson_db_sqlite::SqliteTaskRepository; #[tokio::test] async fn test_add_subtask_and_get() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let subtask = repo .add_subtask(task_id, user_id, "First subtask") .await .expect("Failed to add subtask"); assert!(subtask.is_some(), "Should return Some for valid task/user"); let subtask = subtask.unwrap(); assert_eq!(subtask.task_id, task_id); assert_eq!(subtask.text, "First subtask"); assert!(!subtask.is_completed); assert!(subtask.linked_task_id.is_none()); let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert_eq!(subtasks.len(), 1); assert_eq!(subtasks[0].id, subtask.id); assert_eq!(subtasks[0].text, "First subtask"); } #[tokio::test] async fn test_subtasks_ordered_by_position() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let first = repo .add_subtask(task_id, user_id, "Step 1") .await .expect("Failed to add subtask") .unwrap(); let second = repo .add_subtask(task_id, user_id, "Step 2") .await .expect("Failed to add subtask") .unwrap(); let third = repo .add_subtask(task_id, user_id, "Step 3") .await .expect("Failed to add subtask") .unwrap(); let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert_eq!(subtasks.len(), 3); // Should be ordered by position ascending assert_eq!(subtasks[0].id, first.id); assert_eq!(subtasks[0].text, "Step 1"); assert_eq!(subtasks[1].id, second.id); assert_eq!(subtasks[1].text, "Step 2"); assert_eq!(subtasks[2].id, third.id); assert_eq!(subtasks[2].text, "Step 3"); assert!(subtasks[0].position <= subtasks[1].position); assert!(subtasks[1].position <= subtasks[2].position); } #[tokio::test] async fn test_subtask_position_auto_increments() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let a = repo .add_subtask(task_id, user_id, "A") .await .expect("Failed to add subtask") .unwrap(); let b = repo .add_subtask(task_id, user_id, "B") .await .expect("Failed to add subtask") .unwrap(); let c = repo .add_subtask(task_id, user_id, "C") .await .expect("Failed to add subtask") .unwrap(); assert_eq!(a.position, 0); assert_eq!(b.position, 1); assert_eq!(c.position, 2); } #[tokio::test] async fn test_add_subtask_wrong_user_returns_none() { let pool = common::setup_test_db().await; let user1 = common::create_test_user(&pool).await; let user2 = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user1).await; let repo = SqliteTaskRepository::new(pool); let result = repo .add_subtask(task_id, user2, "Unauthorized subtask") .await .expect("Should not error, just return None"); assert!(result.is_none(), "Should return None when user doesn't own the task"); } #[tokio::test] async fn test_toggle_subtask_completion() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let subtask = repo .add_subtask(task_id, user_id, "Toggle me") .await .expect("Failed to add subtask") .unwrap(); assert!(!subtask.is_completed, "New subtask should be incomplete"); // Toggle to completed let toggled = repo .toggle_subtask(subtask.id, user_id) .await .expect("Failed to toggle subtask"); assert!(toggled.is_some()); assert!(toggled.unwrap().is_completed, "Should be completed after first toggle"); // Toggle back to incomplete let toggled_back = repo .toggle_subtask(subtask.id, user_id) .await .expect("Failed to toggle subtask"); assert!(toggled_back.is_some()); assert!(!toggled_back.unwrap().is_completed, "Should be incomplete after second toggle"); } #[tokio::test] async fn test_update_subtask_text() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let subtask = repo .add_subtask(task_id, user_id, "Original text") .await .expect("Failed to add subtask") .unwrap(); let updated = repo .update_subtask(subtask.id, user_id, "Updated text") .await .expect("Failed to update subtask"); assert!(updated.is_some()); assert_eq!(updated.unwrap().text, "Updated text"); // Verify via get let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert_eq!(subtasks.len(), 1); assert_eq!(subtasks[0].text, "Updated text"); } #[tokio::test] async fn test_delete_subtask() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let subtask = repo .add_subtask(task_id, user_id, "To be deleted") .await .expect("Failed to add subtask") .unwrap(); let deleted = repo .delete_subtask(subtask.id, user_id) .await .expect("Failed to delete subtask"); assert!(deleted, "Should return true for successful deletion"); let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert!(subtasks.is_empty(), "Subtask should be gone after deletion"); } #[tokio::test] async fn test_delete_subtask_wrong_user_returns_false() { let pool = common::setup_test_db().await; let user1 = common::create_test_user(&pool).await; let user2 = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user1).await; let repo = SqliteTaskRepository::new(pool); let subtask = repo .add_subtask(task_id, user1, "User1's subtask") .await .expect("Failed to add subtask") .unwrap(); let deleted = repo .delete_subtask(subtask.id, user2) .await .expect("Should not error, just return false"); assert!(!deleted, "Should return false when user doesn't own the subtask"); // Verify the subtask still exists let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert_eq!(subtasks.len(), 1, "Subtask should still exist after unauthorized delete"); } #[tokio::test] async fn test_add_subtask_link_syncs_status() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let parent_task_id = common::create_test_task(&pool, user_id).await; let completed_task_id = common::create_test_task(&pool, user_id).await; let pending_task_id = common::create_test_task(&pool, user_id).await; // Mark the first linked task as Completed sqlx::query("UPDATE tasks SET status = 'Completed' WHERE id = ?") .bind(completed_task_id.to_string()) .execute(&pool) .await .unwrap(); let repo = SqliteTaskRepository::new(pool); // Link the completed task — subtask should be marked completed let linked_completed = repo .add_subtask_link(parent_task_id, user_id, completed_task_id) .await .expect("Failed to add subtask link"); assert!(linked_completed.is_some(), "Should return Some for valid link"); let linked_completed = linked_completed.unwrap(); assert_eq!(linked_completed.linked_task_id, Some(completed_task_id)); assert!(linked_completed.is_completed, "Linked subtask should be completed when linked task is Completed"); // Link the pending task — subtask should be incomplete let linked_pending = repo .add_subtask_link(parent_task_id, user_id, pending_task_id) .await .expect("Failed to add subtask link"); assert!(linked_pending.is_some(), "Should return Some for valid link"); let linked_pending = linked_pending.unwrap(); assert_eq!(linked_pending.linked_task_id, Some(pending_task_id)); assert!(!linked_pending.is_completed, "Linked subtask should be incomplete when linked task is Pending"); } #[tokio::test] async fn test_get_subtasks_empty_task() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteTaskRepository::new(pool); let subtasks = repo .get_subtasks_for_task(task_id) .await .expect("Failed to get subtasks"); assert!(subtasks.is_empty(), "New task should have no subtasks"); }