use pmat::mcp::tools::semantic_search_tools::*;
use serde_json::json;
use tempfile::TempDir;
async fn setup_engine() -> (
std::sync::Arc<pmat::services::semantic::HybridSearchEngine>,
TempDir,
) {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("mcp_test.db");
let engine = pmat::services::semantic::HybridSearchEngine::new(
db_path.to_str().unwrap(),
temp_dir.path(),
)
.await
.unwrap();
std::fs::create_dir_all(temp_dir.path().join("src")).unwrap();
std::fs::write(
temp_dir.path().join("src/math.rs"),
r#"
/// Add two numbers
fn add(a: i32, b: i32) -> i32 {
a + b
}
"#,
)
.unwrap();
engine.index_directory(temp_dir.path()).await.unwrap();
(std::sync::Arc::new(engine), temp_dir)
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_semantic_search_tool_basic() {
let (engine, _temp_dir) = setup_engine().await;
let tool = SemanticSearchTool::new(engine);
let params = json!({
"query": "add",
"mode": "hybrid",
"limit": 5
});
let result = tool.execute(params).await.unwrap();
assert!(result["results"].is_array());
assert!(result["total"].is_number());
assert!(result["mode"].is_string());
assert!(result["query_time_ms"].is_number());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_semantic_search_result_structure() {
let (engine, _temp_dir) = setup_engine().await;
let tool = SemanticSearchTool::new(engine);
let params = json!({
"query": "function",
"mode": "keyword",
"limit": 1
});
let result = tool.execute(params).await.unwrap();
if let Some(results) = result["results"].as_array() {
if !results.is_empty() {
let first = &results[0];
assert!(first["file_path"].is_string());
assert!(first["chunk_name"].is_string());
assert!(first["chunk_type"].is_string());
assert!(first["language"].is_string());
assert!(first["score"].is_number());
assert!(first["snippet"].is_string());
assert!(first["start_line"].is_number());
assert!(first["end_line"].is_number());
}
}
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_semantic_search_empty_query() {
let (engine, _temp_dir) = setup_engine().await;
let tool = SemanticSearchTool::new(engine);
let params = json!({
"query": "",
"mode": "hybrid"
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_semantic_search_invalid_mode() {
let (engine, _temp_dir) = setup_engine().await;
let tool = SemanticSearchTool::new(engine);
let params = json!({
"query": "test",
"mode": "invalid_mode"
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_semantic_search_with_filters() {
let (engine, _temp_dir) = setup_engine().await;
let tool = SemanticSearchTool::new(engine);
let params = json!({
"query": "function",
"mode": "hybrid",
"language": "rust",
"limit": 10
});
let result = tool.execute(params).await.unwrap();
assert!(result["results"].is_array());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_find_similar_code_tool() {
let (engine, temp_dir) = setup_engine().await;
let tool = FindSimilarCodeTool::new(engine);
let file_path = temp_dir.path().join("src/math.rs");
let params = json!({
"file_path": file_path.to_str().unwrap(),
"limit": 3
});
let result = tool.execute(params).await.unwrap();
assert!(result["results"].is_array());
assert!(result["reference_file"].is_string());
assert!(result["total"].is_number());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_find_similar_result_structure() {
let (engine, temp_dir) = setup_engine().await;
let tool = FindSimilarCodeTool::new(engine);
let file_path = temp_dir.path().join("src/math.rs");
let params = json!({
"file_path": file_path.to_str().unwrap(),
"limit": 1
});
let result = tool.execute(params).await.unwrap();
if let Some(results) = result["results"].as_array() {
if !results.is_empty() {
let first = &results[0];
assert!(first["file_path"].is_string());
assert!(first["chunk_name"].is_string());
assert!(first["similarity"].is_number());
assert!(first["snippet"].is_string());
}
}
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_find_similar_invalid_file() {
let (engine, _temp_dir) = setup_engine().await;
let tool = FindSimilarCodeTool::new(engine);
let params = json!({
"file_path": "/nonexistent/file.rs",
"limit": 3
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_find_similar_missing_params() {
let (engine, _temp_dir) = setup_engine().await;
let tool = FindSimilarCodeTool::new(engine);
let params = json!({
"limit": 3
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_cluster_code_tool() {
let (engine, _temp_dir) = setup_engine().await;
let tool = ClusterCodeTool::new(engine);
let params = json!({
"method": "kmeans",
"k": 3
});
let result = tool.execute(params).await.unwrap();
assert!(result["clusters"].is_array());
assert!(result["method"].is_string());
assert!(result["total_chunks"].is_number());
assert!(result["total_clusters"].is_number());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_cluster_result_structure() {
let (engine, _temp_dir) = setup_engine().await;
let tool = ClusterCodeTool::new(engine);
let params = json!({
"method": "kmeans",
"k": 2
});
let result = tool.execute(params).await.unwrap();
if let Some(clusters) = result["clusters"].as_array() {
if !clusters.is_empty() {
let cluster = &clusters[0];
assert!(cluster["id"].is_number());
assert!(cluster["size"].is_number());
assert!(cluster["chunks"].is_array());
}
}
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_cluster_invalid_method() {
let (engine, _temp_dir) = setup_engine().await;
let tool = ClusterCodeTool::new(engine);
let params = json!({
"method": "invalid_method",
"k": 3
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_cluster_missing_k() {
let (engine, _temp_dir) = setup_engine().await;
let tool = ClusterCodeTool::new(engine);
let params = json!({
"method": "kmeans"
});
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_analyze_topics_tool() {
let (engine, _temp_dir) = setup_engine().await;
let tool = AnalyzeTopicsTool::new(engine);
let params = json!({
"num_topics": 5
});
let result = tool.execute(params).await.unwrap();
assert!(result["topics"].is_array());
assert!(result["num_topics"].is_number());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_analyze_topics_result_structure() {
let (engine, _temp_dir) = setup_engine().await;
let tool = AnalyzeTopicsTool::new(engine);
let params = json!({
"num_topics": 3
});
let result = tool.execute(params).await.unwrap();
if let Some(topics) = result["topics"].as_array() {
if !topics.is_empty() {
let topic = &topics[0];
assert!(topic["id"].is_number());
assert!(topic["keywords"].is_array());
assert!(topic["examples"].is_array());
}
}
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_analyze_topics_invalid_count() {
let (engine, _temp_dir) = setup_engine().await;
let tool = AnalyzeTopicsTool::new(engine);
let params = json!({
"num_topics": 0 });
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[tokio::test]
async fn test_analyze_topics_too_many() {
let (engine, _temp_dir) = setup_engine().await;
let tool = AnalyzeTopicsTool::new(engine);
let params = json!({
"num_topics": 100 });
let result = tool.execute(params).await;
assert!(result.is_err());
}
#[ignore = "requires MCP tools setup"]
#[test]
fn test_semantic_search_schema() {
let schema = SemanticSearchTool::schema();
assert_eq!(schema["name"], "semantic_search");
assert!(schema["description"].is_string());
assert!(schema["parameters"].is_object());
assert!(schema["parameters"]["properties"]["query"].is_object());
assert!(schema["parameters"]["properties"]["mode"].is_object());
assert!(schema["parameters"]["required"].is_array());
}
#[ignore = "requires MCP tools setup"]
#[test]
fn test_find_similar_schema() {
let schema = FindSimilarCodeTool::schema();
assert_eq!(schema["name"], "find_similar_code");
assert!(schema["description"].is_string());
assert!(schema["parameters"]["properties"]["file_path"].is_object());
assert!(schema["parameters"]["required"]
.as_array()
.unwrap()
.contains(&json!("file_path")));
}
#[ignore = "requires MCP tools setup"]
#[test]
fn test_all_tool_schemas() {
let schemas = vec![
SemanticSearchTool::schema(),
FindSimilarCodeTool::schema(),
ClusterCodeTool::schema(),
AnalyzeTopicsTool::schema(),
];
for schema in schemas {
assert!(schema["name"].is_string());
assert!(schema["description"].is_string());
assert!(schema["parameters"].is_object());
assert!(schema["parameters"]["type"] == "object");
assert!(schema["parameters"]["properties"].is_object());
}
}