use crate::git_tree::probe::{LockProbe, WriterProbe};
use crate::git_tree::state::{RESPONSE_FILE, classify};
use crate::git_tree::{AgentState, Probe, STEPS_DIR};
use std::path::{Path, PathBuf};
use tempfile::tempdir;
struct LockStub(Probe);
impl LockProbe for LockStub {
fn lock_state(&self, _dir: &Path) -> Probe {
self.0
}
}
struct WriterStub(Probe);
impl WriterProbe for WriterStub {
fn writer_state(&self, _path: &Path) -> Probe {
self.0
}
}
fn lock(p: Probe) -> LockStub {
LockStub(p)
}
fn writer(p: Probe) -> WriterStub {
WriterStub(p)
}
fn write(path: &Path, contents: &[u8]) {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).unwrap();
}
std::fs::write(path, contents).unwrap();
}
fn reading(
dir: &Path,
agent: &str,
lock: &dyn LockProbe,
writer: &dyn WriterProbe,
) -> (AgentState, bool, bool) {
let read = classify(dir, agent, lock, writer);
(read.state, read.uncertain, read.truncated)
}
fn resp(dir: &Path, agent: &str, seq: &str) -> PathBuf {
dir.join(format!("{STEPS_DIR}/{agent}/{seq}/{RESPONSE_FILE}"))
}
const FINISH_END: &[u8] = br#"{"type":"message_start","v":1,"role":"assistant"}
{"type":"finish","reason":"stop"}
{"type":"end"}
"#;
const ERROR_END: &[u8] = br#"{"type":"message_start","v":1,"role":"assistant"}
{"type":"error","kind":"transport","message":"reset"}
{"type":"end"}
"#;
const LENGTH_END: &[u8] = br#"{"type":"message_start","v":1,"role":"assistant"}
{"type":"content_delta","index":0,"delta":{"thinking_delta":"hmm"}}
{"type":"finish","reason":"length"}
{"type":"end"}
"#;
#[test]
fn lock_held_and_writer_held_is_in_flight() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-aaaa";
write(&resp(dir.path(), agent, "001"), FINISH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Held), &writer(Probe::Held)),
(AgentState::InFlight, false, false)
);
}
#[test]
fn lock_held_and_writer_free_is_live() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-bbbb";
write(&resp(dir.path(), agent, "001"), FINISH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Held), &writer(Probe::Free)),
(AgentState::Live, false, false)
);
}
#[test]
fn lock_held_and_writer_unknown_is_live_uncertain() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-uuuu";
write(&resp(dir.path(), agent, "001"), FINISH_END);
assert_eq!(
reading(
dir.path(),
agent,
&lock(Probe::Held),
&writer(Probe::Unknown)
),
(AgentState::Live, true, false)
);
}
#[test]
fn lock_held_with_no_response_is_live() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-cccc";
std::fs::create_dir_all(dir.path().join(STEPS_DIR).join(agent)).unwrap();
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Held), &writer(Probe::Held)),
(AgentState::Live, false, false)
);
}
#[test]
fn no_lock_and_complete_response_is_quiescent() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-dddd";
write(&resp(dir.path(), agent, "001"), FINISH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Quiescent, false, false)
);
}
#[test]
fn no_lock_and_failed_response_is_stopped() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-eeee";
write(&resp(dir.path(), agent, "001"), ERROR_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Stopped, false, false)
);
}
#[test]
fn no_lock_and_no_response_is_stopped() {
let dir = tempdir().unwrap();
assert_eq!(
reading(
dir.path(),
"no-such-agent",
&lock(Probe::Free),
&writer(Probe::Free)
),
(AgentState::Stopped, false, false)
);
}
#[test]
fn lock_unknown_and_complete_response_is_quiescent_uncertain() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-qqqq";
write(&resp(dir.path(), agent, "001"), FINISH_END);
assert_eq!(
reading(
dir.path(),
agent,
&lock(Probe::Unknown),
&writer(Probe::Free)
),
(AgentState::Quiescent, true, false)
);
}
#[test]
fn lock_unknown_and_incomplete_response_is_stopped_uncertain() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-ssss";
write(&resp(dir.path(), agent, "001"), ERROR_END);
assert_eq!(
reading(
dir.path(),
agent,
&lock(Probe::Unknown),
&writer(Probe::Free)
),
(AgentState::Stopped, true, false)
);
}
#[test]
fn classify_reads_latest_step_only() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-ffff";
write(&resp(dir.path(), agent, "001"), FINISH_END);
write(
&resp(dir.path(), agent, "002"),
b"{\"type\":\"content_delta\",\"index\":0,\"delta\":{\"text_delta\":\"go\"}}\n",
);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Stopped, false, false)
);
}
#[test]
fn no_lock_and_output_limited_response_is_stopped_and_truncated() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-llll";
write(&resp(dir.path(), agent, "001"), LENGTH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Stopped, false, true)
);
}
#[test]
fn a_driver_at_work_over_an_output_limited_step_reads_neither() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-mmmm";
write(&resp(dir.path(), agent, "001"), LENGTH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Held), &writer(Probe::Free)),
(AgentState::Live, false, false)
);
}
#[test]
fn a_tool_use_finish_is_untouched_by_the_output_limit_reading() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-tttt";
write(
&resp(dir.path(), agent, "001"),
br#"{"type":"finish","reason":"tool_use"}
{"type":"end"}
"#,
);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Quiescent, false, false)
);
}
#[test]
fn an_output_limited_step_behind_a_newer_one_is_not_the_reading() {
let dir = tempdir().unwrap();
let agent = "20260427T140000Z-nnnn";
write(&resp(dir.path(), agent, "001"), LENGTH_END);
write(&resp(dir.path(), agent, "002"), FINISH_END);
assert_eq!(
reading(dir.path(), agent, &lock(Probe::Free), &writer(Probe::Free)),
(AgentState::Quiescent, false, false)
);
}