Files
hermes-grocy-mcp/tests/mcp_protocol.rs

62 lines
2.5 KiB
Rust

mod support;
use hermes_grocy_mcp::{mcp::McpServer, model::AccessMode};
use rmcp::{ServiceExt, model::CallToolRequestParams};
use serde_json::json;
use std::sync::Arc;
use support::{MemoryStore, selection};
#[tokio::test]
async fn protocol_returns_data_and_policy() {
let app = axum::Router::new().fallback(|| async { axum::Json(json!([{"id":7,"amount":"2"}])) });
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let api = tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
for mode in [AccessMode::Full, AccessMode::ReadOnly] {
let store = Arc::new(MemoryStore {
url: format!("http://127.0.0.1:{port}"),
access: mode,
key: "synthetic-key".into(),
});
let server = McpServer::new(store, selection(), mode);
let (a, b) = tokio::io::duplex(65536);
let serving =
tokio::spawn(async move { server.serve(a).await.unwrap().waiting().await.unwrap() });
let client = ().serve(b).await.unwrap();
let tools = client.list_all_tools().await.unwrap();
assert!(tools.len() > 20);
if mode == AccessMode::ReadOnly {
assert!(!tools.iter().any(|t| t.name == "records_delete"));
} else {
let delete = tools.iter().find(|t| t.name == "records_delete").unwrap();
assert_eq!(
delete.annotations.as_ref().unwrap().destructive_hint,
Some(true)
);
}
let result = client
.call_tool(
CallToolRequestParams::new("stock_list")
.with_arguments(json!({}).as_object().unwrap().clone()),
)
.await
.unwrap();
let text = result.content[0].as_text().unwrap();
let data: serde_json::Value = serde_json::from_str(&text.text).unwrap();
assert_eq!(Some(data.clone()), result.structured_content);
assert_eq!(data["data"]["items"][0]["id"], 7);
assert_ne!(result.is_error, Some(true));
let invalid = client
.call_tool(CallToolRequestParams::new("not_a_tool"))
.await
.unwrap();
assert_eq!(invalid.is_error, Some(true));
assert!(
!serde_json::to_string(&invalid)
.unwrap()
.contains("synthetic-key")
);
client.cancel().await.unwrap();
serving.await.unwrap();
}
api.abort();
}