#[cfg(test)]
mod tests {
use crate::protocol_impl::McpProtocolHandlerImpl;
use serde_json::{json, Value};
#[tokio::test]
async fn test_response_format_compliance() {
let mut handler = McpProtocolHandlerImpl::new();
let request = json!({
"jsonrpc": "2.0",
"id": 12345,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let response = handler.handle_message(request).await.unwrap();
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 12345);
let has_result = response.get("result").is_some();
let has_error = response.get("error").is_some();
assert!(has_result || has_error);
assert!(!(has_result && has_error));
}
#[tokio::test]
async fn test_error_object_compliance() {
let mut handler = McpProtocolHandlerImpl::new();
let invalid_request = json!({
"jsonrpc": "2.0",
"id": "test-error",
"method": "unknown/method",
"params": {}
});
let response = handler.handle_message(invalid_request).await.unwrap();
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], "test-error");
assert!(response.get("error").is_some());
let error = &response["error"];
assert!(error.get("code").is_some());
assert!(error.get("message").is_some());
assert!(error["code"].is_i64());
assert!(error["message"].is_string());
if let Some(data) = error.get("data") {
assert!(data.is_object() || data.is_array() || data.is_string());
}
}
#[tokio::test]
async fn test_standard_error_codes() {
let mut handler = McpProtocolHandlerImpl::new();
let method_not_found = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "completely/unknown/method",
"params": {}
});
let response = handler.handle_message(method_not_found).await.unwrap();
if let Some(error) = response.get("error") {
let code = error["code"].as_i64().unwrap();
assert_eq!(code, -32601);
}
let invalid_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": 123, "params": {}
});
let response = handler.handle_message(invalid_request).await;
if let Ok(response) = response {
if let Some(error) = response.get("error") {
let code = error["code"].as_i64().unwrap();
assert!(code == -32600 || code == -32602); }
}
}
#[tokio::test]
async fn test_notification_compliance() {
let mut handler = McpProtocolHandlerImpl::new();
let notification = json!({
"jsonrpc": "2.0",
"method": "notifications/cancel",
"params": {
"requestId": "some-request"
}
});
let response = handler.handle_message(notification).await.unwrap();
if !response.as_object().unwrap().is_empty() {
assert_eq!(response.get("id"), Some(&Value::Null));
}
}
#[tokio::test]
async fn test_id_preservation() {
let mut handler = McpProtocolHandlerImpl::new();
let test_cases = vec![
(json!(42), json!(42)), (json!("string-id"), json!("string-id")), (json!(null), json!(null)), ];
for (request_id, expected_id) in test_cases {
let request = json!({
"jsonrpc": "2.0",
"id": request_id,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let response = handler.handle_message(request).await.unwrap();
assert_eq!(response["id"], expected_id);
handler = McpProtocolHandlerImpl::new();
}
}
#[tokio::test]
async fn test_parameter_compliance() {
let mut handler = McpProtocolHandlerImpl::new();
let init = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
handler.handle_message(init).await.unwrap();
let with_object_params = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
});
let response = handler.handle_message(with_object_params).await.unwrap();
assert!(response.get("result").is_some() || response.get("error").is_some());
let with_array_params = json!({
"jsonrpc": "2.0",
"id": 3,
"method": "tools/list",
"params": []
});
let response = handler.handle_message(with_array_params).await.unwrap();
assert!(response.get("result").is_some() || response.get("error").is_some());
let without_params = json!({
"jsonrpc": "2.0",
"id": 4,
"method": "tools/list"
});
let response = handler.handle_message(without_params).await.unwrap();
assert!(response.get("result").is_some() || response.get("error").is_some());
}
#[tokio::test]
async fn test_version_field_enforcement() {
let mut handler = McpProtocolHandlerImpl::new();
let without_version = json!({
"id": 1,
"method": "initialize",
"params": {}
});
let result = handler.handle_message(without_version).await;
match result {
Ok(response) => {
assert!(response.get("error").is_some());
}
Err(_) => {
}
}
let wrong_version = json!({
"jsonrpc": "1.0",
"id": 2,
"method": "initialize",
"params": {}
});
let result = handler.handle_message(wrong_version).await;
match result {
Ok(response) => {
assert!(response.get("error").is_some());
}
Err(_) => {
}
}
let invalid_version = json!({
"jsonrpc": 2.0,
"id": 3,
"method": "initialize",
"params": {}
});
let result = handler.handle_message(invalid_version).await;
match result {
Ok(response) => {
assert!(response.get("error").is_some());
}
Err(_) => {
}
}
}
#[tokio::test]
async fn test_extra_fields_ignored() {
let mut handler = McpProtocolHandlerImpl::new();
let request_with_extras = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
},
"extra_field": "should be ignored",
"another_extra": 42,
"nested_extra": {
"foo": "bar"
}
});
let response = handler.handle_message(request_with_extras).await.unwrap();
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 1);
assert!(response.get("result").is_some());
assert!(response.get("extra_field").is_none());
assert!(response.get("another_extra").is_none());
assert!(response.get("nested_extra").is_none());
}
#[tokio::test]
async fn test_batch_request_rejection() {
let mut handler = McpProtocolHandlerImpl::new();
let batch_request = json!([
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
},
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
]);
let result = handler.handle_message(batch_request).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_large_number_handling() {
let mut handler = McpProtocolHandlerImpl::new();
let large_id_request = json!({
"jsonrpc": "2.0",
"id": 9007199254740991i64, "method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let response = handler.handle_message(large_id_request).await.unwrap();
assert_eq!(response["id"], 9007199254740991i64);
let mut handler2 = McpProtocolHandlerImpl::new();
let float_id_request = json!({
"jsonrpc": "2.0",
"id": 123.456,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let response = handler2.handle_message(float_id_request).await.unwrap();
assert_eq!(response["id"], 123.456);
}
#[tokio::test]
async fn test_unicode_handling() {
let mut handler = McpProtocolHandlerImpl::new();
let unicode_request = json!({
"jsonrpc": "2.0",
"id": "测试-тест-🔥",
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"clientInfo": {
"name": "测试客户端",
"version": "1.0.0-α"
}
}
});
let response = handler.handle_message(unicode_request).await.unwrap();
assert_eq!(response["id"], "测试-тест-🔥");
assert!(response.get("result").is_some());
}
#[tokio::test]
async fn test_response_json_validity() {
let mut handler = McpProtocolHandlerImpl::new();
let request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let response = handler.handle_message(request).await.unwrap();
let json_string = serde_json::to_string(&response).unwrap();
let parsed_back: Value = serde_json::from_str(&json_string).unwrap();
assert_eq!(response, parsed_back);
}
#[tokio::test]
async fn test_response_timing() {
let mut handler = McpProtocolHandlerImpl::new();
let start = std::time::Instant::now();
let request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18"
}
});
let _response = handler.handle_message(request).await.unwrap();
let elapsed = start.elapsed();
assert!(elapsed.as_secs() < 1, "Response took too long: {elapsed:?}");
}
}