use super::BackgroundRunSpec;
use std::collections::HashMap;
fn sample_spec(read_only: bool) -> BackgroundRunSpec {
BackgroundRunSpec {
task_id: "t-1234".to_string(),
worker_pid: Some(11),
agent_name: "codex".to_string(),
prompt: "fix the bug".to_string(),
dir: Some("/tmp/project".to_string()),
output: Some("json".to_string()),
result_file: Some("result.md".to_string()),
result_file_required: Some(true),
model: Some("gpt-5.4".to_string()),
budget: true,
session_id: Some("session-123".to_string()),
verify: Some("cargo check".to_string()),
setup: Some("cargo fetch".to_string()),
iterate: Some(3),
eval: Some("cargo test".to_string()),
eval_feedback_template: Some("Iteration {iteration}/{max_iterations}".to_string()),
judge: Some("cursor".to_string()),
judge_retry: true,
max_duration_mins: Some(15),
max_duration_secs: Some(900),
max_task_cost: Some(2.5),
idle_timeout_secs: Some(60),
retry: 2,
group: Some("core".to_string()),
skills: vec!["ai-coding".to_string()],
checklist: vec!["confirm retry path".to_string()],
hooks: vec!["on_fail:echo fail".to_string()],
template: Some("default".to_string()),
worktree: Some("feature/retry".to_string()),
base_branch: Some("main".to_string()),
peer_review: Some("codex".to_string()),
audit: true,
audit_explicit: true,
no_audit: false,
scope: vec!["src".to_string()],
interactive: true,
on_done: Some("echo done".to_string()),
cascade: vec!["notify".to_string()],
parent_task_id: Some("t-parent".to_string()),
env: Some(HashMap::from([("KEY".to_string(), "VALUE".to_string())])),
env_forward: Some(vec!["HOME".to_string()]),
agent_pid: Some(22),
agent_started: Some("launch-22".to_string()),
sandbox: true,
read_only,
audit_report_mode: true,
container: Some("aid:test".to_string()),
link_deps: true,
pre_task_dirty_paths: Some(vec!["?? pre-existing.rs".to_string()]),
foreground: true,
}
}
#[test]
fn background_run_spec_round_trips_read_only() {
let value = serde_json::to_value(sample_spec(true)).unwrap();
assert_eq!(value.get("read_only").and_then(|v| v.as_bool()), Some(true));
assert_eq!(value.get("result_file").and_then(|v| v.as_str()), Some("result.md"));
assert_eq!(value.get("result_file_required").and_then(|v| v.as_bool()), Some(true));
assert_eq!(value.get("iterate").and_then(|v| v.as_u64()), Some(3));
assert!(value.get("pre_task_dirty_paths").is_some());
assert_eq!(value.get("audit").and_then(|v| v.as_bool()), Some(true));
let decoded: BackgroundRunSpec = serde_json::from_value(value).unwrap();
assert!(decoded.read_only);
assert!(decoded.audit_report_mode);
assert_eq!(decoded.result_file.as_deref(), Some("result.md"));
assert_eq!(decoded.result_file_required, Some(true));
assert_eq!(decoded.eval.as_deref(), Some("cargo test"));
assert_eq!(
decoded.pre_task_dirty_paths.as_deref(),
Some(&["?? pre-existing.rs".to_string()][..])
);
assert_eq!(decoded.hooks, ["on_fail:echo fail".to_string()]);
assert_eq!(decoded.worktree.as_deref(), Some("feature/retry"));
assert_eq!(decoded.base_branch.as_deref(), Some("main"));
assert_eq!(decoded.peer_review.as_deref(), Some("codex"));
assert_eq!(decoded.scope, ["src".to_string()]);
assert!(decoded.budget);
assert_eq!(decoded.session_id.as_deref(), Some("session-123"));
assert_eq!(decoded.max_task_cost, Some(2.5));
assert!(decoded.judge_retry);
assert_eq!(decoded.agent_started.as_deref(), Some("launch-22"));
}
#[test]
fn background_run_spec_defaults_read_only_to_false_when_missing() {
let mut value = serde_json::to_value(sample_spec(false)).unwrap();
value
.as_object_mut()
.unwrap()
.remove("read_only");
value
.as_object_mut()
.unwrap()
.remove("audit_report_mode");
value
.as_object_mut()
.unwrap()
.remove("hooks");
value
.as_object_mut()
.unwrap()
.remove("audit");
let decoded: BackgroundRunSpec = serde_json::from_value(value).unwrap();
assert!(!decoded.read_only);
assert!(!decoded.audit_report_mode);
assert!(decoded.hooks.is_empty());
assert!(!decoded.audit);
}
#[test]
fn legacy_detached_field_is_ignored() {
let mut value = serde_json::to_value(sample_spec(false)).unwrap();
value
.as_object_mut()
.unwrap()
.insert("detached".to_string(), serde_json::Value::Bool(true));
let decoded = serde_json::from_value::<BackgroundRunSpec>(value).unwrap();
assert_eq!(decoded.task_id, "t-1234");
}