use super::*;
use crate::boundary::reply::{self, Reply};
use crate::registry::tools::Tool;
use crate::tool_host::tests::{budget, engine, impatient, site, tool};
use serde_json::json;
use std::path::Path;
use tempfile::TempDir;
fn quiet() -> AtomicBool {
AtomicBool::new(false)
}
fn row(client: &str, present: bool, tools: Vec<Tool>) -> ClientRow {
ClientRow {
client: client.to_owned(),
present,
tools,
}
}
fn against(root: &Path, rows: Vec<ClientRow>, input: &Value) -> Result<String, String> {
let (handle, _seen) = engine(root, &reply::encode(&Reply::Clients(rows)));
let answered = answer(&site(root, budget()), input, &quiet());
handle.join().expect("engine");
answered
}
#[test]
fn the_declared_schema_names_the_three_ops() {
let s = schema();
assert_eq!(
s["properties"]["op"]["enum"],
json!(["list", "get", "load"])
);
assert_eq!(s["required"], json!(["op"]));
assert!(DESCRIPTION.contains("op=load"));
}
#[test]
fn a_well_formed_invocation_reads_as_its_op() {
assert_eq!(parse(&json!({"op": "list"})).expect("list"), Op::List);
assert_eq!(
parse(&json!({"op": "get", "client": "laptop"})).expect("get"),
Op::Get("laptop".to_owned())
);
assert_eq!(
parse(&json!({"op": "load", "client": "laptop", "tools": ["Bash"]})).expect("load"),
Op::Load("laptop".to_owned(), vec!["Bash".to_owned()])
);
}
#[test]
fn a_malformed_invocation_names_the_field() {
let cases = [
(json!(7), "not a JSON object"),
(json!({}), "\"op\""),
(json!({"op": "wat"}), "unknown op"),
(json!({"op": "get"}), "\"client\""),
(json!({"op": "get", "client": ""}), "\"client\""),
(json!({"op": "load", "client": "a"}), "\"tools\""),
(
json!({"op": "load", "client": "a", "tools": []}),
"\"tools\"",
),
(
json!({"op": "load", "client": "a", "tools": ["Bash", 7]}),
"\"tools\"",
),
];
for (input, said) in cases {
let e = parse(&input).expect_err("declined");
assert!(e.contains(said), "{input} -> {e}");
}
}
#[test]
fn list_observes_the_roster_at_an_instant() {
let root = TempDir::new().expect("tmp");
let said = against(
root.path(),
vec![
row("laptop", true, vec![tool("Bash")]),
row("phone", false, vec![]),
],
&json!({"op": "list"}),
)
.expect("listed");
assert!(said.contains("observed 1970-01-01 00:00:00Z"), "{said}");
assert!(said.contains("laptop — connected right now, advertising 1 tool\n"));
assert!(said.contains("phone — not connected right now, advertising 0 tools\n"));
assert!(said.contains("op=get with client=<name>"), "{said}");
}
#[test]
fn list_says_so_when_nothing_is_registered() {
let root = TempDir::new().expect("tmp");
let said = against(root.path(), vec![], &json!({"op": "list"})).expect("listed");
assert!(said.contains("(none is registered here)"), "{said}");
}
#[test]
fn get_shows_each_tool_and_the_name_it_loads_as() {
let root = TempDir::new().expect("tmp");
let said = against(
root.path(),
vec![row("laptop", true, vec![tool("Bash"), tool("Read")])],
&json!({"op": "get", "client": "laptop"}),
)
.expect("got");
assert!(
said.contains("client \"laptop\" of workspace \"home\""),
"{said}"
);
assert!(said.contains("It advertises 2 tools:"), "{said}");
assert!(said.contains("Bash — what Bash does"), "{said}");
assert!(said.contains("loads as: laptop_Bash"), "{said}");
assert!(said.contains("op=load with client=\"laptop\""), "{said}");
}
#[test]
fn get_on_a_client_that_advertises_nothing_says_so() {
let root = TempDir::new().expect("tmp");
let said = against(
root.path(),
vec![row("phone", false, vec![])],
&json!({"op": "get", "client": "phone"}),
)
.expect("got");
assert!(said.contains("not connected right now"), "{said}");
assert!(said.contains("It advertises no tools."), "{said}");
}
#[test]
fn an_unregistered_client_is_absent() {
let root = TempDir::new().expect("tmp");
let e = against(
root.path(),
vec![row("laptop", true, vec![])],
&json!({"op": "get", "client": "desk"}),
)
.expect_err("absent");
assert!(e.contains("no client \"desk\" is registered"), "{e}");
assert!(e.contains("the 1 there are"), "{e}");
}
#[test]
fn load_makes_the_named_tools_callable_and_durable() {
let root = TempDir::new().expect("tmp");
let said = against(
root.path(),
vec![row("laptop", true, vec![tool("Bash"), tool("Read")])],
&json!({"op": "load", "client": "laptop", "tools": ["Bash"]}),
)
.expect("loaded");
assert!(said.contains("loaded, observed 1970-01-01"), "{said}");
assert!(said.contains("laptop_Bash — what Bash does"), "{said}");
assert!(said.contains("now holds 1 loaded tool."), "{said}");
assert!(said.contains("There is no unload"), "{said}");
let held = crate::tool_host::loaded::read(root.path(), "home", "dulcet-mongoose");
assert_eq!(held.len(), 1);
assert_eq!(held[0].tool, tool("Bash"), "the definition is frozen whole");
}
#[test]
fn an_unadvertised_tool_refuses_the_whole_load() {
let root = TempDir::new().expect("tmp");
let e = against(
root.path(),
vec![row("laptop", true, vec![tool("Bash")])],
&json!({"op": "load", "client": "laptop", "tools": ["Bash", "Nope"]}),
)
.expect_err("refused");
assert!(e.contains("advertises no tool \"Nope\""), "{e}");
assert!(
crate::tool_host::loaded::read(root.path(), "home", "dulcet-mongoose").is_empty(),
"nothing was recorded"
);
}
#[test]
fn a_name_a_provider_would_refuse_declines_at_the_load() {
let root = TempDir::new().expect("tmp");
let e = against(
root.path(),
vec![row("laptop.local", true, vec![tool("Bash")])],
&json!({"op": "load", "client": "laptop.local", "tools": ["Bash"]}),
)
.expect_err("refused");
assert!(
e.contains("\"laptop.local_Bash\" is not a usable tool name"),
"{e}"
);
}
#[test]
fn a_load_that_cannot_be_recorded_says_so() {
let root = TempDir::new().expect("tmp");
let mut s = site(root.path(), budget());
s.agent = "..".to_owned();
let (handle, _seen) = engine(
root.path(),
&reply::encode(&Reply::Clients(vec![row(
"laptop",
true,
vec![tool("Bash")],
)])),
);
let e = answer(
&s,
&json!({"op": "load", "client": "laptop", "tools": ["Bash"]}),
&quiet(),
)
.expect_err("unrecordable");
handle.join().expect("engine");
assert!(e.contains("recording the load"), "{e}");
}
#[test]
fn a_declined_invocation_never_reaches_the_engine() {
let root = TempDir::new().expect("tmp");
let e = answer(
&site(root.path(), impatient()),
&json!({"op": "wat"}),
&quiet(),
)
.expect_err("declined");
assert!(e.contains("unknown op"), "{e}");
assert!(
crate::boundary::deposit::pending(root.path()).is_empty(),
"nothing was deposited"
);
}
#[test]
fn an_unreachable_engine_is_said_in_band() {
let root = TempDir::new().expect("tmp");
let e = answer(
&site(root.path(), impatient()),
&json!({"op": "list"}),
&quiet(),
)
.expect_err("no engine");
assert!(e.contains("no engine answered"), "{e}");
}