use serde_json::{Map, Value, json};
use super::{tool, tool_entries};
use crate::network::fake::{Answer, Fake};
fn arguments(value: &Value) -> Map<String, Value> {
value.as_object().cloned().unwrap_or_default()
}
#[test]
fn the_five_tools_are_in_the_table_with_their_schemas() {
let names: Vec<String> = tool_entries()
.iter()
.filter_map(|entry| entry["name"].as_str().map(str::to_owned))
.collect();
assert_eq!(names, ["services", "register", "start", "stop", "retire"]);
let register = &tool_entries()[1]["inputSchema"];
assert_eq!(register["required"], json!(["template", "name"]));
assert_eq!(tool_entries()[2]["inputSchema"]["required"], json!(["id"]));
let table = crate::mcp::tools::tool_table();
assert_eq!(table.as_array().map(Vec::len), Some(21));
}
#[test]
fn register_sends_create_with_the_operation_and_answers_verbatim() {
let reply = json!({ "outcome": "acted", "service": { "id": "svc-1" } });
let rail = Fake::new(
json!({}),
&[("services.create", Answer::Reply(reply.clone()))],
);
let answer = tool(
&rail,
"register",
&arguments(&json!({ "template": "echo", "name": "one", "operation": "op-7" })),
);
assert_eq!(answer.as_ref().map(|a| a.structured.clone()), Some(reply));
assert_eq!(
rail.pushed(),
vec![(
"services.create".to_owned(),
json!({ "operation": "op-7", "payload": { "template": "echo", "name": "one" } })
)]
);
}
#[test]
fn a_refusal_is_the_servers_words() {
let rail = Fake::new(
json!({}),
&[("services.hold", Answer::Refuse("missing_grant".to_owned()))],
);
let answer = tool(&rail, "stop", &arguments(&json!({ "id": "svc-1" })));
assert!(answer.as_ref().is_some_and(|a| a.is_error));
let words = answer.map(|a| a.structured.to_string()).unwrap_or_default();
assert!(words.contains("missing_grant"), "{words}");
}
#[test]
fn start_wakes_a_held_service() {
let rail = Fake::new(
json!({}),
&[
(
"services.deploy",
Answer::Refuse("not_now: deploy does not apply to a service Held".to_owned()),
),
(
"services.wake",
Answer::Reply(json!({ "outcome": "acted" })),
),
],
);
let answer = tool(
&rail,
"start",
&arguments(&json!({ "id": "svc-1", "operation": "op-1" })),
);
assert_eq!(answer.map(|a| a.is_error), Some(false));
let subjects: Vec<String> = rail.pushed().into_iter().map(|(s, _)| s).collect();
assert_eq!(subjects, ["services.deploy", "services.wake"]);
}
#[test]
fn a_missing_argument_sends_nothing_and_another_tool_is_not_ours() {
let rail = Fake::new(json!({}), &[]);
let answer = tool(&rail, "retire", &arguments(&json!({})));
assert!(answer.is_some_and(|a| a.is_error));
assert!(rail.pushed().is_empty());
assert!(tool(&rail, "call", &arguments(&json!({}))).is_none());
}
#[test]
fn an_operation_is_made_when_none_is_given() {
let rail = Fake::new(json!({}), &[("services.list", Answer::Echo)]);
let answer = tool(&rail, "services", &Map::new());
let operation = answer
.and_then(|a| {
a.structured["echoed"]["operation"]
.as_str()
.map(str::to_owned)
})
.unwrap_or_default();
assert!(
operation.starts_with("op-") && operation.len() == 35,
"{operation}"
);
}