use futures_util::{SinkExt, StreamExt};
use serde_json::json;
use std::time::Duration;
use tokio_tungstenite::tungstenite::Message;
mod mcp_test_helpers;
use mcp_test_helpers::{receive_ws_message, with_mcp_test_server};
#[tokio::test]
async fn test_malformed_json_no_panic() {
with_mcp_test_server("malformed_json_test", |server| async move {
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
let malformed_messages = vec![
"{invalid json}",
"{'single': 'quotes'}",
"{\"unterminated\": ",
"null",
"[]",
"42",
];
for msg in malformed_messages {
write.send(Message::Text(msg.to_string().into())).await?;
let response = receive_ws_message(&mut read, Duration::from_secs(2)).await?;
let parsed: serde_json::Value = serde_json::from_str(&response)?;
assert!(parsed.get("error").is_some());
let error_code = parsed["error"]["code"].as_i64().unwrap();
assert!(error_code == -32700 || error_code == -32603);
}
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_missing_fields_no_panic() {
with_mcp_test_server("missing_fields_test", |server| async move {
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
let incomplete_messages = vec![
json!({}), json!({"jsonrpc": "2.0"}), json!({"method": "initialize"}), json!({"jsonrpc": "2.0", "method": "tools/call"}), json!({"jsonrpc": "2.0", "method": "tools/call", "params": {}}), ];
for msg in incomplete_messages {
write
.send(Message::Text(serde_json::to_string(&msg)?.into()))
.await?;
let response = receive_ws_message(&mut read, Duration::from_secs(2)).await?;
let parsed: serde_json::Value = serde_json::from_str(&response)?;
assert!(parsed.get("error").is_some());
let error_code = parsed["error"]["code"].as_i64().unwrap();
assert!(
error_code == -32600
|| error_code == -32602
|| error_code == -32603
|| error_code == -32002,
"Unexpected error code: {}",
error_code
);
}
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_tools_list_response_no_panic() {
with_mcp_test_server("tools_list_panic_test", |server| async move {
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
let init = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {}
}
});
write
.send(Message::Text(serde_json::to_string(&init)?.into()))
.await?;
let _response = receive_ws_message(&mut read, Duration::from_secs(2)).await?;
let tools_list = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
});
write
.send(Message::Text(serde_json::to_string(&tools_list)?.into()))
.await?;
let response = receive_ws_message(&mut read, Duration::from_secs(2)).await?;
let parsed: serde_json::Value = serde_json::from_str(&response)?;
assert!(parsed.get("result").is_some());
let result = &parsed["result"];
assert!(result.get("tools").is_some());
let tools = result["tools"].as_array();
assert!(tools.is_some());
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_websocket_binary_message_no_panic() {
with_mcp_test_server("binary_message_test", |server| async move {
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
write
.send(Message::Binary(vec![0, 1, 2, 3, 4].into()))
.await?;
tokio::time::sleep(Duration::from_millis(100)).await;
let ping = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {}
}
});
write
.send(Message::Text(serde_json::to_string(&ping)?.into()))
.await?;
let response = receive_ws_message(&mut read, Duration::from_secs(2)).await?;
let parsed: serde_json::Value = serde_json::from_str(&response)?;
assert!(parsed.get("result").is_some());
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_http_large_json_no_panic() {
with_mcp_test_server("large_json_test", |server| async move {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()?;
let init = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {}
}
});
let response = client.post(&server.http_url()).json(&init).send().await?;
assert_eq!(response.status(), 200);
let large_message = "x".repeat(100_000); let tool_call = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "echo",
"arguments": {
"message": large_message
}
}
});
let response = client
.post(&server.http_url())
.json(&tool_call)
.send()
.await?;
assert_eq!(response.status(), 200);
let body: serde_json::Value = response.json().await?;
assert!(body.get("result").is_some() || body.get("error").is_some());
Ok(())
})
.await
.unwrap();
}