http_integration.rs•6.61 kB
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use serde_json::{json, Value};
use std::time::Duration;
/// Helper function to start the HTTP MCP server in the background for testing
async fn start_test_server(port: u16) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
// Simulate starting the server on the given port
// In a real scenario, you would import and call your server's start function
let addr = format!("127.0.0.1:{}", port);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
// Accept one connection for testing
let (stream, _) = listener.accept().await.unwrap();
// Simple echo server for now - this will be replaced with actual MCP handling
let (mut reader, mut writer) = tokio::io::split(stream);
let mut buf = vec![0u8; 4096];
loop {
match reader.read(&mut buf).await {
Ok(0) => break,
Ok(n) => {
if let Err(_) = writer.write_all(&buf[..n]).await {
break;
}
}
Err(_) => break,
}
}
})
}
/// Test basic HTTP connection to the server
#[tokio::test]
async fn test_http_connection() {
let port = 9876;
let _server = start_test_server(port).await;
// Give server time to start
tokio::time::sleep(Duration::from_millis(100)).await;
// Connect to the server
let result = TcpStream::connect(format!("127.0.0.1:{}", port)).await;
assert!(result.is_ok(), "Should be able to connect to the test server");
}
/// Test HTTP upgrade request
#[tokio::test]
async fn test_http_upgrade_request() {
let port = 9877;
let _server = start_test_server(port).await;
// Give server time to start
tokio::time::sleep(Duration::from_millis(100)).await;
let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))
.await
.expect("Failed to connect");
// Send HTTP upgrade request
let request = "GET / HTTP/1.1\r\n\
Host: localhost\r\n\
Upgrade: mcp\r\n\
Connection: Upgrade\r\n\
\r\n";
stream.write_all(request.as_bytes()).await.expect("Failed to write request");
// Read response
let mut buf = vec![0u8; 1024];
let n = stream.read(&mut buf).await.expect("Failed to read response");
let response = String::from_utf8_lossy(&buf[..n]);
// Verify we got a response
assert!(!response.is_empty(), "Should receive a response");
}
/// Test MCP initialize message
#[tokio::test]
async fn test_mcp_initialize() {
// Create a test initialize request
let initialize_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "test-client",
"version": "1.0.0"
}
}
});
// Verify the request is properly formatted JSON-RPC
assert_eq!(initialize_request["jsonrpc"], "2.0");
assert_eq!(initialize_request["method"], "initialize");
assert!(initialize_request["params"].is_object());
}
/// Test MCP tool call (run_js)
#[tokio::test]
async fn test_mcp_tool_call_format() {
// Create a test tool call request
let tool_call_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "run_js",
"arguments": {
"code": "1 + 1",
"heap": "test-heap"
}
}
});
// Verify the request is properly formatted
assert_eq!(tool_call_request["jsonrpc"], "2.0");
assert_eq!(tool_call_request["method"], "tools/call");
assert_eq!(tool_call_request["params"]["name"], "run_js");
assert!(tool_call_request["params"]["arguments"].is_object());
}
/// Test JavaScript code execution scenarios
#[tokio::test]
async fn test_javascript_execution_scenarios() {
// Test simple arithmetic
let code1 = "1 + 1";
assert!(!code1.is_empty());
// Test variable assignment with persistence
let code2 = "let x = 5; x";
assert!(code2.contains("let"));
// Test accessing previously set variable
let code3 = "x + 10";
assert!(!code3.is_empty());
// All test code snippets are valid
assert!(true);
}
/// Test heap storage naming
#[tokio::test]
async fn test_heap_naming() {
let heap_names = vec![
"test-heap",
"user-session-123",
"calculation-workspace",
];
// Verify heap names are valid strings
for name in heap_names {
assert!(!name.is_empty());
assert!(!name.contains('/')); // No path separators
}
}
/// Test error handling for invalid JavaScript
#[tokio::test]
async fn test_invalid_javascript_handling() {
// Examples of invalid JavaScript that should be caught
let invalid_codes = vec![
"this is not javascript",
"let x = ;",
"undefined.method()",
];
for code in invalid_codes {
// These should be handled gracefully by the server
let request = json!({
"code": code,
"heap": "test-heap"
});
assert!(request.is_object());
}
}
/// Test concurrent connections
#[tokio::test]
async fn test_concurrent_connections() {
let port = 9878;
// Try to create multiple connection attempts
let tasks: Vec<_> = (0..3).map(|_| {
tokio::spawn(async move {
// Each task attempts to connect
let addr = format!("127.0.0.1:{}", port);
// Connection may fail since server isn't running, but we test the logic
let _ = tokio::time::timeout(
Duration::from_millis(100),
TcpStream::connect(addr)
).await;
})
}).collect();
// Wait for all tasks to complete
for task in tasks {
task.await.expect("Task should complete");
}
}
/// Test JSON-RPC error response format
#[tokio::test]
async fn test_jsonrpc_error_format() {
let error_response = json!({
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid Request"
}
});
assert_eq!(error_response["jsonrpc"], "2.0");
assert!(error_response["error"].is_object());
assert!(error_response["error"]["code"].is_number());
assert!(error_response["error"]["message"].is_string());
}