pmat 3.30.1

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP)
Documentation
#![cfg(feature = "mutation-testing")]

//! GREEN Phase Tests for PMAT-070-002: JSON Parsing
//!
//! Tests updated to use real implementation from json_parser.rs
//!
//! Test Categories:
//! 1. JSON parsing (all outcomes)
//! 2. Edge cases (empty, invalid)
//! 3. Outcome conversion
//! 4. PMAT report conversion

use pmat::services::mutation::json_parser::{CargoMutantsReport, MutantOutcome};
use pmat::services::mutation::types::MutantStatus;
use std::path::PathBuf;

// ============================================================================
// RED PHASE TESTS - JSON Parsing
// ============================================================================

#[test]
fn test_parse_cargo_mutants_json_all_outcomes() {
    // RED Phase Test 1: Parse JSON with all 4 outcomes
    // Expected: Should deserialize correctly

    let json = r#"{
        "mutants": [
            {
                "outcome": "caught",
                "file": "src/lib.rs",
                "function": "add",
                "line": 10
            },
            {
                "outcome": "missed",
                "file": "src/lib.rs",
                "function": "subtract",
                "line": 15
            },
            {
                "outcome": "timeout",
                "file": "src/lib.rs",
                "function": "multiply",
                "line": 20
            },
            {
                "outcome": "unviable",
                "file": "src/lib.rs",
                "function": "divide",
                "line": 25
            }
        ]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse JSON");

    assert_eq!(report.mutants.len(), 4, "Should have 4 mutants");
    assert_eq!(report.mutants[0].outcome, MutantOutcome::Caught);
    assert_eq!(report.mutants[1].outcome, MutantOutcome::Missed);
    assert_eq!(report.mutants[2].outcome, MutantOutcome::Timeout);
    assert_eq!(report.mutants[3].outcome, MutantOutcome::Unviable);
}

#[test]
fn test_parse_empty_mutants_list() {
    // RED Phase Test 2: Parse JSON with empty mutants array
    // Expected: Should succeed with empty Vec

    let json = r#"{"mutants": []}"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse JSON");

    assert_eq!(report.mutants.len(), 0, "Should have 0 mutants");
}

#[test]
fn test_parse_invalid_json_returns_error() {
    // RED Phase Test 3: Parse malformed JSON
    // Expected: Should return Err, not panic

    let json = r#"{"mutants": [invalid json}"#;

    let result = CargoMutantsReport::from_json(json);

    assert!(result.is_err(), "Should return error for invalid JSON");
}

// ============================================================================
// RED PHASE TESTS - Outcome Conversion
// ============================================================================

#[test]
fn test_convert_caught_to_killed() {
    // RED Phase Test 4: Verify caught → Killed mapping
    // Expected: caught outcome becomes MutantStatus::Killed

    let json = r#"{
        "mutants": [{
            "outcome": "caught",
            "file": "src/lib.rs",
            "function": "test",
            "line": 1
        }]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(pmat_report.len(), 1, "Should have 1 mutant");
    assert_eq!(pmat_report[0].status, MutantStatus::Killed);
}

#[test]
fn test_convert_missed_to_survived() {
    // RED Phase Test 5: Verify missed → Survived mapping
    // Expected: missed outcome becomes MutantStatus::Survived

    let json = r#"{
        "mutants": [{
            "outcome": "missed",
            "file": "src/lib.rs",
            "function": "test",
            "line": 1
        }]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(pmat_report[0].status, MutantStatus::Survived);
}

#[test]
fn test_convert_timeout_outcome() {
    // RED Phase Test 6: Verify timeout → Timeout mapping
    // Expected: timeout outcome becomes MutantStatus::Timeout

    let json = r#"{
        "mutants": [{
            "outcome": "timeout",
            "file": "src/lib.rs",
            "function": "test",
            "line": 1
        }]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(pmat_report[0].status, MutantStatus::Timeout);
}

#[test]
fn test_convert_unviable_outcome() {
    // RED Phase Test 7: Verify unviable → CompileError mapping
    // Expected: unviable outcome becomes MutantStatus::CompileError

    let json = r#"{
        "mutants": [{
            "outcome": "unviable",
            "file": "src/lib.rs",
            "function": "test",
            "line": 1
        }]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(pmat_report[0].status, MutantStatus::CompileError);
}

// ============================================================================
// RED PHASE TESTS - PMAT Report Conversion
// ============================================================================

#[test]
fn test_to_pmat_report_preserves_all_data() {
    // RED Phase Test 8: Verify file, line, function preserved in conversion
    // Expected: All data preserved correctly

    let json = r#"{
        "mutants": [{
            "outcome": "caught",
            "file": "src/main.rs",
            "function": "calculate",
            "line": 42
        }]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(pmat_report.len(), 1, "Should have 1 mutant");

    let mutant = &pmat_report[0];
    assert_eq!(mutant.original_file, PathBuf::from("src/main.rs"));
    assert_eq!(mutant.location.line, 42);
    assert_eq!(mutant.status, MutantStatus::Killed);
}

#[test]
fn test_pmat_conversion_preserves_count() {
    // RED Phase Test 9: Verify mutant count preserved
    // Expected: to_pmat_report().len() == original.mutants.len()

    let json = r#"{
        "mutants": [
            {"outcome": "caught", "file": "src/a.rs", "line": 1},
            {"outcome": "missed", "file": "src/b.rs", "line": 2},
            {"outcome": "timeout", "file": "src/c.rs", "line": 3}
        ]
    }"#;

    let report = CargoMutantsReport::from_json(json).expect("Failed to parse");
    let pmat_report = report.to_pmat_report();

    assert_eq!(
        pmat_report.len(),
        report.mutants.len(),
        "PMAT report should have same number of mutants"
    );
}

// ============================================================================
// RED PHASE - Property Tests (Placeholder)
// ============================================================================

#[test]
#[ignore] // Property tests require proptest crate
fn proptest_json_parsing_round_trip() {
    // Property: parse(serialize(data)) == data
    // Implementation: See json_property_tests.rs
    todo!("Property test: JSON round trip (requires proptest)");
}

#[test]
#[ignore] // Property tests require proptest crate
fn proptest_pmat_conversion_never_loses_mutants() {
    // Property: to_pmat().len() == original.mutants.len()
    // Implementation: See json_property_tests.rs
    todo!("Property test: conversion preserves count (requires proptest)");
}

// ============================================================================
// RED PHASE SUMMARY
// ============================================================================
// Total Tests: 9 unit tests + 2 property test placeholders
// Expected Status: ALL SHOULD FAIL (unimplemented!)
//
// Next Phase: GREEN (implement CargoMutantsReport with serde)