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,111 @@
|
||||
use serde_json::{json, Value};
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::Duration;
|
||||
|
||||
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;
|
||||
|
||||
// Read headers
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
if reader.read_line(&mut line).unwrap_or(0) == 0 {
|
||||
return None; // EOF
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Read body
|
||||
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())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mcp_initialization_and_tools_list() {
|
||||
// Determine the path to the built binary.
|
||||
// cargo test automatically sets CARGO_BIN_EXE_<name> for binary targets!
|
||||
let exe_path = env!("CARGO_BIN_EXE_mcp-memory-win-nvim");
|
||||
|
||||
let mut child = Command::new(exe_path)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to start mcp-memory-win-nvim");
|
||||
|
||||
let mut stdin = child.stdin.take().expect("Failed to open stdin");
|
||||
let mut stdout = child.stdout.take().expect("Failed to open stdout");
|
||||
|
||||
// 1. Test Initialize
|
||||
let init_req = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {},
|
||||
"clientInfo": {
|
||||
"name": "test-client",
|
||||
"version": "1.0"
|
||||
}
|
||||
},
|
||||
"id": 1
|
||||
});
|
||||
|
||||
send_message(&mut stdin, init_req);
|
||||
|
||||
let init_resp = read_message(&mut stdout).expect("Failed to read initialize response");
|
||||
|
||||
assert_eq!(init_resp["jsonrpc"], "2.0");
|
||||
assert_eq!(init_resp["id"], 1);
|
||||
|
||||
// VERIFY CRITICAL FIX: Ensure capabilities.tools is present!
|
||||
let capabilities = &init_resp["result"]["capabilities"];
|
||||
assert!(capabilities.get("tools").is_some(), "capabilities.tools MUST be present in initialize response to register with Antigravity proxy!");
|
||||
|
||||
// 2. Test tools/list
|
||||
let tools_req = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/list",
|
||||
"params": {},
|
||||
"id": 2
|
||||
});
|
||||
|
||||
send_message(&mut stdin, tools_req);
|
||||
|
||||
let tools_resp = read_message(&mut stdout).expect("Failed to read tools/list response");
|
||||
|
||||
assert_eq!(tools_resp["jsonrpc"], "2.0");
|
||||
assert_eq!(tools_resp["id"], 2);
|
||||
|
||||
let tools = tools_resp["result"]["tools"].as_array().expect("result.tools must be an array");
|
||||
assert!(!tools.is_empty(), "Server must expose at least one tool");
|
||||
|
||||
// Verify a specific tool exists
|
||||
let has_get_active_buffer = tools.iter().any(|t| t["name"] == "nvim_get_active_buffer");
|
||||
assert!(has_get_active_buffer, "Missing nvim_get_active_buffer tool");
|
||||
|
||||
// Kill the child process cleanly
|
||||
child.kill().expect("Failed to kill child");
|
||||
child.wait().expect("Failed to wait on child");
|
||||
}
|
||||
Reference in new issue
Block a user