use std::sync::Arc;
use tokio::sync::Mutex;
use serde_json::json;
use agentic_evolve_mcp::session::SessionManager;
use agentic_evolve_mcp::tools::ToolRegistry;
use agentic_evolve_mcp::types::error::mcp_error_codes;
fn make_session() -> (tempfile::TempDir, Arc<Mutex<SessionManager>>) {
let dir = tempfile::tempdir().unwrap();
let session = SessionManager::new(dir.path().to_str().unwrap()).unwrap();
(dir, Arc::new(Mutex::new(session)))
}
#[test]
fn list_tools_returns_14() {
let tools = ToolRegistry::list_tools();
assert_eq!(tools.len(), 14, "Expected 14 tools, got {}", tools.len());
}
#[test]
fn tool_names_are_correct() {
let tools = ToolRegistry::list_tools();
let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
let expected = vec![
"evolve_pattern_store",
"evolve_pattern_get",
"evolve_pattern_search",
"evolve_pattern_list",
"evolve_pattern_delete",
"evolve_match_signature",
"evolve_match_context",
"evolve_crystallize",
"evolve_get_body",
"evolve_compose",
"evolve_coverage",
"evolve_confidence",
"evolve_update_usage",
"evolve_optimize",
];
for name in &expected {
assert!(names.contains(name), "Missing tool: {name}");
}
}
#[test]
fn tool_descriptions_start_with_verb() {
let tools = ToolRegistry::list_tools();
for tool in &tools {
if let Some(desc) = &tool.description {
let first_char = desc.chars().next().unwrap_or(' ');
assert!(
first_char.is_uppercase(),
"Tool {} description should start with uppercase verb: '{}'",
tool.name,
desc
);
}
}
}
#[test]
fn tool_descriptions_no_trailing_period() {
let tools = ToolRegistry::list_tools();
for tool in &tools {
if let Some(desc) = &tool.description {
assert!(
!desc.ends_with('.'),
"Tool {} description should not end with period: '{}'",
tool.name,
desc
);
}
}
}
#[test]
fn tool_definitions_have_input_schema() {
let tools = ToolRegistry::list_tools();
for tool in &tools {
assert!(
tool.input_schema.is_object(),
"Tool {} should have object input_schema",
tool.name
);
assert!(
tool.input_schema.get("type").is_some(),
"Tool {} input_schema should have 'type' field",
tool.name
);
}
}
#[test]
fn all_tools_have_descriptions() {
let tools = ToolRegistry::list_tools();
for tool in &tools {
assert!(
tool.description.is_some(),
"Tool {} should have a description",
tool.name
);
}
}
#[tokio::test]
async fn call_unknown_tool_returns_tool_not_found() {
let (_dir, session) = make_session();
let result = ToolRegistry::call("nonexistent_tool", None, &session).await;
let err = result.unwrap_err();
assert_eq!(err.code(), mcp_error_codes::TOOL_NOT_FOUND);
assert_eq!(err.code(), -32803);
}
#[tokio::test]
async fn call_pattern_store_valid() {
let (_dir, session) = make_session();
let args = json!({
"name": "test_pattern",
"domain": "test",
"language": "rust",
"template": "fn test() {}"
});
let result = ToolRegistry::call("evolve_pattern_store", Some(args), &session).await;
assert!(
result.is_ok(),
"evolve_pattern_store should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_pattern_list_valid() {
let (_dir, session) = make_session();
let result = ToolRegistry::call("evolve_pattern_list", Some(json!({})), &session).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn call_pattern_search_valid() {
let (_dir, session) = make_session();
let args = json!({"query": "test"});
let result = ToolRegistry::call("evolve_pattern_search", Some(args), &session).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn call_pattern_get_not_found() {
let (_dir, session) = make_session();
let args = json!({"pattern_id": "nonexistent-id"});
let result = ToolRegistry::call("evolve_pattern_get", Some(args), &session).await;
assert!(result.is_ok() || !result.as_ref().unwrap_err().is_protocol_error());
}
#[tokio::test]
async fn call_pattern_delete_not_found() {
let (_dir, session) = make_session();
let args = json!({"pattern_id": "nonexistent-id"});
let result = ToolRegistry::call("evolve_pattern_delete", Some(args), &session).await;
assert!(result.is_ok() || !result.as_ref().unwrap_err().is_protocol_error());
}
#[tokio::test]
async fn call_match_signature_valid() {
let (_dir, session) = make_session();
let args = json!({
"name": "test_fn",
"language": "rust"
});
let result = ToolRegistry::call("evolve_match_signature", Some(args), &session).await;
assert!(
result.is_ok(),
"evolve_match_signature should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_match_context_valid() {
let (_dir, session) = make_session();
let args = json!({
"name": "handler",
"language": "rust",
"domain": "web"
});
let result = ToolRegistry::call("evolve_match_context", Some(args), &session).await;
assert!(
result.is_ok(),
"evolve_match_context should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_crystallize_valid() {
let (_dir, session) = make_session();
let args = json!({
"code": "pub fn add(a: i32, b: i32) -> i32 {\n a + b\n}",
"language": "rust",
"domain": "math",
"test_results": [{"name": "test_add", "passed": true, "duration_ms": 5}],
"execution_time_ms": 10
});
let result = ToolRegistry::call("evolve_crystallize", Some(args), &session).await;
assert!(
result.is_ok(),
"crystallize should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_get_body_valid() {
let (_dir, session) = make_session();
let args = json!({
"name": "test",
"language": "rust"
});
let result = ToolRegistry::call("evolve_get_body", Some(args), &session).await;
assert!(
result.is_ok(),
"evolve_get_body should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_optimize_valid() {
let (_dir, session) = make_session();
let result = ToolRegistry::call("evolve_optimize", Some(json!({})), &session).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn call_coverage_valid() {
let (_dir, session) = make_session();
let args = json!({
"signatures": [
{"name": "handler", "language": "rust"}
]
});
let result = ToolRegistry::call("evolve_coverage", Some(args), &session).await;
assert!(
result.is_ok(),
"evolve_coverage should succeed: {:?}",
result.err()
);
}
#[tokio::test]
async fn call_pattern_store_missing_required() {
let (_dir, session) = make_session();
let args = json!({"name": "test"}); let result = ToolRegistry::call("evolve_pattern_store", Some(args), &session).await;
assert!(result.is_err(), "Missing required params should fail");
}
#[tokio::test]
async fn call_pattern_store_invalid_confidence() {
let (_dir, session) = make_session();
let args = json!({
"name": "test",
"domain": "test",
"language": "rust",
"template": "fn test() {}",
"confidence": 2.0
});
let result = ToolRegistry::call("evolve_pattern_store", Some(args), &session).await;
assert!(result.is_err(), "Confidence > 1.0 should fail");
}
#[tokio::test]
async fn store_and_get_pattern_roundtrip() {
let (_dir, session) = make_session();
let store_args = json!({
"name": "roundtrip_test",
"domain": "test",
"language": "rust",
"template": "fn roundtrip() { 42 }",
"tags": ["test"]
});
let store_result = ToolRegistry::call("evolve_pattern_store", Some(store_args), &session)
.await
.unwrap();
let content_text = match &store_result.content[0] {
agentic_evolve_mcp::types::response::ToolContent::Text { text } => text.clone(),
_ => panic!("Expected text content"),
};
let stored: serde_json::Value = serde_json::from_str(&content_text).unwrap();
let pattern_id = stored["pattern_id"].as_str().unwrap();
let get_args = json!({"pattern_id": pattern_id});
let get_result = ToolRegistry::call("evolve_pattern_get", Some(get_args), &session)
.await
.unwrap();
assert!(get_result.is_error.is_none());
}
#[tokio::test]
async fn store_and_delete_pattern() {
let (_dir, session) = make_session();
let store_args = json!({
"name": "deletable",
"domain": "test",
"language": "rust",
"template": "fn delete_me() {}"
});
let store_result = ToolRegistry::call("evolve_pattern_store", Some(store_args), &session)
.await
.unwrap();
let content_text = match &store_result.content[0] {
agentic_evolve_mcp::types::response::ToolContent::Text { text } => text.clone(),
_ => panic!("Expected text content"),
};
let stored: serde_json::Value = serde_json::from_str(&content_text).unwrap();
let pattern_id = stored["pattern_id"].as_str().unwrap();
let delete_args = json!({"pattern_id": pattern_id});
let delete_result = ToolRegistry::call("evolve_pattern_delete", Some(delete_args), &session)
.await
.unwrap();
assert!(delete_result.is_error.is_none());
}