use std::path::PathBuf;
use std::sync::Arc;
use tempfile::{TempDir, tempdir};
use super::interrupt;
use crate::boundary::dispatch::Deps;
use crate::boundary::reply::Reply;
use crate::boundary::tests::snapshot;
use crate::cli_outbound::Cli;
use crate::opslog::tail;
const TS: &str = "2026-08-15T00:00:00Z";
const AGENT: &str = "alba-1";
struct World {
dir: TempDir,
}
impl World {
fn new() -> World {
let world = World {
dir: tempdir().expect("tempdir"),
};
std::fs::create_dir_all(world.workspace()).expect("workspace dir");
world
}
fn workspace(&self) -> PathBuf {
self.dir.path().join("names").join("alba")
}
fn state(&self) -> PathBuf {
self.dir.path().join("state")
}
fn deps(&self) -> Deps {
Deps {
lernie: Cli::new("/usr/bin/true"),
bl: Cli::new("/no/such/bl"),
state_root: self.state(),
home: self.dir.path().join("home"),
yog_data_root: self.dir.path().join("data"),
balls_state_root: self.dir.path().join("balls"),
yog_binary: PathBuf::from("/no/such/yog"),
world: crate::xdg::Env::from_env(),
snapshot: Arc::new(snapshot(&self.workspace(), "alba", Vec::new(), Vec::new())),
caller: crate::boundary::dispatch::Caller::default(),
}
}
fn verbs(&self) -> Vec<String> {
tail(&self.state(), usize::MAX)
.into_iter()
.filter_map(|e| e.argv.get(1).cloned())
.collect()
}
}
#[test]
fn it_stops_then_deposits_and_leaves_one_row_for_each() {
let world = World::new();
let reply = interrupt(
&world.deps(),
TS,
&world.workspace(),
AGENT,
"do this instead",
);
assert!(matches!(reply, Ok(Reply::Outcome(_))), "{reply:?}");
assert_eq!(world.verbs(), vec!["stop".to_owned(), "message".to_owned()]);
}
#[test]
fn the_reply_is_the_deposits_own_outcome() {
let world = World::new();
let Ok(Reply::Outcome(outcome)) =
interrupt(&world.deps(), TS, &world.workspace(), AGENT, "text")
else {
panic!("a clean pair answers with an outcome");
};
assert_eq!(outcome.exit, 0);
let rows = tail(&world.state(), usize::MAX);
assert_eq!(rows.len(), 2, "and both halves are on the trail");
}
#[test]
fn a_stop_that_cannot_spawn_refuses_before_the_deposit() {
let world = World::new();
let deps = Deps {
lernie: Cli::new("/no/such/lernie"),
..world.deps()
};
let refused = interrupt(&deps, TS, &world.workspace(), AGENT, "text");
assert!(refused.is_err(), "{refused:?}");
assert_eq!(world.verbs(), vec!["stop".to_owned()]);
}