pmat 3.15.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
    // Helper Function Tests - convert_deep_context_dag_type

    #[test]
    fn test_convert_dag_type_call_graph() {
        let result = convert_deep_context_dag_type(DeepContextDagType::CallGraph);
        assert!(matches!(result, DagType::CallGraph));
    }

    #[test]
    fn test_convert_dag_type_import_graph() {
        let result = convert_deep_context_dag_type(DeepContextDagType::ImportGraph);
        assert!(matches!(result, DagType::ImportGraph));
    }

    #[test]
    fn test_convert_dag_type_inheritance() {
        let result = convert_deep_context_dag_type(DeepContextDagType::Inheritance);
        assert!(matches!(result, DagType::Inheritance));
    }

    #[test]
    fn test_convert_dag_type_full_dependency() {
        let result = convert_deep_context_dag_type(DeepContextDagType::FullDependency);
        assert!(matches!(result, DagType::FullDependency));
    }

    // Helper Function Tests - convert_cache_strategy

    #[test]
    fn test_convert_cache_strategy_normal() {
        let result = convert_cache_strategy(DeepContextCacheStrategy::Normal);
        assert_eq!(result, "normal");
    }

    #[test]
    fn test_convert_cache_strategy_force_refresh() {
        let result = convert_cache_strategy(DeepContextCacheStrategy::ForceRefresh);
        assert_eq!(result, "force-refresh");
    }

    #[test]
    fn test_convert_cache_strategy_offline() {
        let result = convert_cache_strategy(DeepContextCacheStrategy::Offline);
        assert_eq!(result, "offline");
    }

    // Helper Function Tests - get_top_violations

    #[test]
    fn test_get_top_violations_empty() {
        let violations: Vec<crate::entropy::violation_detector::ActionableViolation> = vec![];
        let result = get_top_violations(&violations, 5);
        assert!(result.is_empty());
    }

    #[test]
    fn test_get_top_violations_zero_limit() {
        let violations: Vec<crate::entropy::violation_detector::ActionableViolation> = vec![];
        let result = get_top_violations(&violations, 0);
        assert!(result.is_empty());
    }

    // Helper Function Tests - format_violation_list

    #[test]
    fn test_format_violation_list_empty() {
        let violations: Vec<crate::entropy::violation_detector::ActionableViolation> = vec![];
        let result = format_violation_list(&violations);
        assert!(result.is_empty());
    }

    // Helper Function Tests - format_markdown_violations

    #[test]
    fn test_format_markdown_violations_empty() {
        let violations: Vec<crate::entropy::violation_detector::ActionableViolation> = vec![];
        let result = format_markdown_violations(&violations, 10);
        assert!(result.is_empty());
    }

    #[test]
    fn test_format_markdown_violations_zero_max() {
        let violations: Vec<crate::entropy::violation_detector::ActionableViolation> = vec![];
        let result = format_markdown_violations(&violations, 0);
        assert!(result.is_empty());
    }

    // Helper Function Tests - output_entropy_results

    #[test]
    fn test_output_entropy_results_stdout() {
        let result = output_entropy_results(None, "test content");
        assert!(result.is_ok());
    }

    #[test]
    fn test_output_entropy_results_to_file() {
        let temp_dir = tempfile::tempdir().unwrap();
        let output_path = temp_dir.path().join("test_output.txt");
        let result = output_entropy_results(Some(output_path.clone()), "test content");
        assert!(result.is_ok());
        assert!(output_path.exists());
        let content = std::fs::read_to_string(output_path).unwrap();
        assert_eq!(content, "test content");
    }

    // Helper Function Tests - create_entropy_config

    #[test]
    fn test_create_entropy_config_low_severity() {
        use crate::cli::EntropySeverity;
        use crate::entropy::violation_detector::Severity;
        let config = create_entropy_config(EntropySeverity::Low, true);
        assert!(matches!(config.min_severity, Severity::Low));
        // Default has tests/** and examples/** - include_tests=true doesn't add more
        assert_eq!(config.exclude_paths.len(), 2);
    }

    #[test]
    fn test_create_entropy_config_medium_severity() {
        use crate::cli::EntropySeverity;
        use crate::entropy::violation_detector::Severity;
        let config = create_entropy_config(EntropySeverity::Medium, true);
        assert!(matches!(config.min_severity, Severity::Medium));
    }

    #[test]
    fn test_create_entropy_config_high_severity() {
        use crate::cli::EntropySeverity;
        use crate::entropy::violation_detector::Severity;
        let config = create_entropy_config(EntropySeverity::High, true);
        assert!(matches!(config.min_severity, Severity::High));
    }

    #[test]
    fn test_create_entropy_config_exclude_tests() {
        use crate::cli::EntropySeverity;
        let config = create_entropy_config(EntropySeverity::Low, false);
        // Default has 2, plus exclude_tests adds 2 more
        assert!(config.exclude_paths.len() >= 2);
        assert!(config.exclude_paths.contains(&"**/*test*.rs".to_string()));
        assert!(config.exclude_paths.contains(&"tests/**".to_string()));
    }

    // Enhanced Entropy Tests with real ActionableViolation

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

    fn create_test_violation(
        message: &str,
        loc_reduction: usize,
    ) -> crate::entropy::violation_detector::ActionableViolation {
        use crate::entropy::pattern_extractor::PatternType;
        use crate::entropy::violation_detector::{ActionableViolation, PatternSummary, Severity};
        use std::path::PathBuf;

        ActionableViolation {
            severity: Severity::High,
            pattern: PatternSummary {
                pattern_type: PatternType::ErrorHandling,
                repetitions: 5,
                variation_score: 0.1,
                example_code: "fn example() {}".to_string(),
            },
            message: message.to_string(),
            fix_suggestion: "Extract into function".to_string(),
            estimated_loc_reduction: loc_reduction,
            affected_files: vec![PathBuf::from("src/test.rs")],
            priority_score: 0.9,
        }
    }

    #[test]
    fn test_get_top_violations_with_real_data() {
        let v1 = create_test_violation("First", 10);
        let v2 = create_test_violation("Second", 20);
        let v3 = create_test_violation("Third", 30);
        let violations = vec![v1, v2, v3];

        let result = get_top_violations(&violations, 2);
        assert_eq!(result.len(), 2);
        assert_eq!(result[0].message, "First");
        assert_eq!(result[1].message, "Second");
    }

    #[test]
    fn test_get_top_violations_limit_exceeds_size() {
        let v1 = create_test_violation("Only one", 10);
        let violations = vec![v1];

        let result = get_top_violations(&violations, 10);
        assert_eq!(result.len(), 1);
    }

    #[test]
    fn test_format_violation_list_with_real_data() {
        let violations = vec![
            create_test_violation("Repeated pattern found", 15),
            create_test_violation("Similar code detected", 25),
        ];

        let result = format_violation_list(&violations);
        let stripped = strip_ansi(&result);
        assert!(stripped.contains("1. "));
        assert!(stripped.contains("Repeated pattern found"));
        assert!(stripped.contains("15"));
        assert!(stripped.contains("2. "));
        assert!(stripped.contains("Similar code detected"));
        assert!(stripped.contains("25"));
        assert!(stripped.contains("Extract into function"));
    }

    #[test]
    fn test_format_markdown_violations_with_real_data() {
        let violations = vec![
            create_test_violation("Pattern A", 20),
            create_test_violation("Pattern B", 30),
        ];

        let result = format_markdown_violations(&violations, 2);
        assert!(result.contains("### Pattern A"));
        assert!(result.contains("### Pattern B"));
        assert!(result.contains("**Fix**: Extract into function"));
        assert!(result.contains("**LOC Reduction**: 20 lines"));
        assert!(result.contains("**Pattern**: ErrorHandling"));
    }

    #[test]
    fn test_format_markdown_violations_max_count() {
        let violations = vec![
            create_test_violation("First", 10),
            create_test_violation("Second", 20),
            create_test_violation("Third", 30),
        ];

        let result = format_markdown_violations(&violations, 1);
        assert!(result.contains("### First"));
        assert!(!result.contains("### Second"));
        assert!(!result.contains("### Third"));
    }

    #[test]
    fn test_format_violation_list_single_item() {
        let violations = vec![create_test_violation("Single violation", 50)];
        let result = format_violation_list(&violations);
        let stripped = strip_ansi(&result);
        assert!(stripped.contains("1. "));
        assert!(stripped.contains("Single violation"));
        assert!(stripped.contains("50"));
    }

    #[test]
    fn test_format_markdown_violations_take_zero() {
        let violations = vec![
            create_test_violation("First", 10),
            create_test_violation("Second", 20),
        ];
        // Max 0 should return empty because take(0) takes nothing
        let result = format_markdown_violations(&violations, 0);
        assert!(result.is_empty());
    }

    // Output entropy results tests

    #[test]
    fn test_output_entropy_results_empty_content() {
        let result = output_entropy_results(None, "");
        assert!(result.is_ok());
    }

    #[test]
    fn test_output_entropy_results_unicode_content() {
        let result = output_entropy_results(None, "测试内容 🎉");
        assert!(result.is_ok());
    }

    #[test]
    fn test_output_entropy_results_multiline() {
        let result = output_entropy_results(None, "Line 1\nLine 2\nLine 3");
        assert!(result.is_ok());
    }

    #[test]
    fn test_output_entropy_results_to_nested_directory() {
        let temp_dir = tempfile::tempdir().unwrap();
        let output_path = temp_dir.path().join("nested").join("output.txt");

        // Parent directory doesn't exist, should succeed by creating it
        std::fs::create_dir_all(output_path.parent().unwrap()).unwrap();

        let result = output_entropy_results(Some(output_path.clone()), "test");
        assert!(result.is_ok());
    }

    // DagType conversion tests - additional variants

    #[test]
    fn test_all_deep_context_dag_type_variants() {
        // Test all 4 variants
        let variants = [
            (DeepContextDagType::CallGraph, DagType::CallGraph),
            (DeepContextDagType::ImportGraph, DagType::ImportGraph),
            (DeepContextDagType::Inheritance, DagType::Inheritance),
            (DeepContextDagType::FullDependency, DagType::FullDependency),
        ];

        for (input, expected) in variants {
            let result = convert_deep_context_dag_type(input);
            assert!(
                matches!(result, ref e if std::mem::discriminant(&result) == std::mem::discriminant(e))
            );
            let _ = expected; // suppress unused warning
        }
    }

    // Cache strategy tests - exhaustive

    #[test]
    fn test_cache_strategy_all_variants() {
        assert_eq!(
            convert_cache_strategy(DeepContextCacheStrategy::Normal),
            "normal"
        );
        assert_eq!(
            convert_cache_strategy(DeepContextCacheStrategy::ForceRefresh),
            "force-refresh"
        );
        assert_eq!(
            convert_cache_strategy(DeepContextCacheStrategy::Offline),
            "offline"
        );
    }

    // Create entropy config with boundary conditions

    #[test]
    fn test_create_entropy_config_boundary_include_tests_true() {
        use crate::cli::EntropySeverity;
        let config = create_entropy_config(EntropySeverity::Low, true);
        // When include_tests is true, fewer exclusions
        assert!(config.exclude_paths.len() >= 2);
    }

    #[test]
    fn test_create_entropy_config_boundary_include_tests_false() {
        use crate::cli::EntropySeverity;
        let config = create_entropy_config(EntropySeverity::High, false);
        // When include_tests is false, more exclusions
        assert!(config.exclude_paths.iter().any(|p| p.contains("test")));
    }