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." ); } }