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
This commit is contained in:
1 parent
f2582b58be
commit
d71b1e6e35
11 files changed
+327
-79
No files matched your search
@@ -0,0 +1,134 @@
|
||||
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();
|
||||
}
|
||||
Reference in new issue
Block a user