pmat 3.16.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
    // === DemoRunner Tests ===

    #[tokio::test]
    async fn test_demo_runner_creation() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));
        assert!(runner.execution_log.is_empty());
    }

    #[test]
    fn test_demo_runner_build_mcp_request() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));

        let request = runner.build_mcp_request("test_method", json!({"param1": "value1"}));

        assert_eq!(request.jsonrpc, "2.0");
        assert_eq!(request.method, "tools/call");
        assert!(request.params.is_some());
        let params = request.params.as_ref().unwrap();
        assert_eq!(params["name"], "test_method");
    }

    #[test]
    fn test_demo_runner_generate_system_diagram() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));

        let diagram = runner.generate_system_diagram(&[]).unwrap();
        assert!(diagram.contains("graph TD"));
        assert!(diagram.contains("AST Context Analysis"));
        assert!(diagram.contains("File Parser"));
        assert!(diagram.contains("Rust AST"));
        assert!(diagram.contains("style A fill:#90EE90"));
    }

    #[test]
    fn test_demo_runner_render_system_mermaid() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));

        let components = HashMap::new();
        let mermaid = runner.render_system_mermaid(&components).unwrap();

        assert!(mermaid.starts_with("graph TD"));
        assert!(mermaid.contains("AST Context Analysis"));
        assert!(mermaid.contains("TypeScript AST"));
        assert!(mermaid.contains("Python AST"));
        assert!(mermaid.contains("Code Complexity"));
        assert!(mermaid.contains("DAG Generation"));
        assert!(mermaid.contains("Code Churn"));
        assert!(mermaid.contains("Git Analysis"));
        assert!(mermaid.contains("Template Generation"));
    }

    #[test]
    fn test_demo_runner_create_demo_step_success() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));

        let request = McpRequest {
            jsonrpc: "2.0".to_string(),
            id: json!("test"),
            method: "test".to_string(),
            params: None,
        };
        let response = McpResponse {
            jsonrpc: "2.0".to_string(),
            id: json!("test"),
            result: Some(json!({"success": true})),
            error: None,
        };

        let step = runner.create_demo_step("Test Step", "Test Capability", request, response, 100);

        assert!(step.success);
        assert_eq!(step.name, "Test Step");
        assert_eq!(step.elapsed_ms, 100);
    }

    #[test]
    fn test_demo_runner_create_demo_step_error() {
        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let runner = DemoRunner::new(Arc::new(server));

        let request = McpRequest {
            jsonrpc: "2.0".to_string(),
            id: json!("test"),
            method: "test".to_string(),
            params: None,
        };
        let response = McpResponse {
            jsonrpc: "2.0".to_string(),
            id: json!("test"),
            result: None,
            error: Some(crate::models::mcp::McpError {
                code: -32600,
                message: "Test error".to_string(),
                data: None,
            }),
        };

        let step = runner.create_demo_step("Error Step", "Error Capability", request, response, 50);

        assert!(!step.success);
        assert!(step.output.is_some());
        let output = step.output.unwrap();
        assert!(output["error"].as_str().unwrap().contains("Test error"));
    }

    // === DemoReport render_step_highlights Tests ===

    #[test]
    fn test_render_step_highlights_complexity() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({
            "total_functions": 50,
            "total_warnings": 5,
            "total_errors": 2
        });

        report.render_step_highlights(&mut output, "Code Complexity Analysis", &result);
        assert!(output.contains("Functions: 50"));
        assert!(output.contains("Warnings: 5"));
        assert!(output.contains("Errors: 2"));
    }

    #[test]
    fn test_render_step_highlights_dag() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({
            "stats": {
                "nodes": 25,
                "edges": 40
            }
        });

        report.render_step_highlights(&mut output, "DAG Visualization", &result);
        assert!(output.contains("25 nodes"));
        assert!(output.contains("40 edges"));
    }

    #[test]
    fn test_render_step_highlights_churn() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({
            "files_analyzed": 30,
            "total_churn_score": 150
        });

        report.render_step_highlights(&mut output, "Code Churn Analysis", &result);
        assert!(output.contains("30"));
        assert!(output.contains("150"));
    }

    #[test]
    fn test_render_step_highlights_architecture() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({
            "metadata": {
                "nodes": 10,
                "edges": 15
            }
        });

        report.render_step_highlights(&mut output, "System Architecture Analysis", &result);
        assert!(output.contains("Components: 10"));
        assert!(output.contains("Relationships: 15"));
    }

    #[test]
    fn test_render_step_highlights_defects() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({
            "high_risk_files": ["file1.rs", "file2.rs"],
            "average_probability": 0.35
        });

        report.render_step_highlights(&mut output, "Defect Probability Analysis", &result);
        assert!(output.contains("High-risk files: 2"));
        assert!(output.contains("0.35"));
    }

    #[test]
    fn test_render_step_highlights_unknown_capability() {
        let report = DemoReport {
            repository: "/test".to_string(),
            total_time_ms: 100,
            steps: vec![],
            system_diagram: None,
            analysis: DemoAnalysisResult {
                files_analyzed: 0,
                functions_analyzed: 0,
                avg_complexity: 0.0,
                hotspot_functions: 0,
                quality_score: 0.0,
                tech_debt_hours: 0,
                qa_verification: None,
                language_stats: None,
                complexity_metrics: None,
            },
            execution_time_ms: 100,
        };

        let mut output = String::new();
        let result = json!({"some": "data"});

        report.render_step_highlights(&mut output, "Unknown Capability", &result);
        // Should not add anything for unknown capabilities
        assert!(output.is_empty());
    }


    // === Additional DemoRunner Tests ===

    #[tokio::test]
    async fn test_demo_runner_execute_with_local_repo() {
        let temp_dir = TempDir::new().unwrap();
        std::fs::create_dir(temp_dir.path().join(".git")).unwrap();

        // Create a simple Rust file for analysis
        let src_dir = temp_dir.path().join("src");
        std::fs::create_dir(&src_dir).unwrap();
        std::fs::write(
            src_dir.join("lib.rs"),
            r#"
            /// Hello.
            pub fn hello() -> &'static str {
                "hello"
            }
            "#,
        )
        .unwrap();

        // Create Cargo.toml
        std::fs::write(
            temp_dir.path().join("Cargo.toml"),
            r#"
[package]
name = "test-project"
version = "0.1.0"
edition = "2021"
"#,
        )
        .unwrap();

        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let mut runner = DemoRunner::new(Arc::new(server));

        let result = runner.execute(temp_dir.path().to_path_buf()).await;
        // The demo should run, though analysis may partially fail on minimal project
        // We're testing that it doesn't panic
        assert!(result.is_ok() || result.is_err());
    }

    #[tokio::test]
    async fn test_demo_runner_execute_with_diagram_local() {
        let temp_dir = TempDir::new().unwrap();
        std::fs::create_dir(temp_dir.path().join(".git")).unwrap();

        let server = crate::stateless_server::StatelessTemplateServer::new().unwrap();
        let mut runner = DemoRunner::new(Arc::new(server));

        // Test execute_with_diagram with local path and no URL
        let result = runner.execute_with_diagram(temp_dir.path(), None).await;
        // Should run without panicking
        assert!(result.is_ok() || result.is_err());
    }