pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
#![cfg_attr(coverage_nightly, coverage(off))]
// Additional tests for SimpleDeepContext - recommendations, output, extraction

use super::*;

mod tests_output {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    // ============ generate_recommendations Tests ============

    #[test]
    fn test_generate_recommendations_high_complexity() {
        let analyzer = SimpleDeepContext;
        let metrics = ComplexityMetrics {
            total_functions: 10,
            high_complexity_count: 3,
            avg_complexity: 4.0,
        };
        let recommendations = analyzer.generate_recommendations(&metrics);
        assert!(!recommendations.is_empty());
        assert!(recommendations
            .iter()
            .any(|r| r.contains("refactoring") && r.contains("3")));
    }

    #[test]
    fn test_generate_recommendations_high_avg_complexity() {
        let analyzer = SimpleDeepContext;
        let metrics = ComplexityMetrics {
            total_functions: 10,
            high_complexity_count: 0,
            avg_complexity: 7.5,
        };
        let recommendations = analyzer.generate_recommendations(&metrics);
        assert!(!recommendations.is_empty());
        assert!(recommendations
            .iter()
            .any(|r| r.contains("Average") && r.contains("7.5")));
    }

    #[test]
    fn test_generate_recommendations_no_functions() {
        let analyzer = SimpleDeepContext;
        let metrics = ComplexityMetrics {
            total_functions: 0,
            high_complexity_count: 0,
            avg_complexity: 0.0,
        };
        let recommendations = analyzer.generate_recommendations(&metrics);
        assert!(!recommendations.is_empty());
        assert!(recommendations
            .iter()
            .any(|r| r.contains("No functions detected")));
    }

    #[test]
    fn test_generate_recommendations_good_code() {
        let analyzer = SimpleDeepContext;
        let metrics = ComplexityMetrics {
            total_functions: 20,
            high_complexity_count: 0,
            avg_complexity: 2.5,
        };
        let recommendations = analyzer.generate_recommendations(&metrics);
        assert!(!recommendations.is_empty());
        assert!(recommendations.iter().any(|r| r.contains("looks good")));
    }

    // ============ format_as_json Tests ============

    #[test]
    fn test_format_as_json_empty_report() {
        let analyzer = SimpleDeepContext;
        let report = SimpleAnalysisReport {
            file_count: 0,
            analysis_duration: std::time::Duration::from_millis(100),
            complexity_metrics: ComplexityMetrics {
                total_functions: 0,
                high_complexity_count: 0,
                avg_complexity: 0.0,
            },
            recommendations: vec!["No files found".to_string()],
            file_complexity_details: vec![],
        };
        let json = analyzer.format_as_json(&report).unwrap();
        assert!(json.contains("\"file_count\": 0"));
        assert!(json.contains("\"total_functions\": 0"));
        assert!(json.contains("\"recommendations\""));
    }

    #[test]
    fn test_format_as_json_with_files() {
        let analyzer = SimpleDeepContext;
        let report = SimpleAnalysisReport {
            file_count: 2,
            analysis_duration: std::time::Duration::from_millis(500),
            complexity_metrics: ComplexityMetrics {
                total_functions: 15,
                high_complexity_count: 3,
                avg_complexity: 5.5,
            },
            recommendations: vec!["Consider refactoring".to_string()],
            file_complexity_details: vec![
                FileComplexityDetail {
                    file_path: PathBuf::from("src/main.rs"),
                    function_count: 10,
                    high_complexity_functions: 2,
                    avg_complexity: 6.0,
                    complexity_score: 8.0,
                    function_names: vec!["main".to_string()],
                },
                FileComplexityDetail {
                    file_path: PathBuf::from("src/lib.rs"),
                    function_count: 5,
                    high_complexity_functions: 1,
                    avg_complexity: 4.5,
                    complexity_score: 5.0,
                    function_names: vec!["process".to_string()],
                },
            ],
        };
        let json = analyzer.format_as_json(&report).unwrap();
        assert!(json.contains("\"file_count\": 2"));
        assert!(json.contains("\"total_functions\": 15"));
        assert!(json.contains("main.rs"));
        assert!(json.contains("lib.rs"));
        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed["summary"]["file_count"], 2);
    }

    // ============ format_as_markdown Tests ============

    #[test]
    fn test_format_as_markdown_empty_report() {
        let analyzer = SimpleDeepContext;
        let report = SimpleAnalysisReport {
            file_count: 0,
            analysis_duration: std::time::Duration::from_millis(50),
            complexity_metrics: ComplexityMetrics {
                total_functions: 0,
                high_complexity_count: 0,
                avg_complexity: 0.0,
            },
            recommendations: vec!["No files found".to_string()],
            file_complexity_details: vec![],
        };
        let markdown = analyzer.format_as_markdown(&report, 10);
        assert!(markdown.contains("# Deep Context Analysis Report"));
        assert!(markdown.contains("**Files Analyzed**: 0"));
        assert!(markdown.contains("## Recommendations"));
    }

    #[test]
    fn test_format_as_markdown_with_files() {
        let analyzer = SimpleDeepContext;
        let report = SimpleAnalysisReport {
            file_count: 3,
            analysis_duration: std::time::Duration::from_secs(1),
            complexity_metrics: ComplexityMetrics {
                total_functions: 30,
                high_complexity_count: 5,
                avg_complexity: 6.2,
            },
            recommendations: vec!["Consider refactoring".to_string(), "Add tests".to_string()],
            file_complexity_details: vec![
                FileComplexityDetail {
                    file_path: PathBuf::from("complex.rs"),
                    function_count: 15,
                    high_complexity_functions: 3,
                    avg_complexity: 8.0,
                    complexity_score: 12.0,
                    function_names: vec![],
                },
                FileComplexityDetail {
                    file_path: PathBuf::from("medium.rs"),
                    function_count: 10,
                    high_complexity_functions: 2,
                    avg_complexity: 5.0,
                    complexity_score: 7.0,
                    function_names: vec![],
                },
            ],
        };
        let markdown = analyzer.format_as_markdown(&report, 10);
        assert!(markdown.contains("**Files Analyzed**: 3"));
        assert!(markdown.contains("**Total Functions**: 30"));
        assert!(markdown.contains("**High Complexity Functions**: 5"));
        assert!(markdown.contains("## Top Files by Complexity"));
        let complex_pos = markdown.find("complex.rs").unwrap_or(usize::MAX);
        let medium_pos = markdown.find("medium.rs").unwrap_or(usize::MAX);
        assert!(complex_pos < medium_pos);
    }

    #[test]
    fn test_format_as_markdown_zero_top_files() {
        let analyzer = SimpleDeepContext;
        let report = SimpleAnalysisReport {
            file_count: 1,
            analysis_duration: std::time::Duration::from_millis(100),
            complexity_metrics: ComplexityMetrics {
                total_functions: 5,
                high_complexity_count: 1,
                avg_complexity: 3.0,
            },
            recommendations: vec![],
            file_complexity_details: vec![FileComplexityDetail {
                file_path: PathBuf::from("test.rs"),
                function_count: 5,
                high_complexity_functions: 1,
                avg_complexity: 3.0,
                complexity_score: 5.0,
                function_names: vec![],
            }],
        };
        let markdown = analyzer.format_as_markdown(&report, 0);
        assert!(markdown.contains("test.rs"));
    }

    // ============ extract_function_names Tests ============

    #[tokio::test]
    #[ignore] // Regex pattern issue - needs investigation
    async fn test_extract_function_names_rust() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();
        let test_file = temp_dir.path().join("test.rs");
        fs::write(
            &test_file,
            "fn main() {}\npub fn helper() -> i32 { 42 }\nasync fn async_func() {}\npub(crate) fn crate_visible() {}\n",
        )
        .unwrap();

        let names = analyzer
            .extract_function_names_heuristic(&test_file, "rs")
            .await
            .unwrap();
        assert!(names.contains(&"main".to_string()));
        assert!(names.contains(&"helper".to_string()));
    }

    #[tokio::test]
    async fn test_extract_function_names_python() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();
        let test_file = temp_dir.path().join("test.py");
        fs::write(
            &test_file,
            "def main():\n    pass\n\nasync def async_handler():\n    await something()\n\ndef helper_func(x, y):\n    return x + y\n",
        )
        .unwrap();

        let names = analyzer
            .extract_function_names_heuristic(&test_file, "py")
            .await
            .unwrap();
        assert!(names.contains(&"main".to_string()));
        assert!(names.contains(&"async_handler".to_string()));
        assert!(names.contains(&"helper_func".to_string()));
    }

    #[tokio::test]
    async fn test_extract_function_names_kotlin() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();
        let test_file = temp_dir.path().join("test.kt");
        fs::write(
            &test_file,
            "fun main() {\n    println(\"Hello\")\n}\n\nsuspend fun asyncOperation() {\n    delay(100)\n}\n\nfun processData(data: String): Int {\n    return data.length\n}\n",
        )
        .unwrap();

        let names = analyzer
            .extract_function_names_heuristic(&test_file, "kt")
            .await
            .unwrap();
        assert!(names.contains(&"main".to_string()));
        assert!(names.contains(&"asyncOperation".to_string()));
        assert!(names.contains(&"processData".to_string()));
    }

    #[tokio::test]
    async fn test_extract_function_names_go() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();
        let test_file = temp_dir.path().join("test.go");
        fs::write(
            &test_file,
            "func main() {\n    fmt.Println(\"hello\")\n}\n\nfunc (s *Server) HandleRequest() {\n    // method\n}\n\nfunc processData(data string) int {\n    return len(data)\n}\n",
        )
        .unwrap();

        let names = analyzer
            .extract_function_names_heuristic(&test_file, "go")
            .await
            .unwrap();
        assert!(names.contains(&"main".to_string()));
        assert!(names.contains(&"HandleRequest".to_string()));
        assert!(names.contains(&"processData".to_string()));
    }

    #[tokio::test]
    async fn test_extract_function_names_unknown_extension() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();
        let test_file = temp_dir.path().join("test.xyz");
        fs::write(&test_file, "some content").unwrap();

        let names = analyzer
            .extract_function_names_heuristic(&test_file, "xyz")
            .await
            .unwrap();
        assert!(names.is_empty());
    }

    // ============ analyze_complexity Integration Tests ============

    #[tokio::test]
    async fn test_analyze_complexity_multiple_files() {
        let analyzer = SimpleDeepContext;
        let temp_dir = TempDir::new().unwrap();

        let file1 = temp_dir.path().join("simple.rs");
        fs::write(&file1, "fn simple() { }").unwrap();

        let file2 = temp_dir.path().join("complex.rs");
        fs::write(
            &file2,
            "fn complex(x: i32) -> i32 {\n    if x > 0 {\n        if x > 10 {\n            for i in 0..x {\n                if i % 2 == 0 { return i; }\n            }\n        }\n    }\n    0\n}\n",
        )
        .unwrap();

        let files = vec![file1, file2];
        let (metrics, details) = analyzer.analyze_complexity(&files).await.unwrap();

        assert!(metrics.total_functions >= 2);
        assert_eq!(details.len(), 2);
    }

    // ============ SimpleAnalysisReport Tests ============

    #[test]
    fn test_simple_analysis_report_debug() {
        let report = SimpleAnalysisReport {
            file_count: 5,
            analysis_duration: std::time::Duration::from_millis(250),
            complexity_metrics: ComplexityMetrics {
                total_functions: 25,
                high_complexity_count: 3,
                avg_complexity: 4.5,
            },
            recommendations: vec!["Test recommendation".to_string()],
            file_complexity_details: vec![],
        };
        let debug = format!("{:?}", report);
        assert!(debug.contains("file_count"));
        assert!(debug.contains("5"));
        assert!(debug.contains("complexity_metrics"));
    }

    // ============ FileComplexityMetrics Tests ============

    #[test]
    fn test_file_complexity_metrics_debug() {
        use super::FileComplexityMetrics;

        let metrics = FileComplexityMetrics {
            function_count: 10,
            high_complexity_functions: 2,
            avg_complexity: 5.5,
            function_names: vec!["func1".to_string(), "func2".to_string()],
        };
        let debug = format!("{:?}", metrics);
        assert!(debug.contains("function_count"));
        assert!(debug.contains("10"));
        assert!(debug.contains("function_names"));
    }

    // ============ estimate_complexity Edge Cases ============

    #[test]
    fn test_estimate_complexity_unknown_language() {
        let analyzer = SimpleDeepContext;
        let code = "some random code with if and while";
        let complexity = analyzer.estimate_complexity(code, "unknown_lang");
        assert_eq!(complexity, 1);
    }

    #[test]
    fn test_estimate_complexity_empty_code() {
        let analyzer = SimpleDeepContext;
        let code = "";
        let complexity = analyzer.estimate_complexity(code, "py");
        assert_eq!(complexity, 1);
    }
}