pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
#![cfg_attr(coverage_nightly, coverage(off))]
use crate::handlers;
use crate::models::mcp::McpRequest;
use crate::stateless_server::StatelessTemplateServer;
use std::process::Command;
use std::sync::Arc;

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

    // Test 1: Valid initialize request
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(1),
        method: "initialize".to_string(),
        params: Some(serde_json::json!({
            "protocolVersion": "2024-11-05",
            "capabilities": {},
            "clientInfo": {"name": "test", "version": "1.0"}
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some());
    assert!(response.error.is_none());

    // Test 2: List tools
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(2),
        method: "tools/list".to_string(),
        params: Some(serde_json::json!({})),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some());
    let result = response.result.unwrap();
    let tools = result["tools"].as_array().unwrap();
    assert!(!tools.is_empty());

    // Test 3: List resources
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(3),
        method: "resources/list".to_string(),
        params: Some(serde_json::json!({})),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some());

    // Test 4: Generate template
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(4),
        method: "tools/call".to_string(),
        params: Some(serde_json::json!({
            "name": "generate_template",
            "arguments": {
                "resource_uri": "template://makefile/rust/cli",
                "parameters": {"project_name": "test_project", "has_tests": true}
            }
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 5: Invalid method
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(5),
        method: "invalid/method".to_string(),
        params: None,
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.error.is_some());
    assert_eq!(response.error.unwrap().code, -32601);

    // Test 6: More tool calls for coverage
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(6),
        method: "tools/call".to_string(),
        params: Some(serde_json::json!({
            "name": "list_templates",
            "arguments": {}
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 7: Search templates
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(7),
        method: "tools/call".to_string(),
        params: Some(serde_json::json!({
            "name": "search_templates",
            "arguments": {"query": "rust"}
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 8: Validate template
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(8),
        method: "tools/call".to_string(),
        params: Some(serde_json::json!({
            "name": "validate_template",
            "arguments": {
                "resource_uri": "template://readme/rust/cli",
                "parameters": {"project_name": "test", "author": "Test"}
            }
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 9: Scaffold project
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(9),
        method: "tools/call".to_string(),
        params: Some(serde_json::json!({
            "name": "scaffold_project",
            "arguments": {
                "toolchain": "rust",
                "parameters": {"project_name": "test", "author": "Test", "version": "0.1.0"}
            }
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 10: Read resource
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(10),
        method: "resources/read".to_string(),
        params: Some(serde_json::json!({
            "uri": "template://gitignore/rust/cli"
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());

    // Test 11: List prompts
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(11),
        method: "prompts/list".to_string(),
        params: Some(serde_json::json!({})),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some());

    // Test 12: Get prompt
    let request = McpRequest {
        jsonrpc: "2.0".to_string(),
        id: serde_json::json!(12),
        method: "prompts/get".to_string(),
        params: Some(serde_json::json!({
            "name": "rust_project",
            "arguments": {"project_name": "test"}
        })),
    };
    let response = handlers::handle_request(server.clone(), request).await;
    assert!(response.result.is_some() || response.error.is_some());
}

#[test]
#[ignore] // Flaky in coverage run
#[serial_test::serial]
fn test_cli_main_binary_version() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "--version"])
        .output()
        .expect("Failed to execute command");

    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("pmat"));

    // Read the actual version from Cargo.toml instead of hardcoding
    let expected_version = env!("CARGO_PKG_VERSION");
    assert!(
        stdout.contains(expected_version),
        "Binary version output '{}' should contain expected version '{}'",
        stdout.trim(),
        expected_version
    );
}

#[test]
#[ignore = "e2e test - requires binary build"]
// Requires pmat binary compilation (E2E test) - Sprint 45 Phase 2
// Run manually: cargo build --bin pmat && cargo test test_cli_main_binary_help -- --ignored
#[serial_test::serial]
fn test_cli_main_binary_help() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "--help"])
        .output()
        .expect("Failed to execute command");

    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("Professional project quantitative scaffolding and analysis toolkit"));
    assert!(stdout.contains("Commands:"));
    assert!(stdout.contains("generate"));
    assert!(stdout.contains("scaffold"));
    assert!(stdout.contains("list"));
    assert!(stdout.contains("search"));
}

#[test]
#[ignore] // Flaky in coverage run
fn test_cli_subcommand_help() {
    // Test generate help
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "generate", "--help"])
        .output()
        .expect("Failed to execute command");

    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("Generate a single template"));

    // Test list help
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "list", "--help"])
        .output()
        .expect("Failed to execute command");

    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("List available templates"));
}

/// SLOW: >240s - excluded from fast test suite
#[test]
// Re-enabled: test passes
fn test_cli_mode_list_templates() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "--mode", "cli", "list"])
        .output()
        .expect("Failed to execute command");

    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);

    // Should either succeed with template list or fail with a clear error
    assert!(
        stdout.contains("Template")
            || stdout.contains("makefile")
            || stderr.contains("error")
            || output.status.success()
    );
}

/// SLOW: >240s - excluded from fast test suite
#[test]
// Re-enabled: test passes
fn test_cli_generate_validation_error() {
    let output = Command::new("cargo")
        .args([
            "run", "--bin", "pmat", "--", "generate", "makefile",
            "rust/cli",
            // Missing required project_name parameter - this should fail
        ])
        .output()
        .expect("Failed to execute command");

    // Should fail due to missing required parameter
    let stderr = String::from_utf8_lossy(&output.stderr);
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        !output.status.success()
            || stderr.contains("error")
            || stderr.contains("Missing required parameter")
            || stdout.contains("error")
            || stdout.contains("Missing required parameter")
    );
}

/// SLOW: >240s - excluded from fast test suite
#[test]
// Re-enabled: test passes
fn test_cli_search_templates() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "search", "rust"])
        .output()
        .expect("Failed to execute command");

    if output.status.success() {
        let stdout = String::from_utf8_lossy(&output.stdout);
        // Should find rust templates
        assert!(stdout.contains("rust") || stdout.contains("Rust"));
    }
}

/// SLOW: >240s - excluded from fast test suite
#[test]
#[ignore] // SLOW: >240s - triggers full cargo build, times out in kaizen
fn test_cli_invalid_command() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "invalid-command"])
        .output()
        .expect("Failed to execute command");

    assert!(!output.status.success());
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(stderr.contains("unrecognized subcommand") || stderr.contains("error"));
}

#[test]
#[ignore] // Flaky in coverage run
#[serial_test::serial]
fn test_cli_analyze_churn() {
    let output = Command::new("cargo")
        .args(["run", "--bin", "pmat", "--", "analyze", "churn", "--help"])
        .output()
        .expect("Failed to execute command");

    assert!(output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("churn") || stdout.contains("Analyze"));
}