use tempfile::tempdir;
use super::AGENT;
use super::orphan::{DECLINE, messages, write_driver_log, write_messages};
use crate::git_tree::AgentState;
use crate::steps_view::{ORPHANED_WINDOW, Orphan, Tail, build_aged};
fn write_tool_window(ws: &std::path::Path, name: &str) {
std::fs::write(
messages(ws).join(name),
br#"[{"type":"tool_use","id":"toolu_1","name":"bash","input":{"command":"ls"}}]"#,
)
.unwrap();
}
fn park(ws: &std::path::Path, agent: &str) {
let repo = ws.join("repo.git");
let git = |args: &[&str]| {
crate::git_env::output(crate::git_env::git().arg("--git-dir").arg(&repo).args(args))
.expect("git runs")
};
std::fs::create_dir_all(&repo).unwrap();
git(&["init", "--bare", "-q"]);
let staged = ws.join("mark.json");
std::fs::write(
&staged,
br#"{"tool_use_id":"toolu_1","tool":"bash","reason":"open-world"}"#,
)
.unwrap();
let hashed = git(&["hash-object", "-w", "--", &staged.to_string_lossy()]);
let oid = String::from_utf8_lossy(&hashed.stdout).trim().to_owned();
git(&[
"update-ref",
&format!("{}{agent}", crate::control::hold::HELD_PREFIX),
&oid,
]);
}
#[test]
fn an_unanswered_tool_window_with_no_driver_banners_and_names_the_remedy() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_messages(ws, &["001-user.md"]);
write_tool_window(ws, "002-opus.json");
write_driver_log(ws, DECLINE);
let view = build_aged(ws, AGENT, AgentState::Stopped);
let Orphan::Spoke(Tail::ToolWindow, words) = &view.orphan else {
panic!("expected Spoke(ToolWindow), got {:?}", view.orphan);
};
assert!(words.contains("unmatched"), "{words}");
let banner = view.orphan.banner();
assert!(banner.contains(ORPHANED_WINDOW), "{banner}");
assert!(banner.contains("a message revives it"), "{banner}");
assert!(banner.contains("unmatched"), "{banner}");
}
#[test]
fn a_mute_tool_window_still_banners_its_own_class() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_tool_window(ws, "001-opus.json");
let view = build_aged(ws, AGENT, AgentState::Stopped);
assert_eq!(view.orphan, Orphan::Mute(Tail::ToolWindow));
let banner = view.orphan.banner();
assert!(banner.contains(ORPHANED_WINDOW), "{banner}");
assert!(banner.contains("nothing on disk says why"), "{banner}");
}
#[test]
fn a_model_turn_that_called_nothing_is_no_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
std::fs::write(
messages(ws).join("001-opus.json"),
br#"[{"type":"text","text":"done"}]"#,
)
.unwrap();
assert_eq!(
build_aged(ws, AGENT, AgentState::Stopped).orphan,
Orphan::None
);
}
#[test]
fn an_answered_tool_call_is_no_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_tool_window(ws, "001-opus.json");
std::fs::write(
messages(ws).join("002-tool.json"),
br#"[{"type":"tool_result","tool_use_id":"toolu_1","content":"ok"}]"#,
)
.unwrap();
assert_eq!(
build_aged(ws, AGENT, AgentState::Stopped).orphan,
Orphan::None
);
}
#[test]
fn a_parked_tool_call_is_no_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_tool_window(ws, "001-opus.json");
write_driver_log(ws, DECLINE);
park(ws, AGENT);
assert_eq!(
build_aged(ws, AGENT, AgentState::Stopped).orphan,
Orphan::None
);
}
#[test]
fn a_driven_tool_window_is_never_an_orphan() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_tool_window(ws, "001-opus.json");
for state in [AgentState::Live, AgentState::InFlight] {
assert_eq!(build_aged(ws, AGENT, state).orphan, Orphan::None);
}
}