Files
mcp-memory/stub/tests/e2e.rs
T
Riz Ashraf d71b1e6e35 feat: Replace python tests with native E2E test suite and fix capabilities schema
- Added native E2E test suite in stub to validate server<->stub<->nvim IPC
- Added win-nvim integration tests
- Fixed empty capabilities schema in initialize response in server and win-nvim
- Refactored server to accept dynamic MCP_PORT for test isolation
- Made server exit gracefully on port 3000 collision
2026-09-13 18:03:27 +01:00

135 lines
4.2 KiB
Rust

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<Value> {
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 mut server_exe = std::env::current_exe().unwrap();
server_exe.pop();
server_exe.pop();
server_exe.push("mcp-memory-server.exe");
let mut nvim_exe = server_exe.clone();
nvim_exe.pop();
nvim_exe.push("mcp-memory-win-nvim.exe");
// 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();
}