pmat 3.15.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// Tests for defect prediction helpers
// Included from defect_prediction_helpers.rs - shares parent module scope

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
    use super::*;
    use crate::services::defect_probability::RiskLevel;

    fn strip_ansi(s: &str) -> String {
        let re = regex::Regex::new(r"\x1b\[[0-9;]*m").unwrap();
        re.replace_all(s, "").to_string()
    }

    // ==================== Config Tests ====================

    #[test]
    fn test_defect_prediction_config_struct() {
        let config = DefectPredictionConfig {
            confidence_threshold: 0.8,
            min_lines: 10,
            include_low_confidence: false,
            high_risk_only: true,
            include_recommendations: true,
            include: Some("src/".to_string()),
            exclude: Some("test".to_string()),
        };
        assert_eq!(config.confidence_threshold, 0.8);
        assert_eq!(config.min_lines, 10);
        assert!(!config.include_low_confidence);
        assert!(config.high_risk_only);
    }

    // ==================== Complexity Calculation Tests ====================

    #[test]
    fn test_calculate_simple_complexity_empty() {
        let complexity = calculate_simple_complexity("");
        assert_eq!(complexity, 1); // Base complexity is 1
    }

    #[test]
    fn test_calculate_simple_complexity_no_branching() {
        let code = r#"
let x = 1;
let y = 2;
let z = x + y;
"#;
        let complexity = calculate_simple_complexity(code);
        assert_eq!(complexity, 1); // No branches
    }

    #[test]
    fn test_calculate_simple_complexity_with_if() {
        let code = r#"
if x > 0 {
    return true;
}
"#;
        let complexity = calculate_simple_complexity(code);
        assert!(complexity > 1);
    }

    #[test]
    fn test_calculate_simple_complexity_with_loops() {
        let code = r#"
for item in items {
    process(item);
}
while running {
    tick();
}
"#;
        let complexity = calculate_simple_complexity(code);
        assert!(complexity >= 3); // Base + 2 loops
    }

    #[test]
    fn test_calculate_simple_complexity_with_match() {
        let code = r#"
match value {
    0 => zero(),
    1 => one(),
    _ => other(),
}
"#;
        let complexity = calculate_simple_complexity(code);
        assert!(complexity > 1);
    }

    #[test]
    fn test_calculate_simple_complexity_with_logical_operators() {
        let code = r#"
if x && y {
    do_something();
}
if a || b {
    do_other();
}
"#;
        let complexity = calculate_simple_complexity(code);
        assert!(complexity >= 3);
    }

    #[test]
    fn test_calculate_simple_complexity_with_exception_handling() {
        let code = r#"
catch (Exception e) {
    log(e);
}
except ValueError:
    handle_error()
"#;
        let complexity = calculate_simple_complexity(code);
        assert!(complexity >= 3);
    }

    // ==================== Churn Score Tests ====================

    #[test]
    fn test_calculate_simple_churn_score_empty() {
        let score = calculate_simple_churn_score("", 0);
        assert!((0.0..=1.0).contains(&score));
    }

    #[test]
    fn test_calculate_simple_churn_score_with_todos() {
        let code = r#"
// TODO: fix this
fn main() {
    // FIXME: broken
    println!("hello");
}
"#;
        let score = calculate_simple_churn_score(code, 5);
        assert!(score > 0.0);
    }

    #[test]
    fn test_calculate_simple_churn_score_with_comments() {
        let code = r#"
// This is a comment
// Another comment
/* Block comment */
fn main() {
    println!("hello");
}
"#;
        let score = calculate_simple_churn_score(code, 7);
        assert!((0.0..=1.0).contains(&score));
    }

    #[test]
    fn test_calculate_simple_churn_score_no_comments() {
        let code = r#"
fn main() {
    println!("hello");
}
"#;
        let score = calculate_simple_churn_score(code, 4);
        // No comments means high churn potential
        assert!(score >= 0.4);
    }

    // ==================== File Metrics Collection Tests ====================

    #[test]
    fn test_collect_file_metrics_empty() {
        let files: Vec<(PathBuf, String, usize)> = vec![];
        let metrics = collect_file_metrics(&files);
        assert!(metrics.is_empty());
    }

    #[test]
    fn test_collect_file_metrics_single_file() {
        let files = vec![(
            PathBuf::from("test.rs"),
            "fn main() {\n    println!(\"hello\");\n}".to_string(),
            3,
        )];
        let metrics = collect_file_metrics(&files);
        assert_eq!(metrics.len(), 1);
        assert_eq!(metrics[0].lines_of_code, 3);
        assert!(metrics[0].complexity >= 1.0);
    }

    #[test]
    fn test_collect_file_metrics_with_imports() {
        let code = r#"
use std::io;
import os
#include <stdio.h>
fn main() {}
"#;
        let files = vec![(PathBuf::from("test.rs"), code.to_string(), 5)];
        let metrics = collect_file_metrics(&files);
        assert_eq!(metrics[0].afferent_coupling, 3.0); // 3 imports
    }

    // ==================== Helper Function Tests ====================

    #[test]
    fn test_count_conditional_statements() {
        assert_eq!(count_conditional_statements("if x > 0 {"), 1);
        assert_eq!(count_conditional_statements("else if y < 0 {"), 1);
        assert_eq!(count_conditional_statements("let x = 1;"), 0);
    }

    #[test]
    fn test_count_loop_statements() {
        assert_eq!(count_loop_statements("for item in items {"), 1);
        assert_eq!(count_loop_statements("while running {"), 1);
        assert_eq!(count_loop_statements("let x = 1;"), 0);
    }

    #[test]
    fn test_count_pattern_matching() {
        assert_eq!(count_pattern_matching("match value {"), 1);
        assert_eq!(count_pattern_matching("switch (x) {"), 1);
        assert_eq!(count_pattern_matching("case 1:"), 1);
        assert_eq!(count_pattern_matching("x => y"), 1);
        assert_eq!(count_pattern_matching("let x = 1;"), 0);
    }

    #[test]
    fn test_count_logical_operators() {
        assert_eq!(count_logical_operators("if x && y {"), 1);
        assert_eq!(count_logical_operators("if a || b {"), 1);
        assert_eq!(count_logical_operators("if x && y || z {"), 1); // Only counts once
        assert_eq!(count_logical_operators("let x = 1;"), 0);
    }

    #[test]
    fn test_count_exception_handling() {
        assert_eq!(count_exception_handling("catch (Exception e) {"), 1);
        assert_eq!(count_exception_handling("except ValueError:"), 1);
        assert_eq!(count_exception_handling("try {"), 0);
    }

    #[test]
    fn test_count_line_complexity() {
        // Line with multiple complexity contributors
        let complexity = count_line_complexity("if x && y {");
        assert!(complexity >= 2); // if + &&
    }

    // ==================== Analysis Result Tests ====================

    #[test]
    fn test_defect_analysis_result_struct() {
        let result = DefectAnalysisResult {
            file_metrics: vec![],
            filtered_predictions: vec![],
            analysis_time: std::time::Duration::from_secs(1),
        };
        assert!(result.file_metrics.is_empty());
        assert!(result.filtered_predictions.is_empty());
        assert_eq!(result.analysis_time.as_secs(), 1);
    }

    // ==================== Risk Distribution Struct Tests ====================

    #[test]
    fn test_risk_distribution_struct() {
        let dist = RiskDistribution {
            high_risk_count: 5,
            medium_risk_count: 10,
            low_risk_count: 20,
        };
        assert_eq!(dist.high_risk_count, 5);
        assert_eq!(dist.medium_risk_count, 10);
        assert_eq!(dist.low_risk_count, 20);
    }

    // Continued in defect_prediction_tests_part2.rs
    include!("defect_prediction_tests_part2.rs");
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn basic_property_stability(_input in ".*") {
            // Basic property test for coverage
            prop_assert!(true);
        }

        #[test]
        fn module_consistency_check(_x in 0u32..1000) {
            // Module consistency verification
            prop_assert!(_x < 1001);
        }
    }
}