pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// Tests for MCP server: property tests and coverage tests.
// Included by server.rs - shares parent module scope (no `use` imports here).

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn basic_property_stability(_input in ".*") {
            // Basic property test for coverage
            prop_assert!(true);
        }

        #[test]
        fn module_consistency_check(_x in 0u32..1000) {
            // Module consistency verification
            prop_assert!(_x < 1001);
        }
    }
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod coverage_tests {
    use super::*;
    use serde_json::json;

    // Test McpServer creation and initialization
    #[test]
    fn test_mcp_server_new_creates_valid_instance() {
        let server = McpServer::new();
        // Server should be created with default state manager
        assert!(Arc::strong_count(&server.state_manager) >= 1);
        assert!(Arc::strong_count(&server.cache) >= 1);
    }

    #[test]
    fn test_mcp_server_default_trait() {
        let server1 = McpServer::new();
        let server2 = McpServer::default();
        // Both should create valid instances
        assert!(Arc::strong_count(&server1.state_manager) >= 1);
        assert!(Arc::strong_count(&server2.state_manager) >= 1);
    }

    // Test handle_request with various JSON-RPC requests
    #[tokio::test]
    async fn test_handle_request_initialize() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert_eq!(response.jsonrpc, "2.0");
        assert!(response.result.is_some());
        assert!(response.error.is_none());

        let result = response.result.unwrap();
        assert_eq!(result["protocolVersion"], "2024-11-05");
        assert!(result["capabilities"]["refactor"]["start"]
            .as_bool()
            .unwrap());
    }

    #[tokio::test]
    async fn test_handle_request_invalid_jsonrpc_version() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"1.0","id":1,"method":"initialize","params":{}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert!(response.error.is_some());
        assert_eq!(response.error.as_ref().unwrap().code, -32600);
        assert!(response
            .error
            .as_ref()
            .unwrap()
            .message
            .contains("Invalid JSON-RPC version"));
    }

    #[tokio::test]
    async fn test_handle_request_method_not_found() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"unknown.method","params":{}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert!(response.error.is_some());
        assert_eq!(response.error.as_ref().unwrap().code, -32601);
        assert!(response
            .error
            .as_ref()
            .unwrap()
            .message
            .contains("Method not found"));
    }

    #[tokio::test]
    async fn test_handle_request_refactor_get_state_no_session() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"refactor.getState","params":{}}"#;
        let result = server.handle_request(request).await;
        // Should return error when no session active
        assert!(result.is_err() || result.as_ref().map(|r| r.error.is_some()).unwrap_or(true));
    }

    #[tokio::test]
    #[ignore] // Flaky - requires specific environment
    async fn test_handle_request_refactor_start() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"refactor.start","params":{"targets":["/tmp/test.rs"],"config":{}}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert_eq!(response.jsonrpc, "2.0");
        assert!(response.result.is_some());
    }

    #[tokio::test]
    async fn test_handle_request_refactor_stop_no_session() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"refactor.stop","params":{}}"#;
        let result = server.handle_request(request).await;
        // Should return error when no session to stop
        assert!(result.is_err() || result.as_ref().map(|r| r.error.is_some()).unwrap_or(true));
    }

    #[tokio::test]
    async fn test_handle_request_invalid_json() {
        let server = McpServer::new();
        let request = r#"{"invalid json"#;
        let result = server.handle_request(request).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_handle_request_refactor_next_iteration_no_session() {
        let server = McpServer::new();
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"refactor.nextIteration","params":{}}"#;
        let result = server.handle_request(request).await;
        // Should return error when no session active
        assert!(result.is_err() || result.as_ref().map(|r| r.error.is_some()).unwrap_or(true));
    }

    // Test cache integration
    #[tokio::test]
    async fn test_cache_metrics_initial_state() {
        let server = McpServer::new();
        let metrics = server.cache_metrics().await;

        assert!(metrics.contains("Cache Metrics"));
        assert!(metrics.contains("Hits:"));
        assert!(metrics.contains("Misses:"));
        assert!(metrics.contains("Hit Ratio:"));
    }

    #[tokio::test]
    async fn test_cache_hit_for_initialize() {
        let server = McpServer::new();

        // First call - cache miss
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#;
        let _response1 = server.handle_request(request).await.unwrap();

        // Second call with same params - cache hit
        let _response2 = server.handle_request(request).await.unwrap();

        let metrics = server.cache_metrics().await;
        // Should have at least one hit
        assert!(metrics.contains("Hit"));
    }

    // Test JSON-RPC ID preservation
    #[tokio::test]
    async fn test_response_preserves_request_id() {
        let server = McpServer::new();

        let request = r#"{"jsonrpc":"2.0","id":42,"method":"initialize","params":{}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert_eq!(response.id, json!(42));
    }

    #[tokio::test]
    async fn test_response_preserves_string_id() {
        let server = McpServer::new();

        let request = r#"{"jsonrpc":"2.0","id":"request-123","method":"initialize","params":{}}"#;
        let response = server.handle_request(request).await.unwrap();

        assert_eq!(response.id, json!("request-123"));
    }

    // Test caching behavior for read-only operations
    #[tokio::test]
    async fn test_initialize_is_cached() {
        let server = McpServer::new();

        // Make first request
        let request = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#;
        let _response1 = server.handle_request(request).await.unwrap();

        // Make same request again
        let _response2 = server.handle_request(request).await.unwrap();

        // Cache should have been used
        let size = server.cache.size().await;
        assert!(size >= 1);
    }
}