use super::*;
#[tokio::test]
async fn rmcp_client_integration_test() {
let server = test_server();
let (client_io, server_io) = tokio::io::duplex(8192);
let (server_r, server_w) = tokio::io::split(server_io);
tokio::spawn(async move {
let mut reader = tokio::io::BufReader::new(server_r);
let _ = server.run_async(&mut reader, server_w).await;
});
let mut client = rmcp::service::serve_client((), client_io)
.await
.expect("rmcp client handshake failed");
let tools = client.list_all_tools().await.expect("list tools failed");
assert_eq!(tools.len(), 3);
let call_res = client
.call_tool(
rmcp::model::CallToolRequestParams::new("add").with_arguments(
serde_json::json!({"a": 15, "b": 25})
.as_object()
.unwrap()
.clone(),
),
)
.await
.expect("call tool failed");
assert!(!call_res.is_error.unwrap_or(false));
assert_eq!(call_res.content.len(), 1);
let text = match &call_res.content[0] {
rmcp::model::ContentBlock::Text(t) => &t.text,
other => panic!("expected text content, got {other:?}"),
};
assert_eq!(text, "40");
let fail_res = client
.call_tool(rmcp::model::CallToolRequestParams::new("fail"))
.await
.expect("call tool response expected");
assert!(fail_res.is_error.unwrap_or(false));
let fail_text = match &fail_res.content[0] {
rmcp::model::ContentBlock::Text(t) => &t.text,
other => panic!("expected text content, got {other:?}"),
};
assert!(fail_text.contains("intentional failure"));
let unknown_res = client
.call_tool(rmcp::model::CallToolRequestParams::new("non_existent"))
.await
.expect("call tool response expected");
assert!(unknown_res.is_error.unwrap_or(false));
let unknown_text = match &unknown_res.content[0] {
rmcp::model::ContentBlock::Text(t) => &t.text,
other => panic!("expected text content, got {other:?}"),
};
assert!(unknown_text.contains("non_existent"));
assert!(unknown_text.contains("no tool named"));
let prompts = client
.list_all_prompts()
.await
.expect("list prompts failed");
assert!(prompts.is_empty());
let resources = client
.list_all_resources()
.await
.expect("list resources failed");
assert!(resources.is_empty());
let read_err = client
.read_resource(rmcp::model::ReadResourceRequestParams::new(
"file:///nonexistent",
))
.await
.unwrap_err();
match read_err {
rmcp::ServiceError::McpError(err) => {
assert_eq!(err.code, rmcp::model::ErrorCode::INVALID_PARAMS);
}
other => panic!("expected McpError with INVALID_PARAMS, got {other:?}"),
}
let _ = client.close().await;
}
#[tokio::test]
async fn rmcp_client_tcp_test() {
let server = test_server();
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
let _ = server.run_tcp_listener(listener).await;
});
let stream = tokio::net::TcpStream::connect(addr).await.unwrap();
let mut client = rmcp::service::serve_client((), stream)
.await
.expect("rmcp client handshake failed over TCP");
let tools = client
.list_all_tools()
.await
.expect("list tools failed over TCP");
assert_eq!(tools.len(), 3);
let _ = client.close().await;
}
#[cfg(unix)]
#[tokio::test]
async fn rmcp_client_unix_test() {
let server = test_server();
let dir = tempfile::tempdir().unwrap();
let sock_path = dir.path().join("rmcp.sock");
let listener = tokio::net::UnixListener::bind(&sock_path).unwrap();
tokio::spawn(async move {
let _ = server.run_unix_listener(listener).await;
});
let stream = tokio::net::UnixStream::connect(&sock_path).await.unwrap();
let mut client = rmcp::service::serve_client((), stream)
.await
.expect("rmcp client handshake failed over Unix socket");
let tools = client
.list_all_tools()
.await
.expect("list tools failed over Unix socket");
assert_eq!(tools.len(), 3);
let _ = client.close().await;
}