use super::{stop, tests::make_task};
use crate::{
cmd::run,
support::paths,
store::{Store, TaskCompletionUpdate},
types::*,
};
use std::sync::Arc;
fn setup() -> (tempfile::TempDir, paths::AidHomeGuard, Arc<Store>, TaskId) {
let home = tempfile::tempdir().expect("home");
let guard = paths::AidHomeGuard::set(home.path());
paths::ensure_dirs().expect("dirs");
let store = Arc::new(Store::open_memory().expect("store"));
let id = TaskId("t-settle-done".into());
store
.insert_task(&make_task(id.as_str(), TaskStatus::Running))
.expect("task");
crate::task::lifecycle::update_task_completion(
&store,
TaskCompletionUpdate {
id: id.as_str(),
status: TaskStatus::Done,
tokens: Some(10),
duration_ms: 100,
observed_model: None,
attribution_source: None,
cost_usd: None,
exit_code: Some(0),
},
)
.expect("agent exit");
(home, guard, store, id)
}
async fn settle(store: &Arc<Store>, id: &TaskId) {
let bundle = run::PromptBundle {
effective_prompt: "prompt".into(),
context_files: vec![],
prompt_tokens: 0,
injected_memory_ids: vec![],
};
run::post_run_lifecycle(
run::LifecycleMode::Background,
store,
id,
&run::RunArgs::default(),
AgentKind::Codex,
"codex",
None,
None,
None,
None,
&[],
&bundle,
TaskStatus::Done,
None,
)
.await
.expect("settlement");
}
#[tokio::test]
async fn settle_done_agent_success_is_provisional_until_checks_finish() {
let (_home, _guard, store, id) = setup();
let provisional = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(provisional.status, TaskStatus::Done);
assert!(
provisional.completed_at.is_none(),
"agent exit must not settle success"
);
settle(&store, &id).await;
let settled = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(settled.status, TaskStatus::Done);
assert!(settled.completed_at.is_some());
assert_eq!(settled.duration_ms, Some(100));
assert!(
!store
.update_task_status(id.as_str(), TaskStatus::Failed)
.expect("late failure")
);
assert_eq!(
store.get_task(id.as_str()).expect("get").expect("task").status,
TaskStatus::Done
);
}
#[test]
fn settle_done_stop_can_win_after_agent_exit_before_settlement() {
let (_home, _guard, store, id) = setup();
stop(&store, id.as_str()).expect("stop provisional success");
let task = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(task.status, TaskStatus::Stopped);
assert!(task.completed_at.is_some());
assert!(task.duration_ms.is_some());
let events = store.get_events(id.as_str()).expect("events");
assert_eq!(events.len(), 1);
assert_eq!(events[0].detail, "Task stopped by user");
}
#[test]
fn settle_done_provisional_success_still_fails_verification() {
let (_home, _guard, store, id) = setup();
store
.update_verify_status(id.as_str(), VerifyStatus::Failed)
.expect("verify result");
crate::task::verify::enforce_verify_status(&store, &id);
let task = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(task.status, TaskStatus::Failed);
assert!(task.completed_at.is_some());
assert!(task.duration_ms.is_some());
assert_eq!(task.exit_code, Some(1));
}
#[test]
fn settle_done_worker_failure_can_win_before_success_settlement() {
let (_home, _guard, store, id) = setup();
assert!(
crate::runtime::background::record_failure(&store, id.as_str(), "worker failed", "worker failed")
.expect("worker failure")
);
let task = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(task.status, TaskStatus::Failed);
assert!(task.completed_at.is_some());
assert!(task.duration_ms.is_some());
assert_eq!(store.get_events(id.as_str()).expect("events").len(), 1);
}
#[test]
fn settle_done_retry_tree_includes_provisional_success() {
let (_home, _guard, store, id) = setup();
super::stop_retry_tree(&store, id.as_str(), false).expect("stop retry tree");
assert_eq!(
store.get_task(id.as_str()).expect("get").expect("task").status,
TaskStatus::Stopped
);
assert_eq!(store.get_events(id.as_str()).expect("events").len(), 1);
}
#[test]
fn settle_done_quota_refinement_cannot_reverse_terminal_failure() {
let (_home, _guard, store, id) = setup();
assert!(
store
.update_task_status(id.as_str(), TaskStatus::Failed)
.expect("failure")
);
store
.update_verify_status(id.as_str(), VerifyStatus::Passed)
.expect("verify passed");
let before = store.get_task(id.as_str()).expect("get").expect("task");
assert!(!store.update_task_status(id.as_str(), TaskStatus::Done).expect("refinement"));
let after = store.get_task(id.as_str()).expect("get").expect("task");
assert_eq!(after.status, TaskStatus::Failed);
assert_eq!(after.completed_at, before.completed_at);
assert_eq!(after.duration_ms, before.duration_ms);
assert!(store.get_events(id.as_str()).expect("events").is_empty());
}