use serde_json::{json, Value}; use std::io::{BufRead, BufReader, Read, Write}; use std::process::{Command, Stdio}; use std::time::Duration; use std::sync::Arc; use tokio::sync::Mutex; fn send_message(stdin: &mut std::process::ChildStdin, msg: Value) { let s = serde_json::to_string(&msg).unwrap(); let payload = format!("Content-Length: {}\r\n\r\n{}", s.len(), s); stdin.write_all(payload.as_bytes()).unwrap(); stdin.flush().unwrap(); } fn read_message(stdout: &mut std::process::ChildStdout) -> Option { let mut reader = BufReader::new(stdout); let mut length = 0; loop { let mut line = String::new(); if reader.read_line(&mut line).unwrap_or(0) == 0 { return None; } let line = line.trim_end(); if line.is_empty() { break; } if let Some(len_str) = line.strip_prefix("Content-Length: ") { length = len_str.parse().unwrap_or(0); } } if length == 0 { return None; } let mut buf = vec![0u8; length]; reader.read_exact(&mut buf).unwrap(); let body_str = String::from_utf8_lossy(&buf); Some(serde_json::from_str(&body_str).unwrap()) } #[tokio::test] async fn test_full_system_e2e_performance() { let temp_dir = std::env::temp_dir().join(format!("mcp_e2e_{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs())); std::fs::create_dir_all(&temp_dir).unwrap(); let test_port = "3031"; // Use a distinct port let server_exe = env!("CARGO_BIN_EXE_mcp-memory-server"); let nvim_exe = env!("CARGO_BIN_EXE_mcp-memory-win-nvim"); // 1. Start Server let mut server = Command::new(server_exe) .env("MCP_PORT", test_port) .env("MCP_MEMORY_STORE_DIR", temp_dir.to_str().unwrap()) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() .expect("Failed to start server"); tokio::time::sleep(Duration::from_secs(2)).await; // 2. Start Stub let stub_exe = env!("CARGO_BIN_EXE_mcp-memory-stub"); let mut stub = Command::new(stub_exe) .arg("--target") .arg(format!("http://127.0.0.1:{}", test_port)) .env("MCP_MEMORY_STORE_DIR", temp_dir.to_str().unwrap()) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::null()) .spawn() .expect("Failed to start stub"); let mut stub_stdin = stub.stdin.take().unwrap(); let mut stub_stdout = stub.stdout.take().unwrap(); // 3. Start Win-Nvim let mut win_nvim = Command::new(&nvim_exe) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::null()) .spawn() .expect("Failed to start win-nvim"); let mut nvim_stdin = win_nvim.stdin.take().unwrap(); let mut nvim_stdout = win_nvim.stdout.take().unwrap(); // Send 100 concurrent-like sequential rapid requests to Stub let start_time = std::time::Instant::now(); for i in 1..=100 { let tools_req = json!({ "jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": i }); send_message(&mut stub_stdin, tools_req); let resp = read_message(&mut stub_stdout).expect("Failed to read rapid response from stub"); assert_eq!(resp["id"], i); } let stub_duration = start_time.elapsed(); // Send 100 concurrent-like sequential rapid requests to Win-Nvim let start_time_nvim = std::time::Instant::now(); for i in 1..=100 { let tools_req = json!({ "jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": i }); send_message(&mut nvim_stdin, tools_req); let resp = read_message(&mut nvim_stdout).expect("Failed to read rapid response from win-nvim"); assert_eq!(resp["id"], i); } let nvim_duration = start_time_nvim.elapsed(); println!("Stub 100 requests: {:?}", stub_duration); println!("Win-Nvim 100 requests: {:?}", nvim_duration); // Cleanup let _ = stub.kill(); let _ = win_nvim.kill(); let _ = server.kill(); }