use super::test_support;
use super::*;
#[test]
fn task_full_lifecycle_with_runner_execution() -> Result<()> {
let repo = LifecycleRepo::new()?;
let task_id = "RQ-9001";
let task = test_support::make_test_task(task_id, "Runner test task", TaskStatus::Todo);
repo.write_queue(&[task])?;
let marker_file = repo.path().join(".ralph/runner_executed.marker");
let runner_script = format!(
r#"#!/bin/bash
# Mock runner that verifies it received task context
echo "runner_executed" > "{}"
exit 0
"#,
marker_file.display()
);
repo.setup_runner_with_passing_ci(&runner_script)?;
let queue = repo.read_queue()?;
let task = find_task(&queue.tasks, task_id).expect("task should exist before run");
assert_eq!(
task.status,
TaskStatus::Todo,
"Initial: status should be Todo"
);
repo.run_ok(&["run", "one"])?;
assert!(
marker_file.exists(),
"Runner should have been executed (marker file should exist)"
);
let queue = repo.read_queue()?;
assert!(
find_task(&queue.tasks, task_id).is_none(),
"Task should not be in queue after successful run + CI gate"
);
let done = repo.read_done()?;
assert_eq!(done.tasks.len(), 1, "Task should be in done.json");
let done_task = find_task(&done.tasks, task_id).expect("task should be in done.json");
assert_eq!(done_task.status, TaskStatus::Done, "Status should be Done");
assert!(done_task.started_at.is_some(), "started_at should be set");
assert!(
done_task.completed_at.is_some(),
"completed_at should be set"
);
Ok(())
}
#[test]
fn task_runner_failure_prevents_auto_complete() -> Result<()> {
let repo = LifecycleRepo::new()?;
let task_id = "RQ-9002";
let task = test_support::make_test_task(task_id, "Task that will fail", TaskStatus::Todo);
repo.write_queue(&[task])?;
repo.setup_runner_with_passing_ci("#!/bin/sh\nexit 1\n")?;
let (status, _, _stderr) = repo.run(&["run", "one"]);
assert!(
!status.success(),
"Run should fail when runner exits with error"
);
let queue = repo.read_queue()?;
assert_eq!(
queue.tasks.len(),
1,
"Task should still be in queue after runner failure"
);
let task = find_task(&queue.tasks, task_id).expect("task should remain in queue");
assert_eq!(
task.status,
TaskStatus::Doing,
"Task should be Doing (set by run command)"
);
assert!(
task.started_at.is_some(),
"started_at should be set by run command"
);
repo.run_ok(&[
"task",
"reject",
task_id,
"--note",
"Runner failed - won't fix",
])?;
let queue = repo.read_queue()?;
assert!(
find_task(&queue.tasks, task_id).is_none(),
"Task should be removed from queue"
);
let done = repo.read_done()?;
assert_eq!(done.tasks.len(), 1, "Task should be in done.json");
let done_task = find_task(&done.tasks, task_id).expect("task should exist in done.json");
assert_eq!(
done_task.status,
TaskStatus::Rejected,
"Status should be Rejected"
);
assert!(
done_task
.notes
.iter()
.any(|note| note.contains("Runner failed")),
"Reject note should be preserved"
);
Ok(())
}
#[test]
fn task_lifecycle_queue_state_during_run() -> Result<()> {
let repo = LifecycleRepo::new()?;
let task_id = "RQ-9003";
let task = test_support::make_test_task(task_id, "State tracking task", TaskStatus::Todo);
repo.write_queue(&[task])?;
repo.setup_runner_with_passing_ci("#!/bin/sh\nexit 0\n")?;
let queue = repo.read_queue()?;
let task = find_task(&queue.tasks, task_id).expect("task should exist before run");
assert_eq!(
task.status,
TaskStatus::Todo,
"Initial: status should be Todo"
);
assert!(
task.started_at.is_none(),
"Initial: started_at should not be set"
);
repo.run_ok(&["run", "one"])?;
let queue = repo.read_queue()?;
assert!(
find_task(&queue.tasks, task_id).is_none(),
"After run: task should not be in queue (auto-completed by CI gate)"
);
let done = repo.read_done()?;
assert_eq!(
done.tasks.len(),
1,
"After run: task should be in done.json"
);
let done_task = find_task(&done.tasks, task_id).expect("task should be in done.json");
assert_eq!(
done_task.status,
TaskStatus::Done,
"After run: status should be Done"
);
assert!(
done_task.started_at.is_some(),
"After run: started_at should be set"
);
assert!(
done_task.completed_at.is_some(),
"After run: completed_at should be set"
);
Ok(())
}