#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod extreme_tdd_coverage_tests {
use super::*;
use std::path::PathBuf;
use tempfile::TempDir;
#[test]
fn test_identify_primary_factor_high_complexity() {
let components = crate::models::tdg::TDGComponents {
complexity: 10.0,
churn: 1.0,
coupling: 1.0,
domain_risk: 1.0,
duplication: 1.0,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "High Complexity");
}
#[test]
fn test_identify_primary_factor_frequent_changes() {
let components = crate::models::tdg::TDGComponents {
complexity: 1.0,
churn: 10.0,
coupling: 1.0,
domain_risk: 1.0,
duplication: 1.0,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "Frequent Changes");
}
#[test]
fn test_identify_primary_factor_high_coupling() {
let components = crate::models::tdg::TDGComponents {
complexity: 0.1,
churn: 0.1,
coupling: 10.0,
domain_risk: 0.1,
duplication: 0.1,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "High Coupling");
}
#[test]
fn test_identify_primary_factor_domain_risk() {
let components = crate::models::tdg::TDGComponents {
complexity: 0.1,
churn: 0.1,
coupling: 0.1,
domain_risk: 10.0,
duplication: 0.1,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "Domain Risk");
}
#[test]
fn test_identify_primary_factor_code_duplication() {
let components = crate::models::tdg::TDGComponents {
complexity: 0.1,
churn: 0.1,
coupling: 0.1,
domain_risk: 0.1,
duplication: 10.0,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "Code Duplication");
}
#[test]
fn test_identify_primary_factor_equal_values() {
let components = crate::models::tdg::TDGComponents {
complexity: 1.0,
churn: 1.0,
coupling: 1.0,
domain_risk: 1.0,
duplication: 1.0,
};
let factor = identify_primary_factor(&components);
assert_eq!(factor, "Frequent Changes");
}
#[test]
fn test_determine_satd_severity_hack() {
assert_eq!(determine_satd_severity("HACK"), "high");
}
#[test]
fn test_determine_satd_severity_xxx() {
assert_eq!(determine_satd_severity("XXX"), "high");
}
#[test]
fn test_determine_satd_severity_fixme() {
assert_eq!(determine_satd_severity("FIXME"), "medium");
}
#[test]
fn test_determine_satd_severity_refactor() {
assert_eq!(determine_satd_severity("REFACTOR"), "medium");
}
#[test]
fn test_determine_satd_severity_todo() {
assert_eq!(determine_satd_severity("TODO"), "low");
}
#[test]
fn test_determine_satd_severity_unknown() {
assert_eq!(determine_satd_severity("UNKNOWN"), "low");
}
#[test]
fn test_get_coverage_emoji_positive() {
assert_eq!(get_coverage_emoji(5.0), "📈");
}
#[test]
fn test_get_coverage_emoji_negative() {
assert_eq!(get_coverage_emoji(-5.0), "📉");
}
#[test]
fn test_get_coverage_emoji_zero() {
assert_eq!(get_coverage_emoji(0.0), "📉");
}
#[test]
fn test_extract_filename_basic() {
let path = std::path::Path::new("/home/user/project/src/main.rs");
assert_eq!(extract_filename(path), "main.rs");
}
#[test]
fn test_extract_filename_no_extension() {
let path = std::path::Path::new("/home/user/Makefile");
assert_eq!(extract_filename(path), "Makefile");
}
#[test]
fn test_extract_filename_root() {
let path = std::path::Path::new("/");
assert_eq!(extract_filename(path), "unknown");
}
#[test]
fn test_extract_filename_empty() {
let path = std::path::Path::new("");
assert_eq!(extract_filename(path), "unknown");
}
#[test]
fn test_calculate_files_to_show_zero_top_files() {
let files = vec![
FileCoverageMetrics {
path: PathBuf::from("file1.rs"),
base_coverage: 80.0,
target_coverage: 85.0,
coverage_delta: 5.0,
lines_added: 100,
lines_covered: 85,
lines_uncovered: 15,
},
FileCoverageMetrics {
path: PathBuf::from("file2.rs"),
base_coverage: 70.0,
target_coverage: 75.0,
coverage_delta: 5.0,
lines_added: 50,
lines_covered: 38,
lines_uncovered: 12,
},
];
assert_eq!(calculate_files_to_show(&files, 0), 2);
}
#[test]
fn test_calculate_files_to_show_limited() {
let files = vec![
FileCoverageMetrics {
path: PathBuf::from("file1.rs"),
base_coverage: 80.0,
target_coverage: 85.0,
coverage_delta: 5.0,
lines_added: 100,
lines_covered: 85,
lines_uncovered: 15,
},
FileCoverageMetrics {
path: PathBuf::from("file2.rs"),
base_coverage: 70.0,
target_coverage: 75.0,
coverage_delta: 5.0,
lines_added: 50,
lines_covered: 38,
lines_uncovered: 12,
},
FileCoverageMetrics {
path: PathBuf::from("file3.rs"),
base_coverage: 60.0,
target_coverage: 65.0,
coverage_delta: 5.0,
lines_added: 30,
lines_covered: 20,
lines_uncovered: 10,
},
];
assert_eq!(calculate_files_to_show(&files, 2), 2);
}
#[test]
fn test_calculate_files_to_show_exceeds_available() {
let files = vec![FileCoverageMetrics {
path: PathBuf::from("file1.rs"),
base_coverage: 80.0,
target_coverage: 85.0,
coverage_delta: 5.0,
lines_added: 100,
lines_covered: 85,
lines_uncovered: 15,
}];
assert_eq!(calculate_files_to_show(&files, 10), 1);
}
#[test]
fn test_get_severity_icon_error() {
assert_eq!(get_severity_icon("error"), "🔴");
}
#[test]
fn test_get_severity_icon_warning() {
assert_eq!(get_severity_icon("warning"), "🟡");
}
#[test]
fn test_get_severity_icon_info() {
assert_eq!(get_severity_icon("info"), "🟢");
}
#[test]
fn test_get_severity_icon_unknown() {
assert_eq!(get_severity_icon("unknown"), "🟢");
}
#[test]
fn test_build_complexity_thresholds_defaults() {
let (cyclomatic, cognitive) = build_complexity_thresholds(None, None);
assert_eq!(cyclomatic, 10);
assert_eq!(cognitive, 15);
}
#[test]
fn test_build_complexity_thresholds_custom_cyclomatic() {
let (cyclomatic, cognitive) = build_complexity_thresholds(Some(20), None);
assert_eq!(cyclomatic, 20);
assert_eq!(cognitive, 15);
}
#[test]
fn test_build_complexity_thresholds_custom_cognitive() {
let (cyclomatic, cognitive) = build_complexity_thresholds(None, Some(25));
assert_eq!(cyclomatic, 10);
assert_eq!(cognitive, 25);
}
#[test]
fn test_build_complexity_thresholds_custom_both() {
let (cyclomatic, cognitive) = build_complexity_thresholds(Some(30), Some(40));
assert_eq!(cyclomatic, 30);
assert_eq!(cognitive, 40);
}
fn make_test_file_metrics(path: &str) -> crate::services::complexity::FileComplexityMetrics {
crate::services::complexity::FileComplexityMetrics {
path: path.to_string(),
total_complexity: crate::services::complexity::ComplexityMetrics {
cyclomatic: 1,
cognitive: 1,
nesting_max: 0,
lines: 10,
halstead: None,
},
functions: vec![],
classes: vec![],
}
}
#[test]
fn test_add_top_files_ranking_zero() {
let files = vec![
make_test_file_metrics("file1.rs"),
make_test_file_metrics("file2.rs"),
];
let result = add_top_files_ranking(files, 0);
assert_eq!(result.len(), 2);
}
#[test]
fn test_add_top_files_ranking_limited() {
let files = vec![
make_test_file_metrics("file1.rs"),
make_test_file_metrics("file2.rs"),
make_test_file_metrics("file3.rs"),
];
let result = add_top_files_ranking(files, 1);
assert_eq!(result.len(), 1);
}
#[test]
fn test_params_to_json_empty() {
let params: Vec<(String, serde_json::Value)> = vec![];
let result = params_to_json(params);
assert!(result.is_empty());
}
#[test]
fn test_params_to_json_single() {
let params = vec![("key".to_string(), serde_json::json!("value"))];
let result = params_to_json(params);
assert_eq!(result.get("key").unwrap(), "value");
}
#[test]
fn test_params_to_json_multiple() {
let params = vec![
("string".to_string(), serde_json::json!("hello")),
("number".to_string(), serde_json::json!(42)),
("bool".to_string(), serde_json::json!(true)),
];
let result = params_to_json(params);
assert_eq!(result.len(), 3);
assert_eq!(result.get("string").unwrap(), "hello");
assert_eq!(result.get("number").unwrap(), 42);
assert_eq!(result.get("bool").unwrap(), true);
}
#[test]
fn test_quality_gate_results_default_comprehensive() {
let results = QualityGateResults::default();
assert!(results.passed);
assert_eq!(results.total_violations, 0);
assert_eq!(results.complexity_violations, 0);
assert_eq!(results.dead_code_violations, 0);
assert_eq!(results.satd_violations, 0);
assert_eq!(results.entropy_violations, 0);
assert_eq!(results.security_violations, 0);
assert_eq!(results.duplicate_violations, 0);
assert_eq!(results.coverage_violations, 0);
assert_eq!(results.section_violations, 0);
assert_eq!(results.provability_violations, 0);
assert!(results.provability_score.is_none());
assert!(results.violations.is_empty());
}
#[test]
fn test_quality_violation_serialization() {
let violation = QualityViolation {
check_type: "complexity".to_string(),
severity: "error".to_string(),
file: "src/main.rs".to_string(),
line: Some(42),
message: "Function too complex".to_string(),
};
let json = serde_json::to_string(&violation).unwrap();
assert!(json.contains("\"check_type\":\"complexity\""));
assert!(json.contains("\"severity\":\"error\""));
assert!(json.contains("\"file\":\"src/main.rs\""));
assert!(json.contains("\"line\":42"));
assert!(json.contains("\"message\":\"Function too complex\""));
}
#[test]
fn test_quality_violation_no_line() {
let violation = QualityViolation {
check_type: "coverage".to_string(),
severity: "warning".to_string(),
file: "project".to_string(),
line: None,
message: "Coverage below threshold".to_string(),
};
let json = serde_json::to_string(&violation).unwrap();
assert!(json.contains("\"line\":null"));
}
#[test]
fn test_get_severity_display_error() {
assert_eq!(
get_severity_display(&makefile_linter::Severity::Error),
"❌ Error"
);
}
#[test]
fn test_get_severity_display_warning() {
assert_eq!(
get_severity_display(&makefile_linter::Severity::Warning),
"⚠\u{fe0f} Warning"
);
}
#[test]
fn test_get_severity_display_info() {
assert_eq!(
get_severity_display(&makefile_linter::Severity::Info),
"ℹ\u{fe0f} Info"
);
}
#[test]
fn test_get_sarif_level_error() {
assert_eq!(
get_sarif_level(&makefile_linter::Severity::Error),
"error"
);
}
#[test]
fn test_get_sarif_level_warning() {
assert_eq!(
get_sarif_level(&makefile_linter::Severity::Warning),
"warning"
);
}
#[test]
fn test_get_sarif_level_info() {
assert_eq!(get_sarif_level(&makefile_linter::Severity::Info), "note");
}
#[test]
fn test_get_gcc_level_error() {
assert_eq!(get_gcc_level(&makefile_linter::Severity::Error), "error");
}
#[test]
fn test_get_gcc_level_warning() {
assert_eq!(
get_gcc_level(&makefile_linter::Severity::Warning),
"warning"
);
}
#[test]
fn test_get_gcc_level_info() {
assert_eq!(get_gcc_level(&makefile_linter::Severity::Info), "note");
}
#[test]
fn test_format_quality_gate_output_json() {
let results = QualityGateResults {
passed: false,
total_violations: 5,
complexity_violations: 2,
dead_code_violations: 1,
satd_violations: 1,
entropy_violations: 1,
security_violations: 0,
duplicate_violations: 0,
coverage_violations: 0,
section_violations: 0,
provability_violations: 0,
provability_score: Some(0.85),
violations: vec![],
};
let violations = vec![QualityViolation {
check_type: "complexity".to_string(),
severity: "error".to_string(),
file: "src/main.rs".to_string(),
line: Some(10),
message: "High complexity".to_string(),
}];
let output =
format_quality_gate_output(&results, &violations, QualityGateOutputFormat::Json)
.unwrap();
assert!(output.contains("\"passed\": false"));
assert!(output.contains("\"total_violations\": 5"));
}
#[test]
fn test_format_quality_gate_output_human() {
let results = QualityGateResults {
passed: true,
total_violations: 0,
complexity_violations: 0,
dead_code_violations: 0,
satd_violations: 0,
entropy_violations: 0,
security_violations: 0,
duplicate_violations: 0,
coverage_violations: 0,
section_violations: 0,
provability_violations: 0,
provability_score: None,
violations: vec![],
};
let output =
format_quality_gate_output(&results, &[], QualityGateOutputFormat::Human).unwrap();
assert!(output.contains("PASSED"));
assert!(output.contains("Total violations: 0"));
}
#[test]
fn test_format_quality_gate_output_summary() {
let results = QualityGateResults {
passed: false,
total_violations: 10,
complexity_violations: 5,
dead_code_violations: 3,
satd_violations: 2,
entropy_violations: 0,
security_violations: 0,
duplicate_violations: 0,
coverage_violations: 0,
section_violations: 0,
provability_violations: 0,
provability_score: None,
violations: vec![],
};
let output =
format_quality_gate_output(&results, &[], QualityGateOutputFormat::Summary).unwrap();
assert!(output.contains("FAILED"));
assert!(output.contains("10"));
}
#[test]
fn test_format_incremental_coverage_summary_basic() {
let report = IncrementalCoverageReport {
base_branch: "main".to_string(),
target_branch: "feature".to_string(),
coverage_threshold: 0.8,
files: vec![FileCoverageMetrics {
path: PathBuf::from("src/main.rs"),
base_coverage: 75.0,
target_coverage: 85.0,
coverage_delta: 10.0,
lines_added: 100,
lines_covered: 85,
lines_uncovered: 15,
}],
summary: CoverageSummary {
total_files_changed: 1,
files_improved: 1,
files_degraded: 0,
overall_delta: 10.0,
meets_threshold: true,
},
};
let output = format_incremental_coverage_summary(&report, 10).unwrap();
assert!(output.contains("Incremental Coverage Analysis"));
assert!(output.contains("main"));
assert!(output.contains("feature"));
assert!(output.contains("Files Changed: 1"));
}
#[test]
fn test_format_incremental_coverage_summary_empty_files() {
let report = IncrementalCoverageReport {
base_branch: "main".to_string(),
target_branch: "feature".to_string(),
coverage_threshold: 0.8,
files: vec![],
summary: CoverageSummary {
total_files_changed: 0,
files_improved: 0,
files_degraded: 0,
overall_delta: 0.0,
meets_threshold: true,
},
};
let output = format_incremental_coverage_summary(&report, 10).unwrap();
assert!(output.contains("Files Changed: 0"));
}
#[test]
fn test_incremental_coverage_report_serialization() {
let report = IncrementalCoverageReport {
base_branch: "main".to_string(),
target_branch: "feature".to_string(),
coverage_threshold: 0.8,
files: vec![],
summary: CoverageSummary {
total_files_changed: 0,
files_improved: 0,
files_degraded: 0,
overall_delta: 0.0,
meets_threshold: true,
},
};
let json = serde_json::to_string(&report).unwrap();
assert!(json.contains("\"base_branch\":\"main\""));
assert!(json.contains("\"target_branch\":\"feature\""));
assert!(json.contains("\"coverage_threshold\":0.8"));
}
#[test]
fn test_has_source_extension_rust() {
assert!(has_source_extension(std::path::Path::new("main.rs")));
}
#[test]
fn test_has_source_extension_javascript() {
assert!(has_source_extension(std::path::Path::new("app.js")));
}
#[test]
fn test_has_source_extension_typescript() {
assert!(has_source_extension(std::path::Path::new("app.ts")));
}
#[test]
fn test_has_source_extension_python() {
assert!(has_source_extension(std::path::Path::new("main.py")));
}
#[test]
fn test_has_source_extension_java() {
assert!(has_source_extension(std::path::Path::new("Main.java")));
}
#[test]
fn test_has_source_extension_cpp() {
assert!(has_source_extension(std::path::Path::new("main.cpp")));
}
#[test]
fn test_has_source_extension_c() {
assert!(has_source_extension(std::path::Path::new("main.c")));
}
#[test]
fn test_has_source_extension_non_source() {
assert!(!has_source_extension(std::path::Path::new("README.md")));
assert!(!has_source_extension(std::path::Path::new("Cargo.toml")));
assert!(!has_source_extension(std::path::Path::new("data.json")));
}
#[test]
fn test_is_excluded_test_path_tests_dir() {
assert!(is_excluded_test_path(std::path::Path::new(
"/project/tests/unit.rs"
)));
}
#[test]
fn test_is_excluded_test_path_test_dir() {
assert!(is_excluded_test_path(std::path::Path::new(
"/project/test/integration.rs"
)));
}
#[test]
fn test_is_excluded_test_path_examples_dir() {
assert!(is_excluded_test_path(std::path::Path::new(
"/project/examples/demo.rs"
)));
}
#[test]
fn test_is_excluded_test_path_benches_dir() {
assert!(is_excluded_test_path(std::path::Path::new(
"/project/benches/perf.rs"
)));
}
#[test]
fn test_is_excluded_test_path_src_dir() {
assert!(!is_excluded_test_path(std::path::Path::new(
"/project/src/main.rs"
)));
}
#[tokio::test]
async fn test_check_duplicates_empty_dir() {
let temp_dir = TempDir::new().unwrap();
let result = check_duplicates(temp_dir.path()).await;
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
}
#[tokio::test]
async fn test_check_satd_empty_dir() {
let temp_dir = TempDir::new().unwrap();
let result = check_satd(temp_dir.path()).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_calculate_provability_score_empty_dir() {
let temp_dir = TempDir::new().unwrap();
let result = calculate_provability_score(temp_dir.path()).await;
assert!(result.is_ok());
let score = result.unwrap();
assert!(score >= 0.0 && score <= 1.0);
}
#[tokio::test]
async fn test_analyze_project_files_empty() {
let temp_dir = TempDir::new().unwrap();
let result = analyze_project_files(temp_dir.path(), Some("rust"), &[], 20, 15).await;
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
}
#[tokio::test]
async fn test_analyze_project_files_with_rust_file() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(
temp_dir.path().join("main.rs"),
"fn main() { println!(\"hello\"); }",
)
.unwrap();
let result = analyze_project_files(temp_dir.path(), Some("rust"), &[], 20, 15).await;
assert!(result.is_ok());
let files = result.unwrap();
assert!(!files.is_empty() || files.is_empty()); }
#[test]
fn test_extract_identifiers_rust() {
let code = "pub fn calculate_sum(a: i32, b: i32) -> i32 { a + b }";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "calculate_sum"));
}
#[test]
fn test_extract_identifiers_struct() {
let code = "pub struct MyStruct { field: String }";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "MyStruct"));
}
#[test]
fn test_extract_identifiers_enum() {
let code = "pub enum Status { Active, Inactive }";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "Status"));
}
#[test]
fn test_extract_identifiers_trait() {
let code = "pub trait Serializable { fn serialize(&self); }";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "Serializable"));
}
#[test]
fn test_extract_identifiers_const() {
let code = "pub const MAX_VALUE: u32 = 100;";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "MAX_VALUE"));
}
#[test]
fn test_extract_identifiers_python() {
let code = "def process_data(items):\n return [x * 2 for x in items]";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "process_data"));
}
#[test]
fn test_extract_identifiers_javascript() {
let code = "function handleClick(event) { console.log(event); }";
let identifiers = extract_identifiers(code);
assert!(identifiers.iter().any(|i| i.name == "handleClick"));
}
#[test]
fn test_extract_identifiers_empty() {
let code = "";
let identifiers = extract_identifiers(code);
assert!(identifiers.is_empty());
}
#[test]
fn test_calculate_string_similarity_identical() {
assert!((calculate_string_similarity("hello", "hello") - 1.0).abs() < 0.001);
}
#[test]
fn test_calculate_string_similarity_completely_different() {
let sim = calculate_string_similarity("abc", "xyz");
assert!(sim < 0.5);
}
#[test]
fn test_calculate_string_similarity_empty_both() {
assert!((calculate_string_similarity("", "") - 1.0).abs() < 0.001);
}
#[test]
fn test_calculate_string_similarity_one_empty() {
let sim = calculate_string_similarity("hello", "");
assert!(sim < 1.0);
}
#[test]
fn test_calculate_edit_distance_identical() {
assert_eq!(calculate_edit_distance("hello", "hello"), 0);
}
#[test]
fn test_calculate_edit_distance_one_char_diff() {
assert_eq!(calculate_edit_distance("hello", "hallo"), 1);
}
#[test]
fn test_calculate_edit_distance_empty_to_string() {
assert_eq!(calculate_edit_distance("", "hello"), 5);
}
#[test]
fn test_calculate_edit_distance_string_to_empty() {
assert_eq!(calculate_edit_distance("hello", ""), 5);
}
#[test]
fn test_calculate_edit_distance_both_empty() {
assert_eq!(calculate_edit_distance("", ""), 0);
}
#[test]
fn test_calculate_soundex_basic() {
assert_eq!(calculate_soundex("Robert"), "R163");
}
#[test]
fn test_calculate_soundex_similar_names() {
assert_eq!(calculate_soundex("Robert"), calculate_soundex("Rupert"));
}
#[test]
fn test_calculate_soundex_empty() {
assert_eq!(calculate_soundex(""), "");
}
#[test]
fn test_calculate_soundex_single_char() {
assert_eq!(calculate_soundex("A"), "A000");
}
#[test]
fn test_calculate_soundex_numbers_only() {
assert_eq!(calculate_soundex("123"), "");
}
use proptest::prelude::*;
proptest! {
#[test]
fn prop_edit_distance_symmetric(s1 in "[a-z]{0,10}", s2 in "[a-z]{0,10}") {
let d1 = calculate_edit_distance(&s1, &s2);
let d2 = calculate_edit_distance(&s2, &s1);
prop_assert_eq!(d1, d2);
}
#[test]
fn prop_edit_distance_triangle_inequality(s1 in "[a-z]{0,5}", s2 in "[a-z]{0,5}", s3 in "[a-z]{0,5}") {
let d12 = calculate_edit_distance(&s1, &s2);
let d23 = calculate_edit_distance(&s2, &s3);
let d13 = calculate_edit_distance(&s1, &s3);
prop_assert!(d13 <= d12 + d23);
}
#[test]
fn prop_string_similarity_bounds(s1 in "[a-z]{0,10}", s2 in "[a-z]{0,10}") {
let sim = calculate_string_similarity(&s1, &s2);
prop_assert!(sim >= 0.0 && sim <= 1.0);
}
#[test]
fn prop_soundex_length(s in "[a-zA-Z]{1,20}") {
let soundex = calculate_soundex(&s);
if !soundex.is_empty() {
prop_assert_eq!(soundex.len(), 4);
}
}
#[test]
fn prop_content_hash_same_for_same_content(content in "[a-zA-Z0-9]{0,100}") {
let h1 = calculate_content_hash(&content);
let h2 = calculate_content_hash(&content);
prop_assert_eq!(h1, h2);
}
}
}