debtmap 0.20.0

Code complexity and technical debt analyzer
Documentation
//! Integration test for multi-signal evidence display (spec 148)
//!
//! Verifies that:
//! - Evidence is displayed in actual output for ModuleSplit recommendations
//! - Different verbosity levels produce different output
//! - Alternatives are shown for low-confidence classifications
//! - Signal weights are displayed in output

use debtmap::output::evidence_formatter::EvidenceFormatter;

/// Test that evidence formatter produces output at different verbosity levels
#[test]
fn test_evidence_formatter_verbosity_levels() {
    use debtmap::analysis::multi_signal_aggregation::{
        AggregatedClassification, ResponsibilityCategory, SignalEvidence, SignalType,
    };

    let evidence = AggregatedClassification {
        primary: ResponsibilityCategory::FileIO,
        confidence: 0.85,
        evidence: vec![
            SignalEvidence {
                signal_type: SignalType::IoDetection,
                category: ResponsibilityCategory::FileIO,
                confidence: 0.90,
                weight: 0.35,
                contribution: 0.315,
                description: "3 file operations detected".to_string(),
            },
            SignalEvidence {
                signal_type: SignalType::CallGraph,
                category: ResponsibilityCategory::FileIO,
                confidence: 0.80,
                weight: 0.25,
                contribution: 0.20,
                description: "Calls file-related functions".to_string(),
            },
        ],
        alternatives: vec![],
    };

    // Test minimal verbosity (level 0)
    let formatter_minimal = EvidenceFormatter::new(0);
    let output_minimal = formatter_minimal.format_evidence(&evidence);
    assert!(output_minimal.contains("File I/O"));
    assert!(output_minimal.contains("85% confidence"));
    assert!(!output_minimal.contains("Contributing Signals")); // Should be minimal

    // Test standard verbosity (level 1)
    let formatter_standard = EvidenceFormatter::new(1);
    let output_standard = formatter_standard.format_evidence(&evidence);
    assert!(output_standard.contains("File I/O"));
    assert!(output_standard.contains("Contributing Signals"));
    assert!(output_standard.contains("IoDetection"));
    assert!(output_standard.contains("3 file operations"));

    // Test verbose (level 2)
    let formatter_verbose = EvidenceFormatter::new(2);
    let output_verbose = formatter_verbose.format_evidence(&evidence);
    assert!(output_verbose.contains("DETAILED BREAKDOWN"));
    assert!(output_verbose.contains("Category:"));
    assert!(output_verbose.contains("Confidence:"));
    assert!(output_verbose.contains("Weight:"));
}

/// Test that alternatives are shown for low-confidence classifications
#[test]
fn test_alternatives_display_for_low_confidence() {
    use debtmap::analysis::multi_signal_aggregation::{
        AggregatedClassification, ResponsibilityCategory,
    };

    let evidence = AggregatedClassification {
        primary: ResponsibilityCategory::Validation,
        confidence: 0.65, // Low confidence
        evidence: vec![],
        alternatives: vec![
            (ResponsibilityCategory::Transformation, 0.62),
            (ResponsibilityCategory::PureComputation, 0.58),
        ],
    };

    let formatter = EvidenceFormatter::new(1);
    let output = formatter.format_evidence(&evidence);

    // Should show alternatives section
    assert!(output.contains("Alternative Classifications"));
    assert!(output.contains("Transformation"));
    assert!(output.contains("ambiguous"));
}

/// Test that signal weights are displayed in evidence output
#[test]
fn test_signal_weights_displayed() {
    use debtmap::analysis::multi_signal_aggregation::{
        AggregatedClassification, ResponsibilityCategory, SignalEvidence, SignalType,
    };

    let evidence = AggregatedClassification {
        primary: ResponsibilityCategory::FileIO,
        confidence: 0.85,
        evidence: vec![SignalEvidence {
            signal_type: SignalType::IoDetection,
            category: ResponsibilityCategory::FileIO,
            confidence: 0.90,
            weight: 0.35,
            contribution: 0.315,
            description: "File operations".to_string(),
        }],
        alternatives: vec![],
    };

    let formatter = EvidenceFormatter::new(1);
    let output = formatter.format_evidence(&evidence);

    // Signal weight should be displayed
    assert!(output.contains("weight:"));
    assert!(output.contains("0.35") || output.contains("35%"));
}

/// Test that evidence appears in actual priority output when verbosity > 0
#[test]
fn test_evidence_in_priority_output() {
    // Create a minimal test case with god object that has classification evidence
    // This would require setting up a full analysis, which is complex
    // For now, this tests the formatter integration

    // Note: Full integration would require:
    // 1. Creating a test file with god object
    // 2. Running analysis
    // 3. Checking that evidence appears in formatted output
    // This is a placeholder to indicate where such a test would go
}

/// Test that OutputConfig can be loaded from TOML
#[test]
fn test_output_config_from_toml() {
    let toml_content = r#"
[output]
evidence_verbosity = "standard"
min_confidence_warning = 0.75

[output.signal_filters]
show_io_detection = true
show_call_graph = true
show_type_signatures = false
show_purity = true
show_framework = true
show_name_heuristics = false
"#;

    let config: Result<debtmap::config::DebtmapConfig, _> = toml::from_str(toml_content);
    assert!(config.is_ok());

    let config = config.unwrap();
    assert!(config.output.is_some());

    let output_config = config.output.unwrap();
    assert!(output_config.evidence_verbosity.is_some());
    assert_eq!(output_config.min_confidence_warning, Some(0.75));
}

/// Test that evidence formatter handles empty evidence gracefully
#[test]
fn test_empty_evidence_handling() {
    use debtmap::analysis::multi_signal_aggregation::{
        AggregatedClassification, ResponsibilityCategory,
    };

    let evidence = AggregatedClassification {
        primary: ResponsibilityCategory::Unknown,
        confidence: 0.40, // Very low
        evidence: vec![],
        alternatives: vec![],
    };

    let formatter = EvidenceFormatter::new(1);
    let output = formatter.format_evidence(&evidence);

    // Should still produce output without crashing
    assert!(output.contains("Unknown"));
    assert!(output.contains("40%") || output.contains("0.40"));
}

#[test]
fn test_confidence_bands() {
    use debtmap::output::evidence_formatter::ConfidenceBand;

    assert_eq!(ConfidenceBand::from_score(0.90), ConfidenceBand::High);
    assert_eq!(ConfidenceBand::from_score(0.80), ConfidenceBand::High);
    assert_eq!(ConfidenceBand::from_score(0.70), ConfidenceBand::Medium);
    assert_eq!(ConfidenceBand::from_score(0.60), ConfidenceBand::Medium);
    assert_eq!(ConfidenceBand::from_score(0.50), ConfidenceBand::Low);
    assert_eq!(ConfidenceBand::from_score(0.30), ConfidenceBand::Low);
}