use super::*;
use crate::registry::tools::Tool;
use crate::tool_host::tests::{budget, engine, impatient};
use serde_json::json;
use tempfile::TempDir;
fn quiet() -> AtomicBool {
AtomicBool::new(false)
}
#[test]
fn the_default_budget_is_a_bounded_wait() {
let b = Budget::default();
assert!(b.waits > 0 && b.tick > Duration::ZERO);
assert_eq!(b.tick * b.waits, Duration::from_secs(10));
}
#[test]
fn a_deposit_earns_the_engine_s_own_envelope() {
let root = TempDir::new().expect("tmp");
let answer = json!({"ok": true, "kind": "clients", "rows": []});
let (handle, seen) = engine(root.path(), &answer);
let got = ask(
root.path(),
&json!({"op": "clients", "workspace": "home"}),
budget(),
&quiet(),
)
.expect("answered");
handle.join().expect("engine");
assert_eq!(got, answer);
assert_eq!(seen.recv().expect("request")["op"], "clients");
}
#[test]
fn no_engine_gives_up_and_says_so() {
let root = TempDir::new().expect("tmp");
let e = ask(
root.path(),
&json!({"op": "clients"}),
impatient(),
&quiet(),
)
.expect_err("no consumer");
assert!(e.contains("no engine answered"), "{e}");
}
#[test]
fn a_stop_ends_the_wait() {
let root = TempDir::new().expect("tmp");
let stopped = AtomicBool::new(true);
let e = ask(
root.path(),
&json!({"op": "clients"}),
impatient(),
&stopped,
)
.expect_err("stopped");
assert!(e.contains("stopped while waiting"), "{e}");
}
#[test]
fn an_unusable_state_root_refuses_at_the_mint() {
let root = TempDir::new().expect("tmp");
let file = root.path().join("not-a-dir");
std::fs::write(&file, b"x").expect("write");
let e = ask(&file, &json!({"op": "clients"}), impatient(), &quiet()).expect_err("no inbox");
assert!(e.starts_with("gesture id: "), "{e}");
}
#[test]
fn an_occupied_inbox_slot_refuses_at_the_deposit() {
let root = TempDir::new().expect("tmp");
let dir = crate::boundary::deposit::gestures_dir(root.path());
std::fs::create_dir_all(dir.join("toolhost-0.json")).expect("squat the slot");
let e = ask(
root.path(),
&json!({"op": "clients"}),
impatient(),
&quiet(),
)
.expect_err("occupied");
assert!(e.starts_with("deposit: "), "{e}");
}
#[test]
fn the_roster_is_the_landed_clients_query() {
let root = TempDir::new().expect("tmp");
let rows = vec![crate::registry::roster::ClientRow {
client: "laptop".to_owned(),
present: true,
last_seen: None,
tools: vec![Tool {
name: "Bash".to_owned(),
description: "run a command".to_owned(),
input_schema: json!({"type": "object"}),
subject_cwd: false,
}],
}];
let answer = crate::boundary::reply::encode(&Reply::Clients(rows.clone()));
let (handle, seen) = engine(root.path(), &answer);
let got = roster(root.path(), "home", budget(), &quiet()).expect("rows");
handle.join().expect("engine");
assert_eq!(got, rows);
assert_eq!(
seen.recv().expect("request"),
json!({"op": "clients", "workspace": "home"})
);
}
#[test]
fn an_answer_that_is_not_a_roster_is_named() {
let root = TempDir::new().expect("tmp");
let cases = [
(
json!({"ok": false, "error": "unknown workspace \"x\""}),
"unknown workspace",
),
(
crate::boundary::reply::encode(&Reply::Acked),
"not a client roster",
),
(json!({"ok": true, "kind": "no-such-kind"}), "undecodable"),
];
for (answer, said) in cases {
let (handle, _seen) = engine(root.path(), &answer);
let e = roster(root.path(), "home", budget(), &quiet()).expect_err("not a roster");
handle.join().expect("engine");
assert!(e.contains(said), "{e}");
}
}