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;
pub(super) struct LockStub(Probe);
impl LockProbe for LockStub {
fn lock_state(&self, _dir: &Path) -> Probe {
self.0
}
}
pub(super) struct WriterStub(Probe);
impl WriterProbe for WriterStub {
fn writer_state(&self, _path: &Path) -> Probe {
self.0
}
}
pub(super) fn lock(p: Probe) -> LockStub {
LockStub(p)
}
pub(super) fn writer(p: Probe) -> WriterStub {
WriterStub(p)
}
pub(super) 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();
}
pub(super) 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)
}
pub(super) fn refusal(
dir: &Path,
agent: &str,
lock: &dyn LockProbe,
writer: &dyn WriterProbe,
) -> bool {
let said = classify(dir, agent, lock, writer).failure;
said.as_deref().is_some_and(crate::login::auth::looks_auth)
}
pub(super) fn why(
dir: &Path,
agent: &str,
lock: &dyn LockProbe,
writer: &dyn WriterProbe,
) -> Option<String> {
classify(dir, agent, lock, writer).failure
}
pub(super) fn resp(dir: &Path, agent: &str, seq: &str) -> PathBuf {
dir.join(format!("{STEPS_DIR}/{agent}/{seq}/{RESPONSE_FILE}"))
}
pub(super) 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"}
"#;
#[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)
);
}