use ::litany::cmd::{RoutedCall, ToolInjection as _};
use crate::test_support::FakeClock;
use crate::tool_host::tests::{budget, scripted};
use crate::tool_host::{Injection, loaded};
use serde_json::json;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use tempfile::TempDir;
fn injection(root: &Path) -> Injection {
Injection::new(
root.to_path_buf(),
PathBuf::new(),
budget(),
budget(),
FakeClock::new().arc(),
)
}
macro_rules! call {
($name:expr, $input:expr, $stop:expr) => {
RoutedCall {
id: "toolu_1",
name: $name,
input: $input,
workspace: Path::new("/w/home"),
agent: "dulcet-mongoose",
cwd: Path::new("/w/home/agents/dulcet-mongoose"),
stop: $stop,
}
};
}
fn advertised(name: &str, subject_cwd: bool) -> serde_json::Value {
let mut tool = json!({"name": name, "description": "a tool",
"input_schema": {"type": "object"}});
if subject_cwd {
tool["subject_cwd"] = json!(true);
}
tool
}
fn roster(client: &str, tools: &[serde_json::Value]) -> serde_json::Value {
json!({"ok": true, "kind": "clients",
"rows": [{"client": client, "present": true, "tools": tools}]})
}
#[test]
fn a_granted_worktree_name_routes_with_the_conversations_cwd() {
let root = TempDir::new().expect("tmp");
let (handle, seen) = scripted(
root.path(),
&[
roster("laptop", &[advertised("bash", true)]),
json!({"ok": true, "kind": "routed", "invocation": "inv-9"}),
json!({"ok": true, "kind": "routed", "invocation": "inv-9",
"capture": {"stdout": "made\n", "stderr": "", "exit_code": 0}}),
],
);
let input = json!({"command": "printf made > out.txt"});
let stop = AtomicBool::new(false);
let capture = injection(root.path()).route(call!("bash", &input, &stop));
handle.join().expect("engine");
assert_eq!(capture.exit_code, 0);
assert_eq!(capture.stdout, b"made\n");
assert_eq!(
seen.recv().expect("the roster read"),
json!({"op": "clients", "workspace": "home"})
);
assert_eq!(
seen.recv().expect("the queueing act"),
json!({"op": "invoke", "client": "laptop", "tool": "bash",
"input": {"command": "printf made > out.txt"},
"cwd": "/w/home/agents/dulcet-mongoose"}),
"the invocation carries the subject's location"
);
assert_eq!(
seen.recv().expect("the poll"),
json!({"op": "capture", "invocation": "inv-9"})
);
}
#[test]
fn an_unconsenting_advertiser_is_refused_naming_the_remedy() {
let root = TempDir::new().expect("tmp");
let (handle, _seen) = scripted(
root.path(),
&[roster("laptop", &[advertised("bash", false)])],
);
let input = json!({"command": "ls"});
let stop = AtomicBool::new(false);
let capture = injection(root.path()).route(call!("bash", &input, &stop));
handle.join().expect("engine");
assert_eq!(capture.exit_code, 1);
let said = String::from_utf8_lossy(&capture.stderr).into_owned();
assert!(said.contains("laptop advertises bash"), "{said}");
assert!(
said.contains("no machine of this workspace consents"),
"{said}"
);
assert!(said.contains("\"subject_cwd\": true"), "{said}");
assert!(said.contains("clients tool"), "{said}");
}
#[test]
fn two_consenting_machines_are_an_ambiguity_refused_naming_them() {
let root = TempDir::new().expect("tmp");
let (handle, _seen) = scripted(
root.path(),
&[json!({"ok": true, "kind": "clients", "rows": [
{"client": "laptop", "present": true,
"tools": [advertised("bash", true)]},
{"client": "tower", "present": false,
"tools": [advertised("bash", true)]},
]})],
);
let input = json!({"command": "ls"});
let stop = AtomicBool::new(false);
let capture = injection(root.path()).route(call!("bash", &input, &stop));
handle.join().expect("engine");
assert_eq!(capture.exit_code, 1);
let said = String::from_utf8_lossy(&capture.stderr).into_owned();
assert!(said.contains("2 machines consent"), "{said}");
assert!(said.contains("laptop, tower"), "{said}");
assert!(said.contains("exactly one entry"), "{said}");
}
#[test]
fn a_lane_whose_invoke_is_never_answered_refuses_in_band() {
let root = TempDir::new().expect("tmp");
let (handle, _seen) = scripted(
root.path(),
&[roster("laptop", &[advertised("bash", true)])],
);
let input = json!({"command": "ls"});
let stop = AtomicBool::new(false);
let capture = injection(root.path()).route(call!("bash", &input, &stop));
handle.join().expect("engine");
assert_eq!(capture.exit_code, 1);
let said = String::from_utf8_lossy(&capture.stderr).into_owned();
assert!(said.starts_with("bash: "), "{said}");
assert!(said.contains("no engine answered"), "{said}");
}
#[test]
fn a_lane_whose_roster_is_never_answered_refuses_in_band() {
let root = TempDir::new().expect("tmp");
let input = json!({"command": "ls"});
let stop = AtomicBool::new(false);
let site = crate::tool_host::Site {
state_root: root.path().to_path_buf(),
workspace: "home".to_owned(),
agent: "dulcet-mongoose".to_owned(),
budget: crate::tool_host::tests::impatient(),
patience: crate::tool_host::tests::impatient(),
clock: FakeClock::new().arc(),
};
let capture = super::answer(&site, &call!("bash", &input, &stop));
assert_eq!(capture.exit_code, 1);
let said = String::from_utf8_lossy(&capture.stderr).into_owned();
assert!(said.starts_with("bash: "), "{said}");
assert!(said.contains("no engine answered"), "{said}");
assert!(
!said.contains("advertises"),
"a failed roster read is a transport fact, not a selection one: {said}"
);
}
#[test]
fn one_consenting_machine_wins_over_an_unconsenting_advertiser() {
let rows = vec![
crate::registry::roster::ClientRow {
client: "laptop".to_owned(),
present: true,
tools: vec![crate::registry::tools::Tool {
name: "bash".to_owned(),
description: "a tool".to_owned(),
input_schema: json!({"type": "object"}),
subject_cwd: false,
}],
},
crate::registry::roster::ClientRow {
client: "tower".to_owned(),
present: true,
tools: vec![crate::registry::tools::Tool {
name: "bash".to_owned(),
description: "a tool".to_owned(),
input_schema: json!({"type": "object"}),
subject_cwd: true,
}],
},
];
let picked = super::verdict(&rows, "bash").expect("one consenting machine");
assert_eq!(picked.client, "tower");
assert!(picked.tool.subject_cwd);
let said = super::verdict(&rows, "read_file").expect_err("nothing advertises it");
assert!(said.contains("no machine of this workspace advertises read_file"));
assert!(said.contains("clients tool"), "{said}");
assert!(said.contains("\"subject_cwd\": true"), "{said}");
}
#[test]
fn a_loaded_set_does_not_shadow_the_lane_for_a_bare_name() {
let root = TempDir::new().expect("tmp");
loaded::add(
root.path(),
"home",
"dulcet-mongoose",
&[loaded::Entry {
client: "laptop".to_owned(),
tool: crate::tool_host::tests::tool("bash"),
}],
)
.expect("loaded");
let (handle, seen) = scripted(
root.path(),
&[
roster("tower", &[advertised("bash", true)]),
json!({"ok": true, "kind": "routed", "invocation": "inv-2"}),
json!({"ok": true, "kind": "routed", "invocation": "inv-2",
"capture": {"stdout": "", "stderr": "", "exit_code": 0}}),
],
);
let input = json!({"command": "true"});
let stop = AtomicBool::new(false);
let capture = injection(root.path()).route(call!("bash", &input, &stop));
handle.join().expect("engine");
assert_eq!(capture.exit_code, 0);
let _ = seen.recv().expect("the roster read");
let queued = seen.recv().expect("the queueing act");
assert_eq!(queued["client"], "tower", "routed by roster, not by loads");
}