mod mcp_test_helpers;
use futures_util::{SinkExt, StreamExt};
use mcp_test_helpers::{
init_test_tracing, receive_ws_message, with_mcp_connection, with_mcp_test_server,
};
use serde_json::{json, Value};
use std::time::Duration;
use tokio_tungstenite::tungstenite::Message;
use tracing::{debug, error, info, warn};
#[tokio::test]
async fn test_mcp_protocol_initialize() {
init_test_tracing();
info!("🤝 Testing MCP protocol initialization");
with_mcp_test_server("protocol_initialize_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_message = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "protocol-test", "version": "1.0.0"}
}
});
debug!(
"📤 Sending initialize message: {}",
serde_json::to_string(&init_message)?
);
write
.send(Message::Text(serde_json::to_string(&init_message)?.into()))
.await?;
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
debug!("📥 Received response: {}", response_text);
let response: Value = serde_json::from_str(&response_text.to_string())?;
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 1);
if response.get("error").is_some() {
error!("❌ Initialize failed: {}", response["error"]);
return Err(format!("Initialize failed: {}", response["error"]).into());
}
if let Some(result) = response.get("result") {
assert!(result.get("protocolVersion").is_some());
assert!(result.get("capabilities").is_some());
info!("✅ Protocol initialization successful");
} else {
error!("❌ No result in initialize response");
return Err("No result in initialize response".into());
}
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_mcp_protocol_version() {
init_test_tracing();
info!("📋 Testing MCP protocol version compatibility");
with_mcp_test_server("protocol_version_test", |server| async move {
let test_versions = vec![
"2025-06-18", "2024-10-01", "2025-01-01", ];
for version in test_versions {
debug!("Testing protocol version: {}", version);
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
let init_message = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": version,
"capabilities": {},
"clientInfo": {"name": "version-test", "version": "1.0.0"}
}
});
write
.send(Message::Text(serde_json::to_string(&init_message)?.into()))
.await?;
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
let response: Value = serde_json::from_str(&response_text.to_string())?;
if response.get("error").is_some() {
debug!(
"⚠️ Version {} not supported: {}",
version, response["error"]
);
} else {
debug!("✅ Version {} supported", version);
}
}
info!("✅ Protocol version tests completed");
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_mcp_protocol_malformed() {
init_test_tracing();
info!("🚫 Testing MCP protocol with malformed messages");
with_mcp_test_server("protocol_malformed_test", |server| async move {
let malformed_messages = vec![
json!({
"id": 1,
"method": "initialize",
"params": {}
}),
json!({
"jsonrpc": "2.0",
"id": 1,
"params": {}
}),
json!({
"jsonrpc": "1.0",
"id": 1,
"method": "initialize",
"params": {}
}),
serde_json::Value::String("invalid json string".to_string()),
];
for (i, message) in malformed_messages.iter().enumerate() {
debug!("Testing malformed message {}: {:?}", i, message);
let (ws_stream, _) = tokio_tungstenite::connect_async(&server.ws_url()).await?;
let (mut write, mut read) = ws_stream.split();
if let Ok(message_str) = serde_json::to_string(message) {
write.send(Message::Text(message_str.into())).await?;
match receive_ws_message(&mut read, Duration::from_secs(2)).await {
Ok(response_text) => {
let response: Value = serde_json::from_str(&response_text.to_string())?;
if response.get("error").is_some() {
debug!("✅ Correctly received error for malformed message {}", i);
} else {
warn!("⚠️ Expected error but got success for message {}", i);
}
}
Err(_) => {
debug!("✅ Connection properly rejected malformed message {}", i);
}
}
}
}
info!("✅ Malformed message tests completed");
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_mcp_protocol_ordering() {
init_test_tracing();
info!("🔄 Testing MCP protocol message ordering");
with_mcp_connection(
"protocol_ordering_test",
|_server, mut write, mut read| async move {
let requests = vec![
json!({
"jsonrpc": "2.0",
"id": 10,
"method": "tools/list",
"params": {}
}),
json!({
"jsonrpc": "2.0",
"id": 5,
"method": "tools/list",
"params": {}
}),
json!({
"jsonrpc": "2.0",
"id": 15,
"method": "tools/list",
"params": {}
}),
];
for request in &requests {
write
.send(Message::Text(serde_json::to_string(request)?.into()))
.await?;
}
let mut responses = Vec::new();
for _ in 0..requests.len() {
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
let response: Value = serde_json::from_str(&response_text.to_string())?;
responses.push(response);
}
let response_ids: Vec<i64> = responses
.iter()
.map(|r| r["id"].as_i64().unwrap())
.collect();
assert!(response_ids.contains(&10));
assert!(response_ids.contains(&5));
assert!(response_ids.contains(&15));
info!("✅ Protocol message ordering tests completed");
Ok(())
},
)
.await
.unwrap();
}
#[tokio::test]
async fn test_jsonrpc_compliance() {
init_test_tracing();
info!("📝 Testing JSON-RPC 2.0 compliance");
with_mcp_connection(
"jsonrpc_compliance_test",
|_server, mut write, mut read| async move {
let string_id_request = json!({
"jsonrpc": "2.0",
"id": "test-string-id",
"method": "tools/list",
"params": {}
});
write
.send(Message::Text(
serde_json::to_string(&string_id_request)?.into(),
))
.await?;
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
let response: Value = serde_json::from_str(&response_text.to_string())?;
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], "test-string-id");
let null_id_request = json!({
"jsonrpc": "2.0",
"id": null,
"method": "tools/list",
"params": {}
});
write
.send(Message::Text(
serde_json::to_string(&null_id_request)?.into(),
))
.await?;
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
let response: Value = serde_json::from_str(&response_text.to_string())?;
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], serde_json::Value::Null);
let no_params_request = json!({
"jsonrpc": "2.0",
"id": 42,
"method": "tools/list"
});
write
.send(Message::Text(
serde_json::to_string(&no_params_request)?.into(),
))
.await?;
let response_text = receive_ws_message(&mut read, Duration::from_secs(5)).await?;
let response: Value = serde_json::from_str(&response_text.to_string())?;
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 42);
info!("✅ JSON-RPC compliance tests completed");
Ok(())
},
)
.await
.unwrap();
}