use pmat::stateless_server::StatelessTemplateServer;
use std::sync::Arc;
use tempfile::TempDir;
#[cfg(test)]
mod integration_coverage_tests {
use super::*;
#[test]
fn test_execution_mode_detection() {
let is_mcp = !std::io::IsTerminal::is_terminal(&std::io::stdin())
&& std::env::args().len() == 1
|| std::env::var("MCP_VERSION").is_ok();
let _ = is_mcp;
}
#[tokio::test]
async fn test_cli_run_generate_command() {
let _server = Arc::new(StatelessTemplateServer::new().unwrap());
let temp_dir = TempDir::new().unwrap();
let _args = [
"paiml-mcp-agent-toolkit",
"generate",
"makefile",
"rust/cli",
"--output",
&temp_dir.path().join("Makefile").to_string_lossy(),
"--project-name",
"test-project",
];
let result = std::panic::catch_unwind(|| {
true
});
assert!(result.is_ok());
}
#[tokio::test]
async fn test_ast_rust_analysis() {
use pmat::services::ast_rust::*;
let temp_dir = TempDir::new().unwrap();
let rust_file = temp_dir.path().join("test.rs");
tokio::fs::write(
&rust_file,
r#"
fn simple_function() {
println!("Hello");
}
struct TestStruct {
field: i32,
}
impl TestStruct {
fn new(field: i32) -> Self {
Self { field }
}
}
trait TestTrait {
fn method(&self);
}
enum TestEnum {
Variant1,
Variant2(i32),
}
"#,
)
.await
.unwrap();
let result = analyze_rust_file_with_complexity(&rust_file).await;
assert!(result.is_ok());
let metrics = result.unwrap();
assert!(!metrics.functions.is_empty());
let context_result = analyze_rust_file(&rust_file).await;
assert!(context_result.is_ok());
let context = context_result.unwrap();
assert_eq!(context.language, "rust");
assert!(!context.items.is_empty());
}
#[test]
fn test_complexity_service() {
use pmat::services::complexity::*;
let default_thresholds = ComplexityThresholds::default();
assert_eq!(default_thresholds.cyclomatic_warn, 10);
assert_eq!(default_thresholds.cyclomatic_error, 20);
let custom_thresholds = ComplexityThresholds {
cyclomatic_warn: 5,
cyclomatic_error: 10,
cognitive_warn: 8,
cognitive_error: 15,
nesting_max: 3,
method_length: 30,
};
assert!(custom_thresholds.cyclomatic_warn < default_thresholds.cyclomatic_warn);
let key1 = compute_complexity_cache_key(std::path::Path::new("test.rs"), b"content");
let key2 = compute_complexity_cache_key(std::path::Path::new("test.rs"), b"content");
assert_eq!(key1, key2);
let key3 = compute_complexity_cache_key(std::path::Path::new("test.rs"), b"different");
assert_ne!(key1, key3);
let file_metrics = vec![FileComplexityMetrics {
path: "test.rs".to_string(),
total_complexity: ComplexityMetrics {
cyclomatic: 5,
cognitive: 7,
nesting_max: 2,
lines: 20,
halstead: None,
},
functions: vec![FunctionComplexity {
name: "test_fn".to_string(),
line_start: 1,
line_end: 10,
metrics: ComplexityMetrics {
cyclomatic: 5,
cognitive: 7,
nesting_max: 2,
lines: 10,
halstead: None,
},
}],
classes: vec![],
}];
let report = aggregate_results(file_metrics);
assert_eq!(report.summary.total_files, 1);
assert_eq!(report.summary.total_functions, 1);
let summary = format_complexity_summary(&report);
assert!(summary.contains("Complexity Analysis Summary"));
let full_report = format_complexity_report(&report);
assert!(full_report.contains("Complexity Analysis Summary"));
let sarif_result = format_as_sarif(&report);
assert!(sarif_result.is_ok());
}
#[test]
fn test_dag_builder() {
use pmat::models::dag::*;
use pmat::services::context::*;
use pmat::services::dag_builder::*;
let file_context = FileContext {
path: "src/main.rs".to_string(),
language: "rust".to_string(),
items: vec![
AstItem::Function {
name: "main".to_string(),
line: 1,
visibility: "pub".to_string(),
is_async: false,
},
AstItem::Struct {
name: "TestStruct".to_string(),
line: 10,
fields_count: 2,
visibility: "pub".to_string(),
derives: vec![],
},
],
complexity_metrics: None,
};
let project_context = ProjectContext {
project_type: "rust".to_string(),
files: vec![file_context],
summary: ProjectSummary {
total_files: 1,
total_functions: 1,
total_structs: 1,
total_enums: 0,
total_traits: 0,
total_impls: 0,
dependencies: vec![],
},
graph: None,
};
let graph = DagBuilder::build_from_project(&project_context);
assert!(!graph.nodes.is_empty());
let mut test_graph = DependencyGraph::new();
test_graph.add_node(NodeInfo {
id: "test".to_string(),
label: "test".to_string(),
node_type: NodeType::Function,
file_path: "test.rs".to_string(),
line_number: 1,
complexity: 1,
metadata: rustc_hash::FxHashMap::default(),
});
test_graph.add_edge(Edge {
from: "test".to_string(),
to: "test2".to_string(),
edge_type: EdgeType::Calls,
weight: 1,
});
let filtered_calls = filter_call_edges(test_graph.clone());
assert_eq!(filtered_calls.edges.len(), 1);
let filtered_imports = filter_import_edges(test_graph.clone());
assert_eq!(filtered_imports.edges.len(), 0);
}
#[tokio::test]
async fn test_mcp_handlers() {
use pmat::handlers::tools::*;
use pmat::models::mcp::*;
use serde_json::json;
let server = Arc::new(StatelessTemplateServer::new().unwrap());
let request = McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "tools/call".to_string(),
params: Some(json!({
"name": "get_server_info",
"arguments": {}
})),
};
let response = handle_tool_call(server.clone(), request).await;
if let Some(result) = response.result {
assert!(result["serverInfo"]["name"]
.as_str()
.unwrap()
.contains("pmat"));
} else {
panic!("Expected success response");
}
let bad_request = McpRequest {
jsonrpc: "2.0".to_string(),
id: json!(1),
method: "tools/call".to_string(),
params: None,
};
let error_response = handle_tool_call(server.clone(), bad_request).await;
if let Some(error) = error_response.error {
assert_eq!(error.code, -32602);
} else {
panic!("Expected error response");
}
use chrono::Utc;
use pmat::models::churn::*;
use std::path::PathBuf;
let analysis = CodeChurnAnalysis {
repository_root: PathBuf::from("/test"),
period_days: 30,
generated_at: Utc::now(),
summary: ChurnSummary {
total_files_changed: 5,
total_commits: 10,
hotspot_files: vec![PathBuf::from("test.rs")],
stable_files: vec![PathBuf::from("README.md")],
author_contributions: std::collections::HashMap::new(),
mean_churn_score: 0.0,
variance_churn_score: 0.0,
stddev_churn_score: 0.0,
},
files: vec![],
};
let summary = format_churn_summary(&analysis);
assert!(summary.contains("Code Churn Analysis"));
let markdown = format_churn_as_markdown(&analysis);
assert!(markdown.contains("# Code Churn Analysis Report"));
let csv = format_churn_as_csv(&analysis);
assert!(csv.contains("file_path,commits,additions,deletions,churn_score"));
}
#[tokio::test]
async fn test_binary_main_logic() {
let server_result = StatelessTemplateServer::new();
assert!(server_result.is_ok());
let server = Arc::new(server_result.unwrap());
assert!(Arc::strong_count(&server) > 0);
std::env::set_var("MCP_VERSION", "1.0.0");
assert!(std::env::var("MCP_VERSION").is_ok());
std::env::remove_var("MCP_VERSION");
use tracing_subscriber::EnvFilter;
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
assert!(!format!("{filter:?}").is_empty());
}
#[tokio::test]
async fn test_cli_functions() {
let temp_dir = TempDir::new().unwrap();
let rust_file = temp_dir.path().join("test.rs");
tokio::fs::write(
&rust_file,
r#"
fn main() {
if true {
for i in 0..10 {
if i % 2 == 0 {
println!("{}", i);
}
}
}
}
fn complex_function(x: i32) -> i32 {
match x {
0 => 0,
1 => 1,
_ => complex_function(x - 1) + complex_function(x - 2),
}
}
"#,
)
.await
.unwrap();
let cargo_toml = temp_dir.path().join("Cargo.toml");
tokio::fs::write(
&cargo_toml,
r#"
[package]
name = "test"
version = "0.1.0"
"#,
)
.await
.unwrap();
assert!(rust_file.exists());
assert!(cargo_toml.exists());
}
#[tokio::test]
async fn test_ast_error_handling() {
use pmat::services::ast_rust::*;
let temp_dir = TempDir::new().unwrap();
let invalid_rust = temp_dir.path().join("invalid.rs");
tokio::fs::write(
&invalid_rust,
r#"
fn invalid_syntax( {
this is not valid rust
}
"#,
)
.await
.unwrap();
let result = analyze_rust_file_with_complexity(&invalid_rust).await;
assert!(result.is_err());
let nonexistent = temp_dir.path().join("nonexistent.rs");
let result2 = analyze_rust_file(&nonexistent).await;
assert!(result2.is_err());
}
}