use super::Harness;
use crate::boundary::{Action, reply::Reply};
use crate::cli_outbound::Cli;
use crate::git_tree::AgentState;
use crate::test_support::engine;
use std::path::{Path, PathBuf};
use tempfile::{TempDir, tempdir};
fn fake(dir: &Path, name: &str) -> Cli {
let path = dir.join(name);
crate::test_support::write_exec(&path, "#!/bin/sh\nprintf '%s\\n' \"$*\"\nexit 0\n");
Cli::new(path)
}
struct World {
bin: TempDir,
h: Harness,
named: PathBuf,
}
fn world() -> World {
let bin = tempdir().unwrap();
let mut h = Harness::new();
let named = h.mint_named("cobalt", "c-1");
World { bin, h, named }
}
impl World {
fn deps(&self, rig: &crate::AppModel) -> crate::boundary::dispatch::Deps {
rig.boundary_deps(
&fake(self.bin.path(), "litany"),
&fake(self.bin.path(), "bl"),
)
}
}
#[test]
fn an_unnamed_workspace_is_refused_before_any_gate() {
let w = world();
let (_c, m) = w.h.model();
let deps = w.deps(&m);
for action in [
Action::DeleteWorkspace {
workspace: "ws".to_owned(),
typed: "ws".to_owned(),
},
Action::DeleteAgent {
workspace: "ws".to_owned(),
agent: "c-1".to_owned(),
typed: String::new(),
},
] {
let err = engine::act(&m, &deps, "TS", &action).unwrap_err();
assert!(err.contains("not a yog-named workspace"), "{err}");
}
assert!(w.h.ws.exists(), "a refusal attempts nothing");
}
#[test]
fn a_workspace_delete_needs_its_own_name_typed() {
let w = world();
let (_c, m) = w.h.model();
let deps = w.deps(&m);
let unmake = |typed: &str| {
engine::act(
&m,
&deps,
"TS",
&Action::DeleteWorkspace {
workspace: "cobalt".to_owned(),
typed: typed.to_owned(),
},
)
};
let err = unmake("spare").unwrap_err();
assert!(err.contains("type the workspace's name"), "{err}");
assert!(w.named.exists(), "a refusal attempts nothing");
assert!(matches!(unmake(" cobalt ").unwrap(), Reply::Deleted));
assert!(
!w.named.exists(),
"the armed fire unmakes the workspace's own directory"
);
}
#[test]
fn a_live_conversation_refuses_the_workspace_and_the_conversation() {
let w = world();
let (_c, mut rig) = w.h.model();
rig.deriver
.trees
.entry(w.named.clone())
.or_default()
.agents
.push(super::agent("c-1-x-2", AgentState::Live));
rig.publish();
let deps = w.deps(&rig.model);
let ws_err = engine::act(
&rig.model,
&deps,
"TS",
&Action::DeleteWorkspace {
workspace: "cobalt".to_owned(),
typed: "cobalt".to_owned(),
},
)
.unwrap_err();
assert!(
ws_err.contains("live conversations") && ws_err.contains("hi"),
"the refusal names what is live, by the §3.3 display name a seat shows \
rather than by an id: {ws_err}"
);
let agent_err = engine::act(
&rig.model,
&deps,
"TS",
&Action::DeleteAgent {
workspace: "cobalt".to_owned(),
agent: "c-1".to_owned(),
typed: String::new(),
},
)
.unwrap_err();
assert!(agent_err.contains("stop them first"), "{agent_err}");
assert!(w.named.exists(), "neither refusal attempted anything");
}
#[test]
fn an_agent_delete_spawns_the_litany_verb_and_arms_children_by_name() {
let w = world();
let (_c, m) = w.h.model();
let deps = w.deps(&m);
let fire = |typed: &str| {
let reply = engine::act(
&m,
&deps,
"TS",
&Action::DeleteAgent {
workspace: "cobalt".to_owned(),
agent: "c-1".to_owned(),
typed: typed.to_owned(),
},
)
.expect("the agent delete answers a captured run");
let Reply::Outcome(outcome) = reply else {
panic!("the agent delete answers a captured run");
};
outcome.stdout
};
let bare = fire("");
assert!(bare.contains("delete"), "{bare}");
assert!(
!bare.contains("--children"),
"unarmed is the bare verb, and litany declines a subtree nobody \
confirmed: {bare}"
);
let armed = fire("hi");
assert!(armed.contains("--children"), "{armed}");
}