use debtmap::analyzers::call_graph::debug::{CallGraphDebugger, DebugConfig, DebugFormat};
use debtmap::analyzers::call_graph::validation::CallGraphValidator;
use debtmap::priority::call_graph::{CallGraph, CallType, FunctionCall, FunctionId};
use std::collections::HashSet;
use std::path::PathBuf;
fn create_test_graph() -> CallGraph {
let mut graph = CallGraph::new();
let file = PathBuf::from("test.rs");
let main_fn = FunctionId::new(file.clone(), "main_function".to_string(), 10);
let helper_fn = FunctionId::new(file.clone(), "helper_function".to_string(), 20);
let utility_fn = FunctionId::new(file.clone(), "utility_function".to_string(), 30);
let orphan_fn = FunctionId::new(file.clone(), "orphaned_function".to_string(), 40);
graph.add_function(main_fn.clone(), false, false, 5, 50);
graph.add_function(helper_fn.clone(), false, false, 3, 30);
graph.add_function(utility_fn.clone(), false, false, 2, 20);
graph.add_function(orphan_fn.clone(), false, false, 1, 10);
graph.add_call(FunctionCall {
caller: main_fn.clone(),
callee: helper_fn.clone(),
call_type: CallType::Direct,
});
graph.add_call(FunctionCall {
caller: main_fn.clone(),
callee: utility_fn.clone(),
call_type: CallType::Direct,
});
graph.add_call(FunctionCall {
caller: helper_fn.clone(),
callee: utility_fn.clone(),
call_type: CallType::Direct,
});
graph
}
#[test]
fn test_debugger_creation_and_report_generation() {
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
let mut output = Vec::new();
debugger
.write_report(&mut output)
.expect("Failed to write report");
let report = String::from_utf8_lossy(&output);
assert!(
report.contains("Call Graph Debug Report") || report.contains("Debug Report"),
"Report should contain header"
);
}
#[test]
fn test_debugger_json_format() {
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Json,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
let mut output = Vec::new();
debugger
.write_report(&mut output)
.expect("Failed to write JSON report");
let report = String::from_utf8_lossy(&output);
assert!(report.contains("{"), "JSON report should contain braces");
assert!(
report.contains("statistics") || report.contains("total_attempts"),
"JSON report should contain statistics"
);
}
#[test]
fn test_debugger_with_trace_functions() {
let mut trace_functions = HashSet::new();
trace_functions.insert("main_function".to_string());
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: Some(trace_functions),
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.add_trace_function("main_function".to_string());
assert!(debugger.should_trace("main_function"));
assert!(debugger.should_trace("module::main_function"));
assert!(!debugger.should_trace("other_function"));
debugger.finalize_statistics();
}
#[test]
fn test_validator_on_healthy_graph() {
let graph = create_test_graph();
let report = CallGraphValidator::validate(&graph);
assert!(
report.health_score >= 80,
"Health score should be at least 80, got {}",
report.health_score
);
assert!(
!report.structural_issues.is_empty() || !report.warnings.is_empty(),
"Should detect structural issues or warnings"
);
}
#[test]
fn test_validator_detects_orphaned_nodes() {
let mut graph = CallGraph::new();
let orphan = FunctionId::new(PathBuf::from("test.rs"), "orphan".to_string(), 10);
graph.add_function(orphan, false, false, 1, 10);
let report = CallGraphValidator::validate(&graph);
assert!(
!report.structural_issues.is_empty() || !report.warnings.is_empty(),
"Should detect orphaned node"
);
}
#[test]
fn test_validator_health_score_calculation() {
let graph = create_test_graph();
let report = CallGraphValidator::validate(&graph);
assert!(
report.health_score <= 100,
"Health score should not exceed 100"
);
assert!(report.health_score > 0, "Health score should be positive");
}
#[test]
fn test_validation_report_has_issues_method() {
let graph = create_test_graph();
let report = CallGraphValidator::validate(&graph);
if !report.structural_issues.is_empty() || !report.warnings.is_empty() {
assert!(report.has_issues(), "has_issues() should return true");
}
let empty_graph = CallGraph::new();
let empty_report = CallGraphValidator::validate(&empty_graph);
assert!(
!empty_report.has_issues() || empty_report.health_score == 100,
"Empty graph should have no issues or perfect health score"
);
}
#[test]
fn test_debug_and_validation_integration() {
let graph = create_test_graph();
let validation_report = CallGraphValidator::validate(&graph);
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
let mut output = Vec::new();
debugger.write_report(&mut output).expect("Write failed");
assert!(validation_report.health_score > 0);
assert!(!output.is_empty());
}
#[test]
#[ignore = "Performance timing test - too flaky for CI environments"]
fn test_performance_overhead_is_minimal() {
let graph = create_test_graph();
let start = std::time::Instant::now();
for _ in 0..100 {
CallGraphValidator::validate(&graph);
}
let baseline_duration = start.elapsed();
let start = std::time::Instant::now();
for _ in 0..100 {
CallGraphValidator::validate(&graph);
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
}
let with_debug_duration = start.elapsed();
let overhead_ratio = with_debug_duration.as_secs_f64() / baseline_duration.as_secs_f64();
assert!(
overhead_ratio < 2.0,
"Debug overhead is too high: {:.1}% (expected <100% for tests)",
(overhead_ratio - 1.0) * 100.0
);
}