pmat 3.30.1

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP)
Documentation
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
//! TDG --explain Mode Integration Tests (Issue #78)
//!
//! EXTREME TDD - RED Phase
//! These tests document the expected behavior of the --explain mode
//! and should ALL FAIL until the implementation is complete (GREEN phase).
//!
//! Test Categories:
//! 1. Function Complexity Extraction (from Rust source code)
//! 2. Pattern Detection (repeated match statements, oversized modules)
//! 3. Recommendation Generation (actionable refactoring suggestions)
//! 4. Baseline Comparison (tracking progress over time)
//! 5. Output Formatting (text and JSON)
//!
//! References:
//! - Specification: docs/specifications/tdg-explain-mode.md
//! - Issue: #78

use pmat::tdg::{
    ActionableRecommendation, ComplexitySeverity, ExplainedTDGScore, FunctionComplexity,
    RecommendationType,
};
use std::path::{Path, PathBuf};
use tempfile::TempDir;

/// GREEN TEST 1: Extract function complexity from simple Rust file
///
/// Verifies that the FunctionAnalyzer can parse a Rust file and extract
/// function-level complexity metrics.
///
/// GREEN: FunctionAnalyzer implemented, test should pass
#[test]
fn test_extract_function_complexity_from_rust_file() {
    // Create a test Rust file
    let test_code = r#"
        fn simple_function() -> i32 {
            return 42;
        }

        fn medium_complexity_function(x: i32) -> i32 {
            if x > 10 {
                if x > 20 {
                    return x * 2;
                } else {
                    return x + 5;
                }
            } else {
                return x - 3;
            }
        }

        fn high_complexity_function(value: i32) -> String {
            match value {
                0 => "zero".to_string(),
                1 => "one".to_string(),
                2 => "two".to_string(),
                3 => "three".to_string(),
                4 => "four".to_string(),
                5 => "five".to_string(),
                6 => "six".to_string(),
                7 => "seven".to_string(),
                8 => "eight".to_string(),
                9 => "nine".to_string(),
                10 => "ten".to_string(),
                _ => "many".to_string(),
            }
        }
    "#;

    // Write test file
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test.rs");
    std::fs::write(&test_file, test_code).unwrap();

    // GREEN: FunctionAnalyzer now working!
    let functions = extract_function_complexity(&test_file).unwrap();

    // Verify 3 functions extracted
    assert_eq!(functions.len(), 3, "Should extract 3 functions");

    // Verify simple_function
    let simple = functions
        .iter()
        .find(|f| f.name == "simple_function")
        .unwrap();
    assert_eq!(
        simple.cyclomatic, 1,
        "simple_function should have cyclomatic complexity 1"
    );
    assert_eq!(simple.severity, ComplexitySeverity::Low);

    // Verify medium_complexity_function
    let medium = functions
        .iter()
        .find(|f| f.name == "medium_complexity_function")
        .unwrap();

    // Nested if/else: 1 (base) + 2 (two if statements) = 3
    assert_eq!(
        medium.cyclomatic, 3,
        "medium_complexity_function should have cyclomatic = 3"
    );

    // Complexity 3 is Low according to standard McCabe thresholds (Low: 0-5)
    assert_eq!(
        medium.severity,
        ComplexitySeverity::Low,
        "Complexity 3 should be Low severity per McCabe standards"
    );

    // Verify high_complexity_function
    let high = functions
        .iter()
        .find(|f| f.name == "high_complexity_function")
        .unwrap();
    assert!(
        high.cyclomatic >= 11,
        "high_complexity_function should have cyclomatic >= 11 (match with 11 arms)"
    );
    assert_eq!(
        high.severity,
        ComplexitySeverity::High,
        "high_complexity_function should be High severity"
    );
}

/// RED TEST 2: Detect repeated match/switch dispatch pattern
///
/// Verifies that the PatternDetector can identify repeated match statements
/// that are candidates for macro extraction.
///
/// Expected to FAIL: PatternDetector not implemented yet
#[test]
#[ignore] // RED: Will fail until PatternDetector is implemented
fn test_detect_dispatch_pattern() {
    let test_code = r#"
        fn operation_a(backend: Backend) -> i32 {
            match backend {
                Backend::Simd => simd_impl_a(),
                Backend::Gpu => gpu_impl_a(),
                Backend::Scalar => scalar_impl_a(),
            }
        }

        fn operation_b(backend: Backend) -> i32 {
            match backend {
                Backend::Simd => simd_impl_b(),
                Backend::Gpu => gpu_impl_b(),
                Backend::Scalar => scalar_impl_b(),
            }
        }

        fn operation_c(backend: Backend) -> i32 {
            match backend {
                Backend::Simd => simd_impl_c(),
                Backend::Gpu => gpu_impl_c(),
                Backend::Scalar => scalar_impl_c(),
            }
        }
    "#;

    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("dispatch.rs");
    std::fs::write(&test_file, test_code).unwrap();

    // RED: This should fail - detect_dispatch_pattern doesn't exist yet
    let patterns = detect_dispatch_pattern(&test_file).unwrap();

    // Should detect the repeated match pattern
    assert!(
        !patterns.is_empty(),
        "Should detect at least one dispatch pattern"
    );
    assert_eq!(
        patterns[0].branch_count, 3,
        "Should detect 3 branches in match statement"
    );
}

/// GREEN TEST 3: Generate actionable recommendations
///
/// Verifies that the RecommendationEngine can generate specific, actionable
/// recommendations with estimated impact and effort.
///
/// GREEN: RecommendationEngine now implemented
#[test]
fn test_generate_recommendations_from_complexity() {
    use pmat::tdg::generate_recommendations;

    // Create ExplainedTDGScore with high-complexity functions
    let mut explained = ExplainedTDGScore::new(pmat::tdg::TdgScore::default());

    explained.add_function(FunctionComplexity {
        name: "complex_function_1".to_string(),
        line_number: 100,
        cyclomatic: 25,
        cognitive: 30,
        tdg_impact: 4.5,
        severity: ComplexitySeverity::Critical,
    });

    explained.add_function(FunctionComplexity {
        name: "complex_function_2".to_string(),
        line_number: 200,
        cyclomatic: 22,
        cognitive: 28,
        tdg_impact: 4.2,
        severity: ComplexitySeverity::Critical,
    });

    // GREEN: generate_recommendations now works!
    let recommendations = generate_recommendations(&explained);

    // Should generate recommendations for high complexity
    assert!(
        !recommendations.is_empty(),
        "Should generate recommendations for complex functions"
    );

    // First recommendation should target complex_function_1 (highest impact)
    assert_eq!(
        recommendations[0].rec_type,
        RecommendationType::ReduceComplexity
    );
    assert!(
        recommendations[0].lines.contains(&100),
        "Recommendation should reference line 100"
    );
    assert!(
        recommendations[0].expected_impact > 0.0,
        "Should have positive expected impact"
    );
    assert!(
        recommendations[0].estimated_hours > 0.0,
        "Should have estimated effort"
    );
    assert_eq!(
        recommendations[0].priority, 1,
        "Highest impact should be priority 1"
    );
}

/// GREEN TEST 4: Baseline comparison tracking
///
/// Verifies that the BaselineAnalyzer can compare current state against
/// a baseline and track progress.
///
/// GREEN: BaselineAnalyzer now implemented
#[test]
fn test_baseline_comparison() {
    let test_code = r#"
        fn refactored_function() -> i32 {
            // This function was previously complex but has been refactored
            simple_implementation()
        }

        fn still_complex_function(value: i32) -> String {
            match value {
                0..=9 => format!("digit_{}", value),
                _ => "other".to_string(),
            }
        }
    "#;

    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("baseline_test.rs");
    std::fs::write(&test_file, test_code).unwrap();

    // RED: This should fail - compare_with_baseline doesn't exist yet
    let comparison = compare_with_baseline(&test_file, "baseline_ref").unwrap();

    // Verify baseline comparison structure
    assert_eq!(comparison.baseline_ref, "baseline_ref");
    assert!(
        comparison.delta != 0.0,
        "Delta should be non-zero (improvement or regression)"
    );

    // If we improved, should have completed recommendations
    if comparison.delta > 0.0 {
        assert!(
            !comparison.completed.is_empty(),
            "Should track completed recommendations"
        );
    }

    // Should still have pending recommendations
    assert!(
        !comparison.pending.is_empty(),
        "Should track pending recommendations"
    );
}

/// GREEN TEST 5: Text output formatting
///
/// Verifies that the explain mode produces human-readable text output
/// with function breakdown and recommendations.
///
/// GREEN: format_explain_text now implemented
#[test]
fn test_explain_text_output_format() {
    use pmat::tdg::format_explain_text;

    let mut explained = ExplainedTDGScore::new(pmat::tdg::TdgScore::default());

    explained.add_function(FunctionComplexity {
        name: "test_function".to_string(),
        line_number: 42,
        cyclomatic: 15,
        cognitive: 18,
        tdg_impact: 3.2,
        severity: ComplexitySeverity::High,
    });

    explained.add_recommendation(ActionableRecommendation {
        rec_type: RecommendationType::ExtractMacro,
        action: "Extract dispatch pattern into macro".to_string(),
        lines: vec![100, 200, 300],
        expected_impact: 8.5,
        estimated_hours: 5.0,
        priority: 1,
        pattern: "match_dispatch".to_string(),
    });

    // GREEN: format_explain_text now works!
    let output = format_explain_text(&explained).unwrap();

    // Verify output contains key sections
    assert!(
        output.contains("Function-Level Complexity"),
        "Should have function section"
    );
    assert!(
        output.contains("test_function"),
        "Should show function name"
    );
    assert!(output.contains("line 42"), "Should show line number");
    assert!(
        output.contains("Complexity: 15"),
        "Should show cyclomatic complexity"
    );
    assert!(
        output.contains("Recommendations"),
        "Should have recommendations section"
    );
    assert!(output.contains("[+8.5 pts]"), "Should show expected impact");
    assert!(
        output.contains("Extract dispatch pattern"),
        "Should show action"
    );
}

/// GREEN TEST 6: JSON output formatting
///
/// Verifies that the explain mode produces valid JSON output
/// suitable for CI/CD integration.
///
/// GREEN: format_explain_json now implemented
#[test]
fn test_explain_json_output_format() {
    use pmat::tdg::format_explain_json;

    let mut explained = ExplainedTDGScore::new(pmat::tdg::TdgScore::default());

    explained.add_function(FunctionComplexity {
        name: "test_function".to_string(),
        line_number: 42,
        cyclomatic: 15,
        cognitive: 18,
        tdg_impact: 3.2,
        severity: ComplexitySeverity::High,
    });

    // GREEN: format_explain_json now works!
    let output = format_explain_json(&explained).unwrap();

    // Parse JSON to verify structure
    let json: serde_json::Value = serde_json::from_str(&output).unwrap();

    // Verify JSON structure
    assert!(
        json.get("functions").is_some(),
        "Should have functions array"
    );
    let functions = json["functions"].as_array().unwrap();
    assert_eq!(functions.len(), 1, "Should have 1 function");

    let func = &functions[0];
    assert_eq!(func["name"].as_str().unwrap(), "test_function");
    assert_eq!(func["line"].as_u64().unwrap(), 42);
    assert_eq!(func["cyclomatic"].as_u64().unwrap(), 15);
    assert_eq!(func["tdg_impact"].as_f64().unwrap(), 3.2);
}

/// GREEN TEST 7: Threshold filtering
///
/// Verifies that --threshold flag correctly filters functions
/// based on cyclomatic complexity.
///
/// GREEN: Functionality already implemented in explain.rs data model
#[test]
fn test_threshold_filtering() {
    let mut explained = ExplainedTDGScore::new(pmat::tdg::TdgScore::default());

    explained.add_function(FunctionComplexity {
        name: "simple".to_string(),
        line_number: 10,
        cyclomatic: 5,
        cognitive: 6,
        tdg_impact: 1.0,
        severity: ComplexitySeverity::Low,
    });

    explained.add_function(FunctionComplexity {
        name: "complex".to_string(),
        line_number: 50,
        cyclomatic: 20,
        cognitive: 25,
        tdg_impact: 4.5,
        severity: ComplexitySeverity::Critical,
    });

    // Apply threshold of 10 (should filter out simple function)
    explained.filter_functions_by_threshold(10);

    assert_eq!(
        explained.total_functions(),
        1,
        "Should have 1 function after filtering"
    );
    assert_eq!(
        explained.functions[0].name, "complex",
        "Should keep only complex function"
    );
}

// ============================================================================
// RED Test Stub Functions (NOT IMPLEMENTED - these should fail to compile)
// ============================================================================
//
// The following functions are intentionally NOT implemented.
// They represent the interfaces we need to create in the GREEN phase.
//
// Compilation will fail with "unresolved import" or "cannot find function"
// errors, which is EXPECTED in the RED phase of EXTREME TDD.

/// Extract function complexity from Rust source file
///
/// GREEN: Now implemented using FunctionAnalyzer
fn extract_function_complexity(file: &Path) -> Result<Vec<FunctionComplexity>, String> {
    use pmat::tdg::FunctionAnalyzer;

    let mut analyzer =
        FunctionAnalyzer::new().map_err(|e| format!("Failed to create analyzer: {}", e))?;

    analyzer
        .analyze_file(file)
        .map_err(|e| format!("Failed to analyze file: {}", e))
}

/// Detect dispatch pattern in Rust source file
///
/// RED: Not implemented - this is the interface we need to create
#[allow(dead_code)]
struct DispatchPattern {
    branch_count: usize,
}

fn detect_dispatch_pattern(_file: &PathBuf) -> Result<Vec<DispatchPattern>, String> {
    // RED: Compilation will fail here - function not implemented
    unimplemented!("RED PHASE: detect_dispatch_pattern not implemented yet")
}

/// Compare with baseline git ref
///
/// GREEN: Now implemented in baseline_analyzer module
use pmat::tdg::baseline_analyzer::compare_with_baseline;

// ============================================================================
// RED Phase Summary
// ============================================================================
//
// All 7 tests above should FAIL in the RED phase:
//
// 1. test_extract_function_complexity_from_rust_file - FAIL (extract_function_complexity not implemented)
// 2. test_detect_dispatch_pattern - FAIL (detect_dispatch_pattern not implemented)
// 3. test_generate_recommendations_from_complexity - FAIL (generate_recommendations not implemented)
// 4. test_baseline_comparison - FAIL (compare_with_baseline not implemented)
// 5. test_explain_text_output_format - FAIL (format_explain_text not implemented)
// 6. test_explain_json_output_format - FAIL (format_explain_json not implemented)
// 7. test_threshold_filtering - PASS (basic data model functionality works)
//
// Expected Compilation: FAIL (unimplemented functions)
// Expected Test Execution: 0 passing, 7 failing (when #[ignore] is removed)
//
// Next Step (GREEN Phase):
// - Implement FunctionAnalyzer module (server/src/tdg/function_analyzer.rs)
// - Implement PatternDetector module (server/src/tdg/pattern_detector.rs)
// - Implement RecommendationEngine module (server/src/tdg/recommendation_engine.rs)
// - Implement BaselineAnalyzer module (server/src/tdg/baseline_analyzer.rs)
// - Implement formatters (server/src/tdg/explain_formatters.rs)
// - Update TDG handler to integrate --explain mode
// - Remove #[ignore] from tests
// - Verify all 7 tests PASS (GREEN phase)