#![allow(clippy::unwrap_used)]
#![allow(clippy::pedantic, clippy::nursery, missing_docs)]
use std::sync::Arc;
use polyc_agent::ToolExecutor;
use polyc_llm::ToolSpec;
use polyc_tools::{CompositeRegistry, ConnectOptions, ConnectionPool, McpToolSource, SpecSource};
async fn dead_registry() -> CompositeRegistry {
let source = McpToolSource::pooled(
ConnectionPool::new(),
"conv-dead",
"http://127.0.0.1:1/mcp",
ConnectOptions {
label: Some("notes".to_owned()),
source: SpecSource::Shipped(vec![ToolSpec::new(
"echo",
"Echo the input text back.",
serde_json::json!({ "type": "object" }),
)]),
..ConnectOptions::default()
},
)
.await
.expect("a shipped catalog composes with zero network");
CompositeRegistry::new().with(Arc::new(source))
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn dead_connector_call_is_a_typed_unavailable_result_not_a_vanished_tool() {
let registry = dead_registry().await;
assert!(registry.owns("notes__echo"), "the registry owns the tool");
assert!(
registry.specs().iter().any(|s| s.name == "notes__echo"),
"the tool is listed before the call"
);
let out = registry.execute("notes__echo", "{}").await;
let v: serde_json::Value = serde_json::from_str(&out).expect("a JSON tool result");
assert_eq!(
v["kind"], "transport_unreachable",
"a dead connector must surface the typed transport kind: {out}"
);
let msg = v["error"].as_str().expect("a human-readable message");
assert!(
msg.contains("temporarily unavailable"),
"the message must say the tool is temporarily unavailable: {msg}"
);
assert!(
msg.contains("Try again"),
"the message must say the tool recovers (try again): {msg}"
);
assert!(registry.owns("notes__echo"), "still owned after the call");
assert!(
registry.specs().iter().any(|s| s.name == "notes__echo"),
"still listed after the call"
);
}