#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::models::tdg::{TDGComponents, TDGSeverity, TDGScore, TDGSummary, TDGHotspot};
use tempfile::TempDir;
use std::fs;
#[test]
fn test_percentile_calculation() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(percentile(&values, 0.0), 1.0);
assert_eq!(percentile(&values, 0.5), 3.0);
assert_eq!(percentile(&values, 1.0), 5.0);
let empty: Vec<f64> = vec![];
assert_eq!(percentile(&empty, 0.5), 0.0);
}
#[test]
fn test_identify_primary_factor() {
let components = TDGComponents {
complexity: 0.8,
churn: 0.2,
coupling: 0.1,
domain_risk: 0.1,
duplication: 0.1,
};
assert_eq!(identify_primary_factor(&components), "High Complexity");
let components2 = TDGComponents {
complexity: 0.2,
churn: 0.9,
coupling: 0.1,
domain_risk: 0.1,
duplication: 0.1,
};
assert_eq!(identify_primary_factor(&components2), "Frequent Changes");
}
#[test]
fn test_estimate_refactoring_hours() {
let hours_0 = estimate_refactoring_hours(0.0);
assert_eq!(hours_0, 2.0);
let hours_1 = estimate_refactoring_hours(1.0);
assert!((hours_1 - 3.6).abs() < 0.01);
let hours_2 = estimate_refactoring_hours(2.0);
assert!((hours_2 - 6.48).abs() < 0.01); }
#[test]
fn test_format_empty_results() {
let json_result = format_empty_results(TdgOutputFormat::Json);
assert!(json_result.contains("\"hotspots\": []"));
assert!(json_result.contains("\"total_files\": 0"));
let csv_result = format_empty_results(TdgOutputFormat::Csv);
assert!(csv_result.contains("Path,TDG Score"));
let summary_result = format_empty_results(TdgOutputFormat::Summary);
assert!(summary_result.contains("No files"));
}
#[test]
fn test_tdg_severity_conversion() {
assert_eq!(TDGSeverity::from(0.5), TDGSeverity::Normal);
assert_eq!(TDGSeverity::from(2.0), TDGSeverity::Warning);
assert_eq!(TDGSeverity::from(3.0), TDGSeverity::Critical);
assert_eq!(TDGSeverity::Normal.as_str(), "normal");
assert_eq!(TDGSeverity::Warning.as_str(), "warning");
assert_eq!(TDGSeverity::Critical.as_str(), "critical");
}
#[test]
fn test_format_json_output() {
let summary = TDGSummary {
total_files: 2,
average_tdg: 1.5,
p95_tdg: 2.0,
p99_tdg: 2.0,
estimated_debt_hours: 10.0,
critical_files: 0,
warning_files: 1,
hotspots: vec![
TDGHotspot {
path: "file1.rs".to_string(),
tdg_score: 2.0,
primary_factor: "High Complexity".to_string(),
estimated_hours: 3.6,
},
],
};
let json = format_json_output(&summary);
assert!(json.contains("\"total_files\": 2"));
assert!(json.contains("\"average_tdg\": 1.5"));
assert!(json.contains("file1.rs"));
}
#[test]
fn test_format_csv_output() {
let summary = TDGSummary {
total_files: 1,
average_tdg: 1.5,
p95_tdg: 1.5,
p99_tdg: 1.5,
estimated_debt_hours: 5.0,
critical_files: 0,
warning_files: 1,
hotspots: vec![
TDGHotspot {
path: "test.rs".to_string(),
tdg_score: 1.5,
primary_factor: "Frequent Changes".to_string(),
estimated_hours: 2.7,
},
],
};
let csv = format_csv_output(&summary);
assert!(csv.contains("Path,TDG Score,Severity,Primary Factor,Est. Hours"));
assert!(csv.contains("test.rs,1.50,Medium,Frequent Changes,2.70"));
}
#[test]
fn test_format_summary_output() {
let summary = TDGSummary {
total_files: 10,
average_tdg: 1.2,
p95_tdg: 3.0,
p99_tdg: 3.5,
estimated_debt_hours: 50.0,
critical_files: 1,
warning_files: 2,
hotspots: vec![],
};
let output = format_summary_output(&summary, false);
assert!(output.contains("📊 Technical Debt Gradient Summary"));
assert!(output.contains("Files analyzed: 10"));
assert!(output.contains("Average TDG"));
assert!(output.contains("Critical"));
}
#[test]
fn test_format_markdown_output() {
let summary = TDGSummary {
total_files: 5,
average_tdg: 2.0,
p95_tdg: 3.5,
p99_tdg: 4.0,
estimated_debt_hours: 20.0,
critical_files: 2,
warning_files: 1,
hotspots: vec![
TDGHotspot {
path: "critical.rs".to_string(),
tdg_score: 4.0,
primary_factor: "High Complexity".to_string(),
estimated_hours: 10.0,
},
],
};
let md = format_markdown_output(&summary, true);
assert!(md.contains("# Technical Debt Gradient Report"));
assert!(md.contains("## Summary Statistics"));
assert!(md.contains("critical.rs"));
assert!(md.contains("## Component Breakdown"));
}
#[test]
fn test_format_sarif_output() {
let summary = TDGSummary {
total_files: 1,
average_tdg: 3.0,
p95_tdg: 3.0,
p99_tdg: 3.0,
estimated_debt_hours: 7.2,
critical_files: 1,
warning_files: 0,
hotspots: vec![
TDGHotspot {
path: "problem.rs".to_string(),
tdg_score: 3.0,
primary_factor: "High Coupling".to_string(),
estimated_hours: 7.2,
},
],
};
let sarif = format_sarif_output(&summary);
let parsed: serde_json::Value = serde_json::from_str(&sarif).unwrap();
assert_eq!(parsed["version"], "2.1.0");
assert!(parsed["runs"][0]["tool"]["driver"]["name"] == "pmat-tdg");
assert!(parsed["runs"][0]["results"][0]["ruleId"] == "TDG001");
assert!(parsed["runs"][0]["results"][0]["level"] == "error");
}
#[tokio::test]
async fn test_handle_analyze_tdg_single_file() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, "fn main() { println!(\"test\"); }").unwrap();
let output_file = temp_dir.path().join("output.json");
let result = handle_analyze_tdg(
temp_dir.path().to_path_buf(),
Some(test_file.clone()),
vec![],
0.0, 5,
TdgOutputFormat::Json,
false,
Some(output_file.clone()),
false,
false,
vec![],
false,
).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_handle_analyze_tdg_watch_mode() {
let temp_dir = TempDir::new().unwrap();
let result = handle_analyze_tdg(
temp_dir.path().to_path_buf(),
None,
vec![],
1.0,
5,
TdgOutputFormat::Summary,
false,
None,
false,
false,
vec![],
true, ).await;
assert!(result.is_ok());
}
#[test]
fn test_format_single_file_output() {
let score = TDGScore {
value: 2.5,
severity: TDGSeverity::Warning,
components: TDGComponents {
complexity: 0.7,
churn: 0.3,
coupling: 0.2,
domain_risk: 0.1,
duplication: 0.1,
},
percentile: 90.0,
confidence: 0.95,
};
let path = PathBuf::from("test.rs");
let json_result = format_single_file_output(
&score,
&path,
TdgOutputFormat::Json,
true,
false,
);
assert!(json_result.is_ok());
let json = json_result.unwrap();
assert!(json.contains("\"value\": 2.5"));
assert!(json.contains("\"severity\": \"warning\""));
let summary_result = format_single_file_output(
&score,
&path,
TdgOutputFormat::Summary,
false,
true,
);
assert!(summary_result.is_ok());
let summary = summary_result.unwrap();
assert!(summary.contains("TDG Score: 2.50"));
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
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);
}
}
}