pmat 3.15.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
    use super::*;
    use crate::models::churn::{ChurnSummary, CodeChurnAnalysis, FileChurnMetrics};
    use chrono::Utc;
    use proptest::prelude::*;
    use std::collections::HashMap;
    use std::path::PathBuf;

    proptest! {
        #[test]
        fn prop_is_source_file_deterministic(path in "[a-z]{1,20}\\.[a-z]{1,5}") {
            let path = Path::new(&path);
            let result1 = is_source_file(path);
            let result2 = is_source_file(path);
            prop_assert_eq!(result1, result2, "is_source_file should be deterministic");
        }

        #[test]
        fn prop_rust_files_always_source(name in "[a-z]{1,20}") {
            let path_str = format!("src/{}.rs", name);
            let path = Path::new(&path_str);
            prop_assert!(
                is_source_file(path),
                "Any .rs file in src/ should be a source file"
            );
        }

        #[test]
        fn prop_test_directories_never_source(
            dir in prop_oneof![
                Just("tests"),
                Just("test"),
                Just("examples"),
                Just("benches"),
                Just("fixtures"),
                Just("__tests__")
            ],
            _name in "[a-z]{1,10}"
        ) {
            let path_str = format!("/{}/source.rs", dir);
            let path = Path::new(&path_str);
            prop_assert!(
                !is_source_file(path),
                "Files in {} directory should not be source files",
                dir
            );
        }

        #[test]
        fn prop_format_churn_markdown_always_returns_string(
            period_days in 1u32..365,
            total_commits in 0usize..10000
        ) {
            let summary = ChurnSummary {
                total_commits,
                total_files_changed: 0,
                hotspot_files: vec![],
                stable_files: vec![],
                author_contributions: HashMap::new(),
                mean_churn_score: 0.0,
                variance_churn_score: 0.0,
                stddev_churn_score: 0.0,
            };

            let analysis = CodeChurnAnalysis {
                generated_at: Utc::now(),
                period_days,
                repository_root: PathBuf::from("/test"),
                files: vec![],
                summary,
            };

            let result = format_churn_markdown(&analysis);
            prop_assert!(result.is_ok(), "format_churn_markdown should always succeed");

            let output = result.unwrap();
            prop_assert!(!output.is_empty(), "Output should not be empty");
            prop_assert!(
                output.contains("# Code Churn Analysis Report"),
                "Output should contain header"
            );
        }

        #[test]
        fn prop_summary_table_contains_accurate_counts(
            commits in 0usize..10000,
            files in 0usize..1000,
            hotspots in 0usize..50,
            stable in 0usize..50,
            authors in 0usize..100
        ) {
            let mut author_contributions = HashMap::new();
            for i in 0..authors {
                author_contributions.insert(format!("author_{}", i), 10);
            }

            let summary = ChurnSummary {
                total_commits: commits,
                total_files_changed: files,
                hotspot_files: (0..hotspots).map(|i| PathBuf::from(format!("hot{}.rs", i))).collect(),
                stable_files: (0..stable).map(|i| PathBuf::from(format!("stable{}.rs", i))).collect(),
                author_contributions,
                mean_churn_score: 0.5,
                variance_churn_score: 0.1,
                stddev_churn_score: 0.316,
            };

            let mut output = String::new();
            write_markdown_summary_table(&mut output, &summary).unwrap();

            prop_assert!(
                output.contains(&format!("| Total Commits | {} |", commits)),
                "Output should contain correct commit count"
            );
            prop_assert!(
                output.contains(&format!("| Files Changed | {} |", files)),
                "Output should contain correct files count"
            );
            prop_assert!(
                output.contains(&format!("| Hotspot Files | {} |", hotspots)),
                "Output should contain correct hotspot count"
            );
            prop_assert!(
                output.contains(&format!("| Stable Files | {} |", stable)),
                "Output should contain correct stable count"
            );
            prop_assert!(
                output.contains(&format!("| Contributing Authors | {} |", authors)),
                "Output should contain correct author count"
            );
        }

        #[test]
        fn prop_file_extensions_preserved_in_output(
            ext in prop_oneof![
                Just("rs"),
                Just("js"),
                Just("ts"),
                Just("py"),
                Just("java"),
                Just("go")
            ]
        ) {
            let file_path = format!("src/file.{}", ext);
            let metrics = FileChurnMetrics {
                path: PathBuf::from(&file_path),
                relative_path: file_path.clone(),
                commit_count: 10,
                unique_authors: vec!["dev".to_string()],
                additions: 100,
                deletions: 50,
                churn_score: 0.5,
                last_modified: Utc::now(),
                first_seen: Utc::now(),
            };

            let summary = ChurnSummary {
                total_commits: 10,
                total_files_changed: 1,
                hotspot_files: vec![],
                stable_files: vec![],
                author_contributions: HashMap::new(),
                mean_churn_score: 0.5,
                variance_churn_score: 0.0,
                stddev_churn_score: 0.0,
            };

            let analysis = CodeChurnAnalysis {
                generated_at: Utc::now(),
                period_days: 30,
                repository_root: PathBuf::from("/test"),
                files: vec![metrics],
                summary,
            };

            let result = format_churn_markdown(&analysis).unwrap();
            prop_assert!(
                result.contains(&format!(".{}", ext)),
                "Output should contain the file extension .{}",
                ext
            );
        }
    }
}