use super::*;
use crate::store::Store;
#[test]
fn pick_best_result_prefers_longest_diff() {
let winner = CandidateResult {
task_id: TaskId::generate(),
agent_label: "kilo".to_string(),
status: TaskStatus::Done,
diff_line_count: 12,
metric_score: None,
};
let runner = CandidateResult {
task_id: TaskId::generate(),
agent_label: "cursor".to_string(),
status: TaskStatus::Done,
diff_line_count: 3,
metric_score: None,
};
let failed = CandidateResult {
task_id: TaskId::generate(),
agent_label: "gemini".to_string(),
status: TaskStatus::Failed,
diff_line_count: 0,
metric_score: None,
};
let results = vec![winner.clone(), runner, failed];
let best = pick_best_result(&results).unwrap();
assert_eq!(best.task_id, winner.task_id);
}
#[test]
fn pick_best_result_none_when_no_done() {
let failed = CandidateResult {
task_id: TaskId::generate(),
agent_label: "opencode".to_string(),
status: TaskStatus::Failed,
diff_line_count: 0,
metric_score: None,
};
assert!(pick_best_result(&[failed]).is_none());
}
#[test]
fn pick_best_result_prefers_metric_score() {
let candidates = vec![
CandidateResult {
task_id: TaskId("t-1".into()),
agent_label: "a".into(),
status: TaskStatus::Done,
diff_line_count: 100,
metric_score: Some(3.0),
},
CandidateResult {
task_id: TaskId("t-2".into()),
agent_label: "b".into(),
status: TaskStatus::Done,
diff_line_count: 10,
metric_score: Some(9.0),
},
];
let best = pick_best_result(&candidates).unwrap();
assert_eq!(best.task_id, TaskId("t-2".into()));
}
#[test]
fn pick_best_result_treats_merged_as_success() {
let merged = CandidateResult {
task_id: TaskId("t-merged".into()),
agent_label: "a".into(),
status: TaskStatus::Merged,
diff_line_count: 4,
metric_score: None,
};
let failed = CandidateResult {
task_id: TaskId("t-failed".into()),
agent_label: "b".into(),
status: TaskStatus::Failed,
diff_line_count: 10,
metric_score: None,
};
let candidates = [failed, merged.clone()];
let best = pick_best_result(&candidates).unwrap();
assert_eq!(best.task_id, merged.task_id);
}
#[test]
fn pick_best_result_ignores_nan_metric_scores() {
let candidates = vec![
CandidateResult {
task_id: TaskId("t-nan".into()),
agent_label: "a".into(),
status: TaskStatus::Done,
diff_line_count: 100,
metric_score: Some(f64::NAN),
},
CandidateResult {
task_id: TaskId("t-finite".into()),
agent_label: "b".into(),
status: TaskStatus::Done,
diff_line_count: 1,
metric_score: Some(9.0),
},
];
let best = pick_best_result(&candidates).unwrap();
assert_eq!(best.task_id, TaskId("t-finite".into()));
}
#[test]
fn best_of_count_validation() {
assert!(validate_best_of_count(2).is_ok());
assert!(validate_best_of_count(5).is_ok());
assert!(validate_best_of_count(1).is_err());
assert!(validate_best_of_count(0).is_err());
}
#[test]
fn best_of_completion_includes_awaiting_input() {
assert!(is_completed_best_of_status(&TaskStatus::AwaitingInput));
assert!(is_completed_best_of_status(&TaskStatus::Done));
assert!(!is_completed_best_of_status(&TaskStatus::Running));
}
#[test]
fn best_of_task_ids_always_use_candidate_suffixes() {
let base = TaskId("t-batch".into());
let store = Store::open_memory().unwrap();
let first = best_of_task_id(&store, Some(&base), 0).unwrap();
let third = best_of_task_id(&store, Some(&base), 2).unwrap();
assert_eq!(first, Some(TaskId("t-batch-bo1".into())));
assert_eq!(third, Some(TaskId("t-batch-bo3".into())));
}
#[test]
fn best_of_task_ids_truncate_to_fit_task_limit() {
let store = Store::open_memory().unwrap();
let base = TaskId(format!("t-{}", "a".repeat(62)));
store
.insert_waiting_task(
base.as_str(),
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status(base.as_str(), TaskStatus::Running)
.unwrap();
let derived = best_of_task_id(&store, Some(&base), 4).unwrap().unwrap();
assert_eq!(derived.as_str().len(), 64);
assert!(derived.as_str().ends_with("-bo5"));
}
#[test]
fn best_of_task_ids_ignore_running_base_for_siblings() {
let store = Store::open_memory().unwrap();
let base = TaskId("t-batch".into());
store
.insert_waiting_task(
base.as_str(),
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status(base.as_str(), TaskStatus::Pending)
.unwrap();
let sibling = best_of_task_id(&store, Some(&base), 1).unwrap();
assert_eq!(sibling, Some(TaskId("t-batch-bo2".into())));
}
#[test]
fn best_of_task_ids_fall_back_to_random_when_derived_id_is_running() {
let store = Store::open_memory().unwrap();
let base = TaskId("t-batch".into());
store
.insert_waiting_task(
base.as_str(),
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status(base.as_str(), TaskStatus::Pending)
.unwrap();
store
.insert_waiting_task(
"t-batch-bo2",
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status("t-batch-bo2", TaskStatus::Running)
.unwrap();
let candidate = best_of_task_id(&store, Some(&base), 1).unwrap();
assert_eq!(candidate, None);
}
#[test]
fn best_of_task_ids_drop_invalid_auto_suffixes() {
let store = Store::open_memory().unwrap();
let base = TaskId(format!("t-{}", "a".repeat(62)));
let derived = format!("t-{}-bo5", "a".repeat(58));
store
.insert_waiting_task(
base.as_str(),
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status(base.as_str(), TaskStatus::Pending)
.unwrap();
store
.insert_waiting_task(
&derived,
"codex",
"prompt",
None,
None,
None,
None,
None,
None,
false,
false,
)
.unwrap();
store
.update_task_status(&derived, TaskStatus::Pending)
.unwrap();
let candidate = best_of_task_id(&store, Some(&base), 4).unwrap();
assert_eq!(candidate, None);
}
#[test]
fn best_of_task_ids_reject_invalid_base_ids_before_reuse() {
let store = Store::open_memory().unwrap();
let err = best_of_task_id(&store, Some(&TaskId("-bad".into())), 0).unwrap_err();
assert!(err
.to_string()
.contains("Invalid task ID"));
}