use super::*;
mod tests {
use super::*;
#[test]
fn test_agent_config_default() {
let config = AgentConfig::default();
assert_eq!(config.name, "pmat-agent");
assert_eq!(config.version, "1.0.0");
assert_eq!(config.complexity_threshold, 20);
assert!(!config.watch_patterns.is_empty());
assert!(config.watch_patterns.contains(&"**/*.rs".to_string()));
}
#[test]
fn test_monitored_project_creation() {
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "test_project".to_string(),
watch_patterns: vec!["**/*.rs".to_string()],
complexity_threshold: 20,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
assert_eq!(project.name, "test_project");
assert_eq!(project.complexity_threshold, 20);
}
#[tokio::test]
async fn test_mcp_server_creation() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
assert_eq!(server.config.name, "pmat-agent");
assert!(server.monitored_projects.is_empty());
}
}
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}
mod coverage_tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_agent_config_custom() {
let config = AgentConfig {
name: "custom-agent".to_string(),
version: "2.0.0".to_string(),
complexity_threshold: 15,
watch_patterns: vec!["**/*.go".to_string()],
update_interval: 10,
max_projects: 5,
};
assert_eq!(config.name, "custom-agent");
assert_eq!(config.version, "2.0.0");
assert_eq!(config.complexity_threshold, 15);
assert_eq!(config.watch_patterns.len(), 1);
assert_eq!(config.update_interval, 10);
assert_eq!(config.max_projects, 5);
}
#[test]
fn test_agent_config_default_watch_patterns() {
let config = AgentConfig::default();
assert!(config.watch_patterns.contains(&"**/*.rs".to_string()));
assert!(config.watch_patterns.contains(&"**/*.py".to_string()));
assert!(config.watch_patterns.contains(&"**/*.js".to_string()));
assert!(config.watch_patterns.contains(&"**/*.ts".to_string()));
assert!(config.watch_patterns.contains(&"**/*.java".to_string()));
assert!(config.watch_patterns.contains(&"**/*.go".to_string()));
assert!(config.watch_patterns.contains(&"**/*.cpp".to_string()));
assert!(config.watch_patterns.contains(&"**/*.c".to_string()));
}
#[test]
fn test_agent_config_serialization() {
let config = AgentConfig::default();
let json = serde_json::to_string(&config).unwrap();
let deserialized: AgentConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config.name, deserialized.name);
assert_eq!(config.version, deserialized.version);
assert_eq!(
config.complexity_threshold,
deserialized.complexity_threshold
);
}
#[test]
fn test_monitored_project_with_analysis() {
let analysis = ProjectAnalysisResult {
timestamp: "2024-01-01T00:00:00Z".to_string(),
quality_score: 0.85,
files_analyzed: 10,
functions_analyzed: 50,
avg_complexity: 8.5,
hotspot_functions: 3,
satd_issues: 2,
quality_gate_status: "PASSED".to_string(),
recommendations: vec!["Reduce complexity".to_string()],
};
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "test_project".to_string(),
watch_patterns: vec!["**/*.rs".to_string()],
complexity_threshold: 20,
last_analysis: Some(analysis),
started_at: std::time::SystemTime::now(),
};
assert!(project.last_analysis.is_some());
let analysis = project.last_analysis.unwrap();
assert_eq!(analysis.quality_score, 0.85);
assert_eq!(analysis.files_analyzed, 10);
}
#[test]
fn test_monitored_project_clone() {
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "cloned_project".to_string(),
watch_patterns: vec!["**/*.py".to_string()],
complexity_threshold: 25,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
let cloned = project.clone();
assert_eq!(cloned.name, project.name);
assert_eq!(cloned.complexity_threshold, project.complexity_threshold);
}
#[test]
fn test_project_analysis_result_creation() {
let result = ProjectAnalysisResult {
timestamp: chrono::Utc::now().to_rfc3339(),
quality_score: 0.92,
files_analyzed: 25,
functions_analyzed: 100,
avg_complexity: 6.5,
hotspot_functions: 2,
satd_issues: 1,
quality_gate_status: "PASSED".to_string(),
recommendations: vec![
"Good code quality".to_string(),
"Consider adding more tests".to_string(),
],
};
assert_eq!(result.quality_score, 0.92);
assert_eq!(result.files_analyzed, 25);
assert_eq!(result.recommendations.len(), 2);
}
#[test]
fn test_project_analysis_result_serialization() {
let result = ProjectAnalysisResult {
timestamp: "2024-01-01T00:00:00Z".to_string(),
quality_score: 0.75,
files_analyzed: 15,
functions_analyzed: 60,
avg_complexity: 10.0,
hotspot_functions: 5,
satd_issues: 3,
quality_gate_status: "FAILED".to_string(),
recommendations: vec!["Reduce complexity".to_string()],
};
let json = serde_json::to_string(&result).unwrap();
assert!(json.contains("quality_score"));
assert!(json.contains("0.75"));
let deserialized: ProjectAnalysisResult = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.quality_score, 0.75);
}
#[test]
fn test_quality_monitor_command_start_monitoring() {
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "test".to_string(),
watch_patterns: vec![],
complexity_threshold: 20,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
let command = QualityMonitorCommand::StartMonitoring {
project_path: PathBuf::from("/test/project"),
config: Box::new(project),
};
match command {
QualityMonitorCommand::StartMonitoring {
project_path,
config,
} => {
assert_eq!(project_path, PathBuf::from("/test/project"));
assert_eq!(config.name, "test");
}
_ => panic!("Wrong command type"),
}
}
#[test]
fn test_quality_monitor_command_stop_monitoring() {
let command = QualityMonitorCommand::StopMonitoring {
project_id: "test_project".to_string(),
};
match command {
QualityMonitorCommand::StopMonitoring { project_id } => {
assert_eq!(project_id, "test_project");
}
_ => panic!("Wrong command type"),
}
}
#[test]
fn test_quality_monitor_command_shutdown() {
let command = QualityMonitorCommand::Shutdown;
matches!(command, QualityMonitorCommand::Shutdown);
}
#[tokio::test]
async fn test_mcp_server_new_with_custom_config() {
let config = AgentConfig {
name: "custom-server".to_string(),
version: "3.0.0".to_string(),
complexity_threshold: 30,
watch_patterns: vec!["**/*.rs".to_string()],
update_interval: 15,
max_projects: 20,
};
let server = ClaudeCodeAgentMcpServer::new(config);
assert_eq!(server.config.name, "custom-server");
assert_eq!(server.config.version, "3.0.0");
assert_eq!(server.config.complexity_threshold, 30);
}
#[tokio::test]
async fn test_mcp_server_handle_initialize() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server.handle_initialize(json!({})).await.unwrap();
assert!(result.get("protocolVersion").is_some());
assert!(result.get("capabilities").is_some());
assert!(result.get("serverInfo").is_some());
let server_info = result.get("serverInfo").unwrap();
assert_eq!(server_info["name"], "pmat-agent");
}
#[tokio::test]
async fn test_mcp_server_handle_tools_list() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server.handle_tools_list().await.unwrap();
assert!(result.get("tools").is_some());
let tools = result["tools"].as_array().unwrap();
assert!(!tools.is_empty());
let tool_names: Vec<&str> = tools.iter().filter_map(|t| t["name"].as_str()).collect();
assert!(tool_names.contains(&"start_quality_monitoring"));
assert!(tool_names.contains(&"run_quality_gates"));
assert!(tool_names.contains(&"analyze_complexity"));
assert!(tool_names.contains(&"health_check"));
}
#[tokio::test]
async fn test_mcp_server_handle_health_check() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server.handle_health_check().await.unwrap();
assert_eq!(result["status"], "healthy");
assert!(result.get("timestamp").is_some());
assert!(result.get("version").is_some());
}
#[tokio::test]
async fn test_mcp_server_get_tool_capabilities() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_tool_capabilities();
assert!(capabilities.get("start_quality_monitoring").is_some());
assert!(capabilities.get("stop_quality_monitoring").is_some());
assert!(capabilities.get("get_quality_status").is_some());
assert!(capabilities.get("run_quality_gates").is_some());
assert!(capabilities.get("analyze_complexity").is_some());
assert!(capabilities.get("health_check").is_some());
}
#[tokio::test]
async fn test_mcp_server_get_resource_capabilities() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_resource_capabilities();
assert!(capabilities.get("quality-metrics").is_some());
assert!(capabilities.get("complexity-heatmap").is_some());
assert!(capabilities.get("refactor-suggestions").is_some());
assert!(capabilities.get("quality-reports").is_some());
}
#[tokio::test]
async fn test_mcp_server_get_prompt_capabilities() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_prompt_capabilities();
assert!(capabilities.get("quality-summary").is_some());
assert!(capabilities.get("refactoring-guide").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_start_monitoring() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"project_path": path_str,
"watch_patterns": ["**/*.rs", "**/*.py"],
"complexity_threshold": 25
});
let result = server.handle_start_monitoring(¶ms).await.unwrap();
assert!(result.get("text").is_some());
let text = result["text"].as_str().unwrap();
assert!(text.contains("Started quality monitoring"));
}
#[tokio::test]
async fn test_mcp_server_handle_start_monitoring_missing_path() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({});
let result = server.handle_start_monitoring(¶ms).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("project_path"));
}
#[tokio::test]
async fn test_mcp_server_handle_start_monitoring_nonexistent_path() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"project_path": "/nonexistent/path/that/does/not/exist"
});
let result = server.handle_start_monitoring(¶ms).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("does not exist"));
}
#[tokio::test]
async fn test_mcp_server_handle_stop_monitoring() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"project_id": "test_project"
});
let result = server.handle_stop_monitoring(¶ms).await.unwrap();
assert!(result.get("text").is_some());
let text = result["text"].as_str().unwrap();
assert!(text.contains("Stopped quality monitoring"));
assert!(text.contains("test_project"));
}
#[tokio::test]
async fn test_mcp_server_handle_stop_monitoring_missing_id() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({});
let result = server.handle_stop_monitoring(¶ms).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("project_id"));
}
#[tokio::test]
async fn test_mcp_server_handle_get_status_not_monitored() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"project_id": "unknown_project"
});
let result = server.handle_get_status(¶ms).await;
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("not being monitored"));
}
#[tokio::test]
async fn test_mcp_server_handle_get_status_missing_id() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({});
let result = server.handle_get_status(¶ms).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("project_id"));
}
#[tokio::test]
async fn test_mcp_server_handle_run_quality_gates() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"target_path": path_str
});
let result = server.handle_run_quality_gates(¶ms).await.unwrap();
assert!(result.get("content").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_analyze_complexity() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"file_path": "/test/file.rs"
});
let result = server.handle_analyze_complexity(¶ms).await.unwrap();
assert!(result.get("content").is_some());
let content = result["content"].as_array().unwrap();
assert!(!content.is_empty());
let text = content[0]["text"].as_str().unwrap();
assert!(text.contains("Complexity Analysis"));
}
#[tokio::test]
async fn test_mcp_server_format_complexity_analysis_results() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server.format_complexity_analysis_results("/test/path");
assert!(result.contains("Complexity Analysis"));
assert!(result.contains("/test/path"));
assert!(result.contains("Files analyzed"));
assert!(result.contains("Toyota Way standards"));
}
#[tokio::test]
async fn test_mcp_server_format_quality_gate_results_passed() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let output = QualityGateOutput {
passed: true,
results: vec![crate::services::quality_gate_service::QualityCheckResult {
check: "complexity".to_string(),
passed: true,
message: "All good".to_string(),
violations: vec![],
}],
summary: crate::services::quality_gate_service::QualitySummary {
total_checks: 1,
passed_checks: 1,
failed_checks: 0,
total_violations: 0,
error_count: 0,
warning_count: 0,
},
};
let result = server.format_quality_gate_results("/test/path", &output);
assert!(result.contains("Quality Gate Results"));
assert!(result.contains("PASSED"));
assert!(result.contains("/test/path"));
}
#[tokio::test]
async fn test_mcp_server_format_quality_gate_results_failed() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let output = QualityGateOutput {
passed: false,
results: vec![crate::services::quality_gate_service::QualityCheckResult {
check: "complexity".to_string(),
passed: false,
message: "Complexity too high".to_string(),
violations: vec![],
}],
summary: crate::services::quality_gate_service::QualitySummary {
total_checks: 1,
passed_checks: 0,
failed_checks: 1,
total_violations: 0,
error_count: 0,
warning_count: 0,
},
};
let result = server.format_quality_gate_results("/test/path", &output);
assert!(result.contains("Quality Gate Results"));
assert!(result.contains("FAILED"));
assert!(result.contains("Failed Checks"));
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_health_check() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"name": "health_check",
"arguments": {}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("status").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_unknown() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"name": "unknown_tool",
"arguments": {}
});
let result = server.handle_tool_call(params).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Unknown tool"));
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_missing_name() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"arguments": {}
});
let result = server.handle_tool_call(params).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("tool name"));
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_initialize() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 1);
assert!(response.get("result").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_tools_list() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert!(response["result"]["tools"].is_array());
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_health_check() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 3,
"method": "health_check",
"params": {}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert_eq!(response["result"]["status"], "healthy");
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_unknown_method() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 4,
"method": "unknown_method",
"params": {}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert!(response.get("error").is_some());
assert_eq!(response["error"]["code"], -32601);
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_notification() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"method": "health_check",
"params": {}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_none());
}
#[tokio::test]
async fn test_mcp_server_clone() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let cloned = server.clone();
assert_eq!(cloned.config.name, server.config.name);
assert_eq!(cloned.config.version, server.config.version);
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_start_quality_monitoring() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"name": "start_quality_monitoring",
"arguments": {
"project_path": path_str
}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("text").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_stop_quality_monitoring() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"name": "stop_quality_monitoring",
"arguments": {
"project_id": "test_project"
}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("text").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_get_monitoring_status() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let start_params = json!({
"project_path": path_str
});
let _ = server.handle_start_monitoring(&start_params).await.unwrap();
let project_name = temp_dir
.path()
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
let params = json!({
"name": "get_monitoring_status",
"arguments": {
"project_id": project_name
}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("text").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_run_quality_gates() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"name": "run_quality_gates",
"arguments": {
"target_path": path_str
}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("content").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_tool_call_analyze_complexity() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"name": "analyze_complexity",
"arguments": {
"file_path": "/some/path"
}
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("content").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_invalid_json() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let result = server.handle_mcp_request("not valid json").await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_missing_method() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 1,
"params": {}
});
let result = server.handle_mcp_request(&request.to_string()).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("method"));
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_tools_call() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "health_check",
"arguments": {}
}
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 5);
assert!(response.get("result").is_some());
}
#[tokio::test]
async fn test_mcp_server_handle_start_monitoring_default_watch_patterns() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config.clone());
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"project_path": path_str
});
let result = server.handle_start_monitoring(¶ms).await.unwrap();
let text = result["text"].as_str().unwrap();
assert!(text.contains("Started quality monitoring"));
}
#[tokio::test]
async fn test_mcp_server_handle_get_status_with_last_analysis() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let analysis = ProjectAnalysisResult {
timestamp: "2024-01-01T00:00:00Z".to_string(),
quality_score: 0.95,
files_analyzed: 20,
functions_analyzed: 80,
avg_complexity: 7.0,
hotspot_functions: 1,
satd_issues: 0,
quality_gate_status: "PASSED".to_string(),
recommendations: vec!["Keep up the good work".to_string()],
};
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "test_with_analysis".to_string(),
watch_patterns: vec!["**/*.rs".to_string()],
complexity_threshold: 20,
last_analysis: Some(analysis),
started_at: std::time::SystemTime::now(),
};
server
.monitored_projects
.insert("test_with_analysis".to_string(), project);
let params = json!({
"project_id": "test_with_analysis"
});
let result = server.handle_get_status(¶ms).await.unwrap();
let text = result["text"].as_str().unwrap();
assert!(text.contains("Quality Status"));
assert!(text.contains("quality_score"));
}
#[tokio::test]
async fn test_mcp_server_handle_get_status_without_last_analysis() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "test_no_analysis".to_string(),
watch_patterns: vec!["**/*.rs".to_string()],
complexity_threshold: 20,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
server
.monitored_projects
.insert("test_no_analysis".to_string(), project);
let params = json!({
"project_id": "test_no_analysis"
});
let result = server.handle_get_status(¶ms).await.unwrap();
let text = result["text"].as_str().unwrap();
assert!(text.contains("Quality Status"));
assert!(text.contains("PENDING"));
}
#[tokio::test]
async fn test_format_failed_checks_with_multiple_failures() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let output = QualityGateOutput {
passed: false,
results: vec![
crate::services::quality_gate_service::QualityCheckResult {
check: "complexity".to_string(),
passed: false,
message: "High complexity detected".to_string(),
violations: vec![],
},
crate::services::quality_gate_service::QualityCheckResult {
check: "satd".to_string(),
passed: false,
message: "Too many TODOs".to_string(),
violations: vec![],
},
crate::services::quality_gate_service::QualityCheckResult {
check: "lint".to_string(),
passed: true,
message: "No lint issues".to_string(),
violations: vec![],
},
],
summary: crate::services::quality_gate_service::QualitySummary {
total_checks: 3,
passed_checks: 1,
failed_checks: 2,
total_violations: 0,
error_count: 0,
warning_count: 0,
},
};
let mut result_text = String::new();
server.format_failed_checks(&mut result_text, &output);
assert!(result_text.contains("Failed Checks: 2/3"));
assert!(result_text.contains("complexity"));
assert!(result_text.contains("satd"));
}
#[tokio::test]
async fn test_format_failed_checks_with_no_failures() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let output = QualityGateOutput {
passed: true,
results: vec![crate::services::quality_gate_service::QualityCheckResult {
check: "complexity".to_string(),
passed: true,
message: "All good".to_string(),
violations: vec![],
}],
summary: crate::services::quality_gate_service::QualitySummary {
total_checks: 1,
passed_checks: 1,
failed_checks: 0,
total_violations: 0,
error_count: 0,
warning_count: 0,
},
};
let mut result_text = String::new();
server.format_failed_checks(&mut result_text, &output);
assert!(!result_text.contains("Failed Checks"));
}
#[tokio::test]
async fn test_format_quality_summary() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let output = QualityGateOutput {
passed: true,
results: vec![],
summary: crate::services::quality_gate_service::QualitySummary {
total_checks: 5,
passed_checks: 4,
failed_checks: 1,
total_violations: 3,
error_count: 1,
warning_count: 2,
},
};
let mut result_text = String::new();
server.format_quality_summary(&mut result_text, &output);
assert!(result_text.contains("Summary"));
assert!(result_text.contains("Total Checks: 5"));
assert!(result_text.contains("Passed: 4"));
assert!(result_text.contains("Failed: 1"));
}
#[tokio::test]
async fn test_handle_run_quality_gates_rejects_missing_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({});
let result = server.handle_run_quality_gates(¶ms).await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.starts_with("INVALID_PARAMS: "),
"error must be tagged INVALID_PARAMS for -32602 mapping, got: {err}"
);
assert!(err.contains("target_path"));
}
#[tokio::test]
async fn test_handle_run_quality_gates_rejects_null_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server
.handle_run_quality_gates(&json!({ "target_path": null }))
.await;
assert!(result.is_err(), "null target_path must be rejected");
assert!(result
.unwrap_err()
.to_string()
.starts_with("INVALID_PARAMS: "));
}
#[tokio::test]
async fn test_handle_run_quality_gates_rejects_empty_string_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
for empty in ["", " ", "\t\n"] {
let result = server
.handle_run_quality_gates(&json!({ "target_path": empty }))
.await;
assert!(
result.is_err(),
"empty/whitespace target_path ({empty:?}) must be rejected"
);
assert!(result
.unwrap_err()
.to_string()
.starts_with("INVALID_PARAMS: "));
}
}
#[tokio::test]
async fn test_handle_analyze_complexity_rejects_missing_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({});
let result = server.handle_analyze_complexity(¶ms).await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.starts_with("INVALID_PARAMS: "),
"error must be tagged INVALID_PARAMS for -32602 mapping, got: {err}"
);
assert!(err.contains("file_path"));
}
#[tokio::test]
async fn test_handle_analyze_complexity_rejects_null_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let result = server
.handle_analyze_complexity(&json!({ "file_path": null }))
.await;
assert!(result.is_err(), "null file_path must be rejected");
assert!(result
.unwrap_err()
.to_string()
.starts_with("INVALID_PARAMS: "));
}
#[tokio::test]
async fn test_handle_analyze_complexity_rejects_empty_string_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
for empty in ["", " ", "\t\n"] {
let result = server
.handle_analyze_complexity(&json!({ "file_path": empty }))
.await;
assert!(
result.is_err(),
"empty/whitespace file_path ({empty:?}) must be rejected"
);
assert!(result
.unwrap_err()
.to_string()
.starts_with("INVALID_PARAMS: "));
}
}
#[tokio::test]
async fn test_handle_analyze_complexity_rejects_non_string_path() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
for wrong_type in [json!({ "file_path": 42 }), json!({ "file_path": ["a"] })] {
let result = server.handle_analyze_complexity(&wrong_type).await;
assert!(result.is_err(), "non-string file_path must be rejected");
assert!(result
.unwrap_err()
.to_string()
.starts_with("INVALID_PARAMS: "));
}
}
#[test]
fn test_quality_monitor_command_get_status() {
let (tx, _rx) = oneshot::channel();
let command = QualityMonitorCommand::GetStatus {
project_id: "test_project".to_string(),
response_tx: Box::new(tx),
};
match command {
QualityMonitorCommand::GetStatus {
project_id,
response_tx: _,
} => {
assert_eq!(project_id, "test_project");
}
_ => panic!("Wrong command type"),
}
}
#[test]
fn test_project_analysis_result_clone() {
let result = ProjectAnalysisResult {
timestamp: "2024-01-01T00:00:00Z".to_string(),
quality_score: 0.90,
files_analyzed: 10,
functions_analyzed: 50,
avg_complexity: 8.0,
hotspot_functions: 2,
satd_issues: 1,
quality_gate_status: "PASSED".to_string(),
recommendations: vec!["Test recommendation".to_string()],
};
let cloned = result.clone();
assert_eq!(cloned.quality_score, result.quality_score);
assert_eq!(cloned.files_analyzed, result.files_analyzed);
assert_eq!(cloned.recommendations.len(), result.recommendations.len());
}
#[test]
fn test_agent_config_debug() {
let config = AgentConfig::default();
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("pmat-agent"));
assert!(debug_str.contains("1.0.0"));
}
#[test]
fn test_monitored_project_debug() {
let project = MonitoredProject {
path: PathBuf::from("/test/project"),
name: "debug_test".to_string(),
watch_patterns: vec!["**/*.rs".to_string()],
complexity_threshold: 20,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
let debug_str = format!("{:?}", project);
assert!(debug_str.contains("debug_test"));
}
#[test]
fn test_project_analysis_result_debug() {
let result = ProjectAnalysisResult {
timestamp: "2024-01-01T00:00:00Z".to_string(),
quality_score: 0.85,
files_analyzed: 10,
functions_analyzed: 50,
avg_complexity: 8.5,
hotspot_functions: 3,
satd_issues: 2,
quality_gate_status: "PASSED".to_string(),
recommendations: vec![],
};
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("quality_score"));
}
#[test]
fn test_quality_monitor_command_debug() {
let command = QualityMonitorCommand::Shutdown;
let debug_str = format!("{:?}", command);
assert!(debug_str.contains("Shutdown"));
}
#[tokio::test]
async fn test_mcp_server_handle_mcp_request_with_null_params() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let request = json!({
"jsonrpc": "2.0",
"id": 6,
"method": "health_check"
});
let result = server
.handle_mcp_request(&request.to_string())
.await
.unwrap();
assert!(result.is_some());
let response = result.unwrap();
assert_eq!(response["result"]["status"], "healthy");
}
#[tokio::test]
async fn test_mcp_server_handle_initialize_with_params() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "test-client",
"version": "1.0.0"
}
});
let result = server.handle_initialize(params).await.unwrap();
assert!(result.get("protocolVersion").is_some());
assert_eq!(result["protocolVersion"], "2024-11-05");
}
#[tokio::test]
async fn test_mcp_server_start_and_stop_monitoring_sequence() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let project_name = temp_dir
.path()
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
let start_params = json!({
"project_path": path_str,
"complexity_threshold": 15
});
let start_result = server.handle_start_monitoring(&start_params).await.unwrap();
assert!(start_result["text"].as_str().unwrap().contains("Started"));
assert!(server.monitored_projects.contains_key(&project_name));
let stop_params = json!({
"project_id": project_name
});
let stop_result = server.handle_stop_monitoring(&stop_params).await.unwrap();
assert!(stop_result["text"].as_str().unwrap().contains("Stopped"));
assert!(!server.monitored_projects.contains_key(&project_name));
}
#[test]
fn test_agent_config_hpp_pattern() {
let config = AgentConfig::default();
assert!(config.watch_patterns.contains(&"**/*.hpp".to_string()));
assert!(config.watch_patterns.contains(&"**/*.h".to_string()));
}
#[tokio::test]
async fn test_mcp_server_quality_gate_service_is_initialized() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let temp_dir = TempDir::new().unwrap();
let path_str = temp_dir.path().to_string_lossy().to_string();
let params = json!({
"target_path": path_str
});
let result = server.handle_run_quality_gates(¶ms).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_tool_capabilities_input_schema_structure() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_tool_capabilities();
let start_monitoring = capabilities.get("start_quality_monitoring").unwrap();
let input_schema = start_monitoring.get("inputSchema").unwrap();
assert_eq!(input_schema["type"], "object");
assert!(input_schema.get("properties").is_some());
assert!(input_schema.get("required").is_some());
let quality_gates = capabilities.get("run_quality_gates").unwrap();
let qg_schema = quality_gates.get("inputSchema").unwrap();
assert!(qg_schema["properties"].get("target_path").is_some());
assert!(qg_schema["properties"].get("output_format").is_some());
}
#[tokio::test]
async fn test_resource_capabilities_mime_types() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_resource_capabilities();
assert_eq!(
capabilities["quality-metrics"]["mimeType"],
"application/json"
);
assert_eq!(
capabilities["complexity-heatmap"]["mimeType"],
"application/json"
);
}
#[tokio::test]
async fn test_prompt_capabilities_arguments() {
let config = AgentConfig::default();
let server = ClaudeCodeAgentMcpServer::new(config);
let capabilities = server.get_prompt_capabilities();
let quality_summary = capabilities.get("quality-summary").unwrap();
assert!(quality_summary.get("description").is_some());
let args = quality_summary.get("arguments").unwrap();
assert!(args.get("project_id").is_some());
let refactoring = capabilities.get("refactoring-guide").unwrap();
let refactor_args = refactoring.get("arguments").unwrap();
assert!(refactor_args.get("file_path").is_some());
assert!(refactor_args.get("complexity_target").is_some());
}
#[test]
fn test_monitored_project_with_multiple_watch_patterns() {
let project = MonitoredProject {
path: PathBuf::from("/test/polyglot"),
name: "polyglot_project".to_string(),
watch_patterns: vec![
"**/*.rs".to_string(),
"**/*.py".to_string(),
"**/*.js".to_string(),
"**/*.ts".to_string(),
],
complexity_threshold: 15,
last_analysis: None,
started_at: std::time::SystemTime::now(),
};
assert_eq!(project.watch_patterns.len(), 4);
assert!(project.watch_patterns.contains(&"**/*.py".to_string()));
}
#[tokio::test]
async fn test_handle_tool_call_with_null_arguments() {
let config = AgentConfig::default();
let mut server = ClaudeCodeAgentMcpServer::new(config);
let params = json!({
"name": "health_check"
});
let result = server.handle_tool_call(params).await.unwrap();
assert!(result.get("status").is_some());
}
#[test]
fn test_project_analysis_result_all_fields() {
let result = ProjectAnalysisResult {
timestamp: "2024-06-15T12:30:45Z".to_string(),
quality_score: 0.88,
files_analyzed: 42,
functions_analyzed: 156,
avg_complexity: 12.5,
hotspot_functions: 7,
satd_issues: 4,
quality_gate_status: "PASSED_WITH_WARNINGS".to_string(),
recommendations: vec![
"Refactor complex functions".to_string(),
"Add missing documentation".to_string(),
"Reduce TODO count".to_string(),
],
};
assert_eq!(result.timestamp, "2024-06-15T12:30:45Z");
assert_eq!(result.quality_score, 0.88);
assert_eq!(result.files_analyzed, 42);
assert_eq!(result.functions_analyzed, 156);
assert_eq!(result.avg_complexity, 12.5);
assert_eq!(result.hotspot_functions, 7);
assert_eq!(result.satd_issues, 4);
assert_eq!(result.quality_gate_status, "PASSED_WITH_WARNINGS");
assert_eq!(result.recommendations.len(), 3);
}
}