use intent_engine::db::{create_pool, run_migrations};
use intent_engine::tasks::TaskManager;
use sqlx::SqlitePool;
use tempfile::TempDir;
async fn setup_test_db() -> (TempDir, SqlitePool) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let db_path = temp_dir.path().join("focus_test.db");
let pool = create_pool(&db_path)
.await
.expect("Failed to create test database");
run_migrations(&pool)
.await
.expect("Failed to run migrations");
(temp_dir, pool)
}
async fn get_current_task_id(pool: &SqlitePool) -> Option<i64> {
#[derive(sqlx::FromRow)]
struct SessionRow {
current_task_id: Option<i64>,
}
let session_id = intent_engine::workspace::resolve_session_id(None);
sqlx::query_as::<_, SessionRow>("SELECT current_task_id FROM sessions WHERE session_id = ?")
.bind(&session_id)
.fetch_optional(pool)
.await
.ok()
.flatten()
.and_then(|row| row.current_task_id)
}
#[tokio::test]
async fn test_focus_switch_from_none() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
assert_eq!(get_current_task_id(&pool).await, None);
mgr.start_task(1, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(1));
let task = mgr.get_task(1).await.unwrap();
assert_eq!(task.status, "doing");
}
#[tokio::test]
async fn test_focus_switch_between_tasks() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task B".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.start_task(1, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(1));
let task_a_doing = mgr.get_task(1).await.unwrap();
assert_eq!(task_a_doing.status, "doing");
mgr.start_task(2, false).await.unwrap();
assert_eq!(
get_current_task_id(&pool).await,
Some(2),
"Focus should switch to Task B"
);
let task_a_paused = mgr.get_task(1).await.unwrap();
assert_eq!(
task_a_paused.status, "doing",
"Task A should remain 'doing' (becomes paused implicitly)"
);
let task_b_focused = mgr.get_task(2).await.unwrap();
assert_eq!(task_b_focused.status, "doing");
}
#[tokio::test]
async fn test_paused_state_is_derived() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task B".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task C".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.start_task(1, false).await.unwrap();
mgr.start_task(2, false).await.unwrap();
mgr.start_task(3, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(3));
let task_a = mgr.get_task(1).await.unwrap();
let task_b = mgr.get_task(2).await.unwrap();
let task_c = mgr.get_task(3).await.unwrap();
assert_eq!(
task_a.status, "doing",
"Task A is paused (doing + unfocused)"
);
assert_eq!(
task_b.status, "doing",
"Task B is paused (doing + unfocused)"
);
assert_eq!(
task_c.status, "doing",
"Task C is focused (doing + focused)"
);
}
#[tokio::test]
async fn test_resume_paused_task() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task B".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.start_task(1, false).await.unwrap();
mgr.start_task(2, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(2));
mgr.start_task(1, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(1));
let task_a = mgr.get_task(1).await.unwrap();
let task_b = mgr.get_task(2).await.unwrap();
assert_eq!(task_a.status, "doing", "Task A is focused again");
assert_eq!(task_b.status, "doing", "Task B is now paused");
}
#[tokio::test]
async fn test_start_already_focused_task() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.start_task(1, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(1));
let result = mgr.start_task(1, false).await;
assert!(
result.is_ok(),
"Starting an already focused task should succeed"
);
assert_eq!(get_current_task_id(&pool).await, Some(1));
let task = mgr.get_task(1).await.unwrap();
assert_eq!(task.status, "doing");
}
#[tokio::test]
async fn test_focus_with_parent_child_cascade() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Parent".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Child A".to_string(), None, Some(1), None, None, None)
.await
.unwrap();
mgr.add_task("Child B".to_string(), None, Some(1), None, None, None)
.await
.unwrap();
mgr.start_task(2, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(2));
let parent = mgr.get_task(1).await.unwrap();
let child_a = mgr.get_task(2).await.unwrap();
assert_eq!(
parent.status, "todo",
"Parent remains todo (cascade not implemented)"
);
assert_eq!(child_a.status, "doing", "Child A is focused");
mgr.start_task(3, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(3));
let parent_after = mgr.get_task(1).await.unwrap();
let child_a_after = mgr.get_task(2).await.unwrap();
let child_b_after = mgr.get_task(3).await.unwrap();
assert_eq!(parent_after.status, "todo", "Parent remains todo");
assert_eq!(child_a_after.status, "doing", "Child A is now paused");
assert_eq!(child_b_after.status, "doing", "Child B is now focused");
}
#[tokio::test]
async fn test_multiple_paused_tasks() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
for i in 1..=5 {
mgr.add_task(format!("Task {}", i), None, None, None, None, None)
.await
.unwrap();
}
for i in 1..=5 {
mgr.start_task(i, false).await.unwrap();
}
assert_eq!(get_current_task_id(&pool).await, Some(5));
for i in 1..=5 {
let task = mgr.get_task(i).await.unwrap();
assert_eq!(task.status, "doing");
}
let all_doing: Vec<i64> =
sqlx::query_scalar("SELECT id FROM tasks WHERE status = 'doing' ORDER BY id")
.fetch_all(&pool)
.await
.unwrap();
assert_eq!(all_doing, vec![1, 2, 3, 4, 5]);
let current = get_current_task_id(&pool).await.unwrap();
let paused: Vec<i64> = all_doing.into_iter().filter(|&id| id != current).collect();
assert_eq!(paused, vec![1, 2, 3, 4], "Tasks 1-4 are paused");
}
#[tokio::test]
async fn test_done_removes_from_paused_pool() {
let (_temp_dir, pool) = setup_test_db().await;
let mgr = TaskManager::new(&pool);
mgr.add_task("Task A".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task B".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.add_task("Task C".to_string(), None, None, None, None, None)
.await
.unwrap();
mgr.start_task(1, false).await.unwrap();
mgr.start_task(2, false).await.unwrap();
mgr.start_task(3, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(3));
mgr.done_task(false).await.unwrap();
let task_c = mgr.get_task(3).await.unwrap();
assert_eq!(task_c.status, "done");
assert_eq!(get_current_task_id(&pool).await, None);
let task_a = mgr.get_task(1).await.unwrap();
let task_b = mgr.get_task(2).await.unwrap();
assert_eq!(task_a.status, "doing");
assert_eq!(task_b.status, "doing");
mgr.start_task(1, false).await.unwrap();
assert_eq!(get_current_task_id(&pool).await, Some(1));
}