use serde_json::{Map, Value, json};
use super::tools::{Server, ToolResult, tool_table};
use crate::network::fake::{Answer, Fake};
type TestResult = Result<(), Box<dyn std::error::Error>>;
pub(super) fn fixture(subjects: &[&str]) -> Fake {
fixture_services(&json!([{"id":"svc-one", "name":"one", "subjects":subjects}]))
}
fn fixture_services(services: &Value) -> Fake {
Fake::new(
json!({}),
&[("services.list", Answer::Reply(json!({"services":services})))],
)
}
pub(super) fn args(value: Value) -> Map<String, Value> {
match value {
Value::Object(arguments) => arguments,
_ => Map::new(),
}
}
fn tool(server: &Server<Fake>, name: &str, service: &str) -> ToolResult {
let answer = server.tool(name, &args(json!({"service":service})));
assert!(answer.is_ok(), "{answer:?}");
answer.unwrap_or_else(|refusal| ToolResult::failed(&refusal.message, json!({})))
}
#[test]
fn net027_tools_list_service_schemas() {
for name in ["subscribe", "unsubscribe"] {
let table = tool_table();
let entry = table
.as_array()
.and_then(|tools| tools.iter().find(|t| t["name"] == name));
assert!(entry.is_some(), "missing {name} in tools/list");
let schema = entry.map(|t| t["inputSchema"].clone()).unwrap_or_default();
assert_eq!(schema["required"], json!(["service"]));
assert_eq!(schema["properties"]["service"]["type"], "string");
assert_eq!(schema["additionalProperties"], false);
}
}
#[test]
fn net027_subscribe_by_name_is_idempotent() -> TestResult {
let server = Server::new(fixture(&["orders.one", "events.one"]), 65536);
let expected =
json!({"service":"one", "station":"svc-one", "subjects":["orders.one", "events.one"]});
for _ in 0..2 {
let answer = tool(&server, "subscribe", "one");
assert!(!answer.is_error, "{:?}", answer.structured);
assert_eq!(answer.structured, expected);
}
assert_eq!(
server.rail().taps.opened()?,
["console.tap.orders.one", "console.tap.events.one"]
);
assert_eq!(server.rail().pushed().len(), 1);
Ok(())
}
#[test]
fn net027_unknown_service_is_a_structured_refusal() {
let server = Server::new(fixture(&["orders.one"]), 65536);
let answer = tool(&server, "subscribe", "missing");
assert!(answer.is_error);
assert_eq!(answer.structured["service"], "missing");
assert_eq!(answer.structured["error"], "no service named 'missing'");
}
#[test]
fn net027_unsubscribe_closes_all_and_refuses_absence() -> TestResult {
let server = Server::new(fixture(&["orders.one", "events.one"]), 65536);
assert!(!tool(&server, "subscribe", "one").is_error);
let answer = tool(&server, "unsubscribe", "one");
assert!(!answer.is_error);
assert_eq!(
answer.structured,
json!({"service":"one", "subjects":["orders.one", "events.one"]})
);
assert_eq!(server.rail().taps.active()?, 0);
for service in ["one", "missing"] {
let answer = tool(&server, "unsubscribe", service);
assert!(answer.is_error);
assert_eq!(
answer.structured["error"],
format!("no subscription to service '{service}'")
);
}
Ok(())
}
#[test]
fn net027_a_refused_subject_rolls_back_the_entire_service() -> TestResult {
let server = Server::new(fixture(&["orders.one", "events.one"]), 65536);
server
.rail()
.taps
.refuse("console.tap.events.one", "missing_grant: events.one")?;
let answer = tool(&server, "subscribe", "one");
assert!(answer.is_error);
assert_eq!(answer.structured["subject"], "events.one");
assert_eq!(answer.structured["error"], "missing_grant: events.one");
assert_eq!(server.rail().taps.active()?, 0);
assert!(tool(&server, "unsubscribe", "one").is_error);
Ok(())
}
#[test]
fn net027_sixteen_services_is_the_ticket_bound() -> TestResult {
let services: Vec<_> = (0..17).map(|n| json!({"id":format!("svc-{n}"), "name":format!("s{n}"), "subjects":[format!("orders.s{n}")]})).collect();
let server = Server::new(fixture_services(&json!(services)), 65536);
for n in 0..16 {
assert!(!tool(&server, "subscribe", &format!("s{n}")).is_error);
}
let answer = tool(&server, "subscribe", "s16");
assert!(answer.is_error);
assert_eq!(
answer.structured["error"],
"the ticket already subscribes to 16 services"
);
assert_eq!(server.rail().taps.active()?, 16);
assert!(!tool(&server, "unsubscribe", "s0").is_error);
assert!(!tool(&server, "subscribe", "s16").is_error);
Ok(())
}
#[test]
fn net027_malformed_service_listing_is_named() {
let server = Server::new(
fixture_services(&json!([{"id":"svc-one", "name":"one", "subjects":[7]}])),
65536,
);
let answer = tool(&server, "subscribe", "one");
assert!(answer.is_error);
assert!(
answer.structured["error"]
.as_str()
.is_some_and(|words| words.contains("subjects"))
);
}
#[test]
fn net027_logging_is_declared_and_levels_are_validated() {
let mut server = Server::new(fixture(&["orders.one"]), 65536);
let answer = server.answer(
&json!(1),
"initialize",
&args(json!({"protocolVersion":"2025-11-25"})),
);
assert_eq!(answer["result"]["capabilities"]["logging"], json!({}));
assert_eq!(
super::negotiate::Session::discover()["capabilities"]["logging"],
json!({})
);
let answer = server.answer(
&json!(2),
"logging/setLevel",
&args(json!({"level":"info"})),
);
assert_eq!(answer["result"], json!({}));
for arguments in [json!({}), json!({"level":7}), json!({"level":"wrong"})] {
let answer = server.answer(&json!(3), "logging/setLevel", &args(arguments));
assert_eq!(answer["error"]["code"], -32602);
}
}
#[test]
fn net027_subscribe_validates_arguments_before_opening() -> TestResult {
let mut server = Server::new(fixture(&["orders.one"]), 65536);
let _init = server.answer(
&json!(1),
"initialize",
&args(json!({"protocolVersion":"2025-11-25"})),
);
for arguments in [
json!({}),
json!({"service":7}),
json!({"service":""}),
json!({"service":"one", "other":true}),
] {
let answer = server.answer(
&json!(2),
"tools/call",
&args(json!({"name":"subscribe", "arguments":arguments})),
);
assert_eq!(answer["error"]["code"], -32602, "{answer}");
}
assert!(server.rail().pushed().is_empty());
assert_eq!(server.rail().taps.active()?, 0);
Ok(())
}
#[test]
fn net027_service_listing_refusal_is_verbatim() {
let rail = Fake::new(
json!({}),
&[(
"services.list",
Answer::Refuse("missing_grant: services.list".to_owned()),
)],
);
let answer = tool(&Server::new(rail, 65536), "subscribe", "one");
assert!(answer.is_error);
assert_eq!(answer.structured["error"], "missing_grant: services.list");
}
#[test]
fn net027_ambiguous_names_and_duplicate_subjects_are_refused() -> TestResult {
for services in [
json!([{"id":"svc-a","name":"one","subjects":["a"]},{"id":"svc-b","name":"one","subjects":["b"]}]),
json!([{"id":"svc-a","name":"one","subjects":["a","a"]}]),
] {
let server = Server::new(fixture_services(&services), 65536);
assert!(tool(&server, "subscribe", "one").is_error);
assert_eq!(server.rail().taps.active()?, 0);
}
Ok(())
}