1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#![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)