168 lines
6.0 KiB
Rust
168 lines
6.0 KiB
Rust
use std::collections::HashSet;
|
|
|
|
#[test]
|
|
fn test_eager_tools_parity() {
|
|
// 1. Read handlers_v2/*.rs to get memory tools
|
|
let mut memory_tools = HashSet::new();
|
|
let v2_handlers_dir = std::fs::read_dir("src/handlers").expect("Failed to read handlers dir");
|
|
for entry in v2_handlers_dir {
|
|
let entry = entry.unwrap();
|
|
if entry.path().extension().unwrap_or_default() == "rs" {
|
|
let memory_source = std::fs::read_to_string(entry.path()).unwrap();
|
|
let parts: Vec<&str> = memory_source.split("crate::mcp::tool_def").collect();
|
|
for part in parts.iter().skip(1) {
|
|
if let Some(start) = part.find('"') {
|
|
let rest = &part[start + 1..];
|
|
if let Some(end) = rest.find('"') {
|
|
memory_tools.insert(rest[..end].to_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
!memory_tools.is_empty(),
|
|
"Could not find memory tools in handlers_v2 directory"
|
|
);
|
|
|
|
// 2. Read nvim-core/src/lib.rs to get nvim tools
|
|
let nvim_source =
|
|
std::fs::read_to_string("../nvim-core/src/lib.rs").expect("Failed to read nvim lib.rs");
|
|
let mut nvim_tools = HashSet::new();
|
|
for line in nvim_source.lines() {
|
|
if line.contains("\"name\": \"nvim_")
|
|
&& let Some(start) = line.find("\"name\": \"")
|
|
{
|
|
let rest = &line[start + 9..];
|
|
if let Some(end) = rest.find('"') {
|
|
nvim_tools.insert(rest[..end].to_string());
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
!nvim_tools.is_empty(),
|
|
"Could not find nvim tools in lib.rs"
|
|
);
|
|
|
|
// 3. Read Windows mcp_config.json
|
|
let win_home =
|
|
std::env::var("USERPROFILE").unwrap_or_else(|_| "C:\\Users\\reazul.ashraf".to_string());
|
|
let win_config_path = std::path::PathBuf::from(win_home).join(".gemini/config/mcp_config.json");
|
|
if win_config_path.exists() {
|
|
let config_str = std::fs::read_to_string(&win_config_path).unwrap();
|
|
let config: serde_json::Value = serde_json::from_str(&config_str).unwrap();
|
|
|
|
let mem_server = if config["mcpServers"]["mcp-memory"].is_object() {
|
|
&config["mcpServers"]["mcp-memory"]
|
|
} else {
|
|
&config["mcpServers"]["memory"]
|
|
};
|
|
if let Some(eager) = mem_server["eagerTools"].as_array() {
|
|
for tool in eager {
|
|
let name = tool.as_str().unwrap();
|
|
assert!(
|
|
memory_tools.contains(name),
|
|
"Windows config Memory tool '{name}' not implemented in handlers.rs!"
|
|
);
|
|
}
|
|
}
|
|
|
|
if let Some(nvim_eager) = config["mcpServers"]["win-nvim"]["eagerTools"].as_array() {
|
|
for tool in nvim_eager {
|
|
let name = tool.as_str().unwrap();
|
|
assert!(
|
|
nvim_tools.contains(name),
|
|
"Windows config Nvim tool '{name}' not implemented in nvim-core!"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_dashboard_route_parity() {
|
|
let dashboard_path = if std::path::Path::new("src/dashboard.ts").exists() {
|
|
"src/dashboard.ts"
|
|
} else {
|
|
"server/src/dashboard.ts"
|
|
};
|
|
|
|
let setup_path = if std::path::Path::new("src/api/setup.rs").exists() {
|
|
"src/api/setup.rs"
|
|
} else {
|
|
"server/src/api/setup.rs"
|
|
};
|
|
|
|
let dashboard_content =
|
|
std::fs::read_to_string(dashboard_path).expect("Failed to read dashboard.ts");
|
|
let setup_content = std::fs::read_to_string(setup_path).expect("Failed to read setup.rs");
|
|
|
|
// 1. Extract routes from setup.rs
|
|
let mut registered_routes = HashSet::new();
|
|
for part in setup_content.split(".route(") {
|
|
let trimmed = part.trim_start();
|
|
if trimmed.starts_with('"') {
|
|
if let Some(end_quote) = trimmed[1..].find('"') {
|
|
let route = &trimmed[1..=end_quote];
|
|
registered_routes.insert(route.to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
assert!(!registered_routes.is_empty(), "No routes found in setup.rs");
|
|
|
|
// 2. Extract /api/ endpoints called in dashboard.ts
|
|
let mut frontend_endpoints = HashSet::new();
|
|
let mut cursor = 0;
|
|
while let Some(idx) = dashboard_content[cursor..].find("/api/") {
|
|
let start = cursor + idx;
|
|
let rest = &dashboard_content[start..];
|
|
let quote_char = dashboard_content[..start].chars().last();
|
|
let mut endpoint = String::new();
|
|
if let Some(q) = quote_char {
|
|
if q == '`' || q == '"' || q == '\'' {
|
|
if let Some(close_idx) = rest.find(q) {
|
|
let full_call = &rest[..close_idx];
|
|
let clean = if let Some(q_idx) = full_call.find('?') {
|
|
&full_call[..q_idx]
|
|
} else {
|
|
full_call
|
|
};
|
|
let mut normalized = clean.to_string();
|
|
while let Some(start_var) = normalized.find("${") {
|
|
if let Some(end_var) = normalized[start_var..].find('}') {
|
|
normalized.replace_range(start_var..=start_var + end_var, "{id}");
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
endpoint = normalized;
|
|
}
|
|
}
|
|
}
|
|
if endpoint.is_empty() {
|
|
let end_idx = rest
|
|
.find(['"', '`', '\'', ' ', '\n', '?'])
|
|
.unwrap_or(rest.len());
|
|
endpoint = rest[..end_idx].to_string();
|
|
}
|
|
|
|
if endpoint.starts_with("/api/") {
|
|
frontend_endpoints.insert(endpoint);
|
|
}
|
|
cursor = start + 5;
|
|
}
|
|
|
|
assert!(
|
|
!frontend_endpoints.is_empty(),
|
|
"No frontend endpoints found in dashboard.ts"
|
|
);
|
|
|
|
for endpoint in &frontend_endpoints {
|
|
assert!(
|
|
registered_routes.contains(endpoint),
|
|
"Frontend endpoint '{endpoint}' is not registered in setup.rs! Parity violation detected."
|
|
);
|
|
}
|
|
}
|