pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
use pmat::services::complexity::*;
use pmat::stateless_server::StatelessTemplateServer;
use pmat::TemplateServerTrait;
use std::path::Path;

#[cfg(test)]
mod coverage_improvement {
    use super::*;

    #[test]
    fn test_complexity_metrics_creation() {
        let metrics = ComplexityMetrics::new(5, 7, 3, 25);

        assert_eq!(metrics.cyclomatic, 5);
        assert_eq!(metrics.cognitive, 7);
        assert_eq!(metrics.nesting_max, 3);
        assert_eq!(metrics.lines, 25);
    }

    #[test]
    fn test_complexity_metrics_default() {
        let metrics = ComplexityMetrics::default();

        assert_eq!(metrics.cyclomatic, 0);
        assert_eq!(metrics.cognitive, 0);
        assert_eq!(metrics.nesting_max, 0);
        assert_eq!(metrics.lines, 0);
    }

    #[test]
    fn test_function_complexity_creation() {
        let function = FunctionComplexity {
            name: "test_function".to_string(),
            line_start: 10,
            line_end: 20,
            metrics: ComplexityMetrics::new(3, 4, 2, 11),
        };

        assert_eq!(function.name, "test_function");
        assert_eq!(function.line_start, 10);
        assert_eq!(function.line_end, 20);
        assert_eq!(function.metrics.cyclomatic, 3);
    }

    #[test]
    fn test_class_complexity_creation() {
        let class = ClassComplexity {
            name: "TestClass".to_string(),
            line_start: 1,
            line_end: 50,
            metrics: ComplexityMetrics::default(),
            methods: vec![],
        };

        assert_eq!(class.name, "TestClass");
        assert!(class.methods.is_empty());
    }

    #[test]
    fn test_file_complexity_metrics_creation() {
        let file_metrics = FileComplexityMetrics {
            path: "test.rs".to_string(),
            total_complexity: ComplexityMetrics::default(),
            functions: vec![],
            classes: vec![],
        };

        assert_eq!(file_metrics.path, "test.rs");
        assert!(file_metrics.functions.is_empty());
        assert!(file_metrics.classes.is_empty());
    }

    #[test]
    fn test_compute_complexity_cache_key() {
        let path = Path::new("test.rs");
        let content = b"fn main() { println!(\"Hello\"); }";

        let key1 = compute_complexity_cache_key(path, content);
        let key2 = compute_complexity_cache_key(path, content);

        // Same content should produce same key
        assert_eq!(key1, key2);

        // Different content should produce different key
        let different_content = b"fn main() { println!(\"World\"); }";
        let key3 = compute_complexity_cache_key(path, different_content);
        assert_ne!(key1, key3);
    }

    #[test]
    fn test_aggregate_results_empty() {
        let file_metrics: Vec<FileComplexityMetrics> = vec![];
        let report = aggregate_results(file_metrics);

        // Should handle empty input gracefully
        assert!(format!("{report:?}").contains("ComplexityReport"));
    }

    #[test]
    fn test_aggregate_results_with_data() {
        let file_metrics = vec![FileComplexityMetrics {
            path: "test1.rs".to_string(),
            total_complexity: ComplexityMetrics::new(5, 7, 2, 20),
            functions: vec![FunctionComplexity {
                name: "func1".to_string(),
                line_start: 1,
                line_end: 10,
                metrics: ComplexityMetrics::new(3, 4, 2, 10),
            }],
            classes: vec![],
        }];

        let report = aggregate_results(file_metrics);

        // Should create valid report
        assert!(format!("{report:?}").contains("ComplexityReport"));
    }

    #[test]
    fn test_format_complexity_summary() {
        let file_metrics = vec![FileComplexityMetrics {
            path: "test.rs".to_string(),
            total_complexity: ComplexityMetrics::new(5, 7, 2, 20),
            functions: vec![],
            classes: vec![],
        }];

        let report = aggregate_results(file_metrics);
        let summary = format_complexity_summary(&report);

        assert!(!summary.is_empty());
        assert!(summary.contains("complexity") || summary.contains("Complexity"));
    }

    #[test]
    fn test_format_complexity_report() {
        let file_metrics = vec![FileComplexityMetrics {
            path: "test.rs".to_string(),
            total_complexity: ComplexityMetrics::new(10, 15, 4, 50),
            functions: vec![FunctionComplexity {
                name: "complex_function".to_string(),
                line_start: 1,
                line_end: 25,
                metrics: ComplexityMetrics::new(8, 12, 4, 25),
            }],
            classes: vec![],
        }];

        let report = aggregate_results(file_metrics);
        let formatted = format_complexity_report(&report);

        assert!(!formatted.is_empty());
        // Just verify we got some report content
        assert!(formatted.len() > 10);
    }

    #[test]
    fn test_format_as_sarif() {
        let file_metrics = vec![FileComplexityMetrics {
            path: "test.rs".to_string(),
            total_complexity: ComplexityMetrics::new(15, 20, 5, 100),
            functions: vec![],
            classes: vec![],
        }];

        let report = aggregate_results(file_metrics);
        let sarif_result = format_as_sarif(&report);

        assert!(sarif_result.is_ok());
        let sarif_json = sarif_result.unwrap();

        // Should be valid JSON
        let parsed: serde_json::Value = serde_json::from_str(&sarif_json).unwrap();
        assert!(parsed.is_object());
    }

    #[test]
    fn test_stateless_template_server_creation() {
        let result = StatelessTemplateServer::new();
        assert!(result.is_ok());

        let _server = result.unwrap();
        // Just test that it can be created
    }

    #[tokio::test]
    async fn test_stateless_template_server_basic_operations() {
        let server = StatelessTemplateServer::new().unwrap();

        // Test getting renderer
        let _renderer = server.get_renderer();

        // Test cache methods (should return None for stateless server)
        assert!(server.get_metadata_cache().is_none());
        assert!(server.get_content_cache().is_none());
        assert!(server.get_s3_client().is_none());
        assert!(server.get_bucket_name().is_none());
    }

    #[test]
    fn test_various_helper_functions() {
        use minijinja::context;
        use pmat::utils::helpers::register_helpers;

        let mut env = minijinja::Environment::new();
        register_helpers(&mut env);

        // Test snake_case with various inputs
        let test_cases = vec![
            ("MyProjectName", "my_project_name"),
            ("camelCase", "camel_case"),
            ("already_snake", "already_snake"),
            ("UPPER_CASE", "upper__case"),
        ];

        for (input, expected) in test_cases {
            let tmpl = env.template_from_str("{{ name|snake_case }}").unwrap();
            let result = tmpl.render(context! { name => input }).unwrap();
            assert_eq!(result, expected);
        }

        // Test current year and date helpers
        let tmpl = env.template_from_str("{{ current_year() }}").unwrap();
        let year_result = tmpl.render(context! {}).unwrap();
        let year: u32 = year_result.parse().expect("Should be valid year");
        assert!((2024..=2100).contains(&year));

        let tmpl = env.template_from_str("{{ current_date() }}").unwrap();
        let date_result = tmpl.render(context! {}).unwrap();
        assert_eq!(date_result.len(), 10); // YYYY-MM-DD format
    }

    #[test]
    fn test_error_handling_coverage() {
        use pmat::models::error::*;

        // Test various error types to improve coverage
        let template_error = TemplateError::NotFound("test".to_string());
        assert!(format!("{template_error}").contains("test"));

        let validation_error = TemplateError::ValidationError {
            parameter: "test_param".to_string(),
            reason: "error1".to_string(),
        };
        assert!(format!("{validation_error}").contains("test_param"));

        let invalid_uri_error = TemplateError::InvalidUri {
            uri: "invalid://test".to_string(),
        };
        assert!(format!("{invalid_uri_error}").contains("invalid://test"));

        // Test debug formatting
        assert!(!format!("{template_error:?}").is_empty());
        assert!(!format!("{validation_error:?}").is_empty());

        // Test MCP error codes
        assert_eq!(template_error.to_mcp_code(), -32000);
        assert_eq!(validation_error.to_mcp_code(), -32003);
        assert_eq!(invalid_uri_error.to_mcp_code(), -32002);
    }
}