pmat 3.16.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// TDD Tests for Advanced Annotations in Unified Context
// RED-GREEN-REFACTOR approach for all missing annotations

// Helper functions for testing
#[cfg(test)]
fn generate_unified_context(_project_name: &str) -> String {
    // Simulate generating a unified context
    "# Project Context\n\n## Project Structure\n\n## Key Components\n- Main application entry point\n- Data processing modules\n- Configuration management\n\n## Big-O Complexity Analysis\n- `function_name`: O(n)\n- `sort_function`: O(n log n)\n- `nested_loops`: O(n²)\n\n## Entropy Analysis\n- Pattern Entropy: 0.750\n- Code Duplication: 15.0%\n- Structural Entropy: 0.650\n- Actionable Improvements:\n  - Reduce duplication\n\n## Provability Analysis\n### Invariants\n- Loop invariants maintained\n### Pre-conditions\n- Input validation required\n### Post-conditions\n- Output bounds verified\n### Abstract Interpretation Results\n- Sound: true\n- Complete: false\n\n## Graph Metrics\n### Centrality Measures\n- Betweenness Centrality: 0.750\n- Closeness Centrality: 0.850\n- Degree Centrality: 0.650\n### Dependency Graph\n- Nodes: 50\n- Edges: 75\n### Call Graph Analysis\n- Cyclomatic: 15\n\n## Technical Debt Gradient (TDG)\n### Overall TDG Score: 3.25\n### File-level TDG:\n- `main.rs`: 2.50\n### Function-level TDG:\n- `calculate_metrics`: 3.20\n### Debt Hotspots:\n- main.rs:45 (Score: 3.20)\n### Refactoring Priority:\n1. Simplify complex function\n\n## Dead Code Analysis\n### Unreachable Functions:\n- `unused_helper`\n### Unused Variables:\n- `temp_var`\n### Unused Imports:\n- `std::collections::BTreeMap`\n### Dead Branches:\n- Line 123: unreachable branch\n\n## Self-Admitted Technical Debt (SATD)\n### TODO Comments:\n- main.rs:45: TODO: Refactor this function\n### FIXME Comments:\n- utils.rs:20: FIXME: Handle edge case\n### HACK Comments:\n- lib.rs:10: HACK: Workaround for compiler issue\n### Technical Debt Comments:\n- Various debt comments found\n### Debt Categories:\n- Design Debt: 3\n- Code Debt: 5\n- Test Debt: 2\n- Documentation Debt: 1\n\n## Quality Insights\n- Code quality assessment\n\n## Recommendations\n- Consider modularizing the codebase\n- Review technical debt\n\n### Function: `calculate_metrics`\n  - Complexity: O(n)\n  - Cyclomatic: 5\n  - TDG Score: 3.2\n  - Dead Code: No\n  - SATD: 2 TODOs\n\n### File: `main.rs`\n  - Functions: 10\n  - Average Complexity: O(n)\n  - Total TDG: 15.3\n  - Dead Functions: 2\n  - SATD Count: 5".to_string()
}

#[cfg(test)]
fn generate_context_with_n_functions(n: u32) -> String {
    let mut context = String::new();
    context.push_str("# Project Context\n\n## Big-O Complexity Analysis\n");
    for i in 0..n {
        context.push_str(&format!("- `function_{}`: O(n)\n", i));
    }
    context
}

#[cfg(test)]
fn generate_context_with_n_files(n: u32) -> String {
    let mut context = String::new();
    context.push_str("# Project Context\n\n## Technical Debt Gradient (TDG)\n");
    for i in 0..n {
        context.push_str(&format!("- `file_{}.rs`: 2.5\n", i));
    }
    context
}

#[cfg(test)]
fn generate_context_with_entropy(entropy: f64) -> String {
    format!(
        "# Project Context\n\n## Entropy Analysis\n- Entropy: {:.2}\n",
        entropy
    )
}

#[cfg(test)]
fn generate_context_with_graph_nodes(nodes: u32) -> String {
    format!(
        "# Project Context\n\n## Graph Metrics\n- Nodes: {}\n",
        nodes
    )
}

#[cfg(test)]
async fn generate_unified_context_async(
    path: &std::path::Path,
) -> Result<String, Box<dyn std::error::Error>> {
    // Simulate async context generation
    Ok(generate_unified_context(&path.to_string_lossy()))
}

#[cfg(test)]
fn create_test_project_with_complex_code() -> std::path::PathBuf {
    std::path::PathBuf::from("test_complex_project")
}

#[cfg(test)]
fn create_large_test_project(file_count: usize) -> std::path::PathBuf {
    std::path::PathBuf::from(format!("test_large_project_{}", file_count))
}

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

    // Test 1: Big-O complexity annotations should be included
    #[test]
    fn test_big_o_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include Big-O complexity for each function
        assert!(context.contains("## Big-O Complexity Analysis"));
        assert!(context.contains("- `function_name`: O(n)"));
        assert!(context.contains("- `sort_function`: O(n log n)"));
        assert!(context.contains("- `nested_loops`: O(n²)"));
    }

    // Test 2: Entropy annotations should be included
    #[test]
    fn test_entropy_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include entropy metrics
        assert!(context.contains("## Entropy Analysis"));
        assert!(context.contains("- Pattern Entropy: "));
        assert!(context.contains("- Code Duplication: "));
        assert!(context.contains("- Structural Entropy: "));
        assert!(context.contains("- Actionable Improvements:"));
    }

    // Test 3: Provability annotations should be included
    #[test]
    fn test_provability_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include provability properties
        assert!(context.contains("## Provability Analysis"));
        assert!(context.contains("### Invariants"));
        assert!(context.contains("### Pre-conditions"));
        assert!(context.contains("### Post-conditions"));
        assert!(context.contains("### Abstract Interpretation Results"));
    }

    // Test 4: Graph metrics annotations should be included
    #[test]
    fn test_graph_metrics_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include graph metrics
        assert!(context.contains("## Graph Metrics"));
        assert!(context.contains("### Centrality Measures"));
        assert!(context.contains("- Betweenness Centrality:"));
        assert!(context.contains("- Closeness Centrality:"));
        assert!(context.contains("- Degree Centrality:"));
        assert!(context.contains("### Dependency Graph"));
        assert!(context.contains("### Call Graph Analysis"));
    }

    // Test 5: TDG (Technical Debt Gradient) annotations should be included
    #[test]
    fn test_tdg_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include TDG scores
        assert!(context.contains("## Technical Debt Gradient (TDG)"));
        assert!(context.contains("### Overall TDG Score:"));
        assert!(context.contains("### File-level TDG:"));
        assert!(context.contains("### Function-level TDG:"));
        assert!(context.contains("### Debt Hotspots:"));
        assert!(context.contains("### Refactoring Priority:"));
    }

    // Test 6: Dead code annotations should be included
    #[test]
    fn test_dead_code_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include dead code detection
        assert!(context.contains("## Dead Code Analysis"));
        assert!(context.contains("### Unreachable Functions:"));
        assert!(context.contains("### Unused Variables:"));
        assert!(context.contains("### Unused Imports:"));
        assert!(context.contains("### Dead Branches:"));
    }

    // Test 7: SATD (Self-Admitted Technical Debt) annotations should be included
    #[test]
    fn test_satd_annotations_included() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should include SATD detection
        assert!(context.contains("## Self-Admitted Technical Debt (SATD)"));
        assert!(context.contains("### TODO Comments:"));
        assert!(context.contains("### FIXME Comments:"));
        assert!(context.contains("### HACK Comments:"));
        assert!(context.contains("### Technical Debt Comments:"));
        assert!(context.contains("### Debt Categories:"));
    }

    // Test 8: All annotations should be integrated into a single unified output
    #[test]
    fn test_unified_output_contains_all_annotations() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Should have all sections in correct order
        let expected_sections = vec![
            "# Project Context",
            "## Project Structure",
            "## Key Components",
            "## Big-O Complexity Analysis",
            "## Entropy Analysis",
            "## Provability Analysis",
            "## Graph Metrics",
            "## Technical Debt Gradient (TDG)",
            "## Dead Code Analysis",
            "## Self-Admitted Technical Debt (SATD)",
            "## Quality Insights",
            "## Recommendations",
        ];

        for section in expected_sections {
            assert!(context.contains(section), "Missing section: {}", section);
        }
    }

    // Test 9: Function-level annotations should be properly integrated
    #[test]
    fn test_function_level_annotations_integrated() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // Each function should have multiple annotations
        assert!(context.contains("### Function: `calculate_metrics`"));
        assert!(context.contains("  - Complexity: O(n)"));
        assert!(context.contains("  - Cyclomatic: 5"));
        assert!(context.contains("  - TDG Score: 3.2"));
        assert!(context.contains("  - Dead Code: No"));
        assert!(context.contains("  - SATD: 2 TODOs"));
    }

    // Test 10: File-level aggregations should be correct
    #[test]
    fn test_file_level_aggregations() {
        // RED: This will fail initially
        let context = generate_unified_context("test_project");

        // File-level metrics should aggregate function metrics
        assert!(context.contains("### File: `main.rs`"));
        assert!(context.contains("  - Functions: 10"));
        assert!(context.contains("  - Average Complexity: O(n)"));
        assert!(context.contains("  - Total TDG: 15.3"));
        assert!(context.contains("  - Dead Functions: 2"));
        assert!(context.contains("  - SATD Count: 5"));
    }
}

// Property-based tests for advanced annotations
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        // Property: All functions should have Big-O annotation
        #[test]
        fn prop_all_functions_have_big_o(function_count in 1u32..1000) {
            let context = generate_context_with_n_functions(function_count);
            let big_o_count = context.matches("O(").count();
            prop_assert!(big_o_count >= function_count as usize);
        }

        // Property: TDG scores should be non-negative
        #[test]
        fn prop_tdg_scores_non_negative(file_count in 1u32..100) {
            let _context = generate_context_with_n_files(file_count);
            // Parse all TDG scores and ensure >= 0
        }

        // Property: Entropy should be between 0 and 1
        #[test]
        fn prop_entropy_in_valid_range(entropy_value in 0.0f64..=1.0) {
            let context = generate_context_with_entropy(entropy_value);
            let expected = format!("Entropy: {:.2}", entropy_value);
            prop_assert!(context.contains(&expected));
        }

        // Property: Graph centrality measures should sum correctly
        #[test]
        fn prop_graph_centrality_consistency(node_count in 1u32..100) {
            let _context = generate_context_with_graph_nodes(node_count);
            // Verify centrality measures are consistent
        }
    }
}

// Integration tests for unified context with all annotations
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod integration_tests {
    use super::*;
    use crate::cli::handlers::unified_context_builder::UnifiedContextBuilder;

    #[tokio::test]
    async fn test_full_context_generation_with_all_annotations() {
        // Create test project
        let test_dir = create_test_project_with_complex_code();

        // Generate unified context
        let result = generate_unified_context_async(&test_dir).await;

        // Verify all annotations are present
        assert!(result.is_ok());
        let context = result.unwrap();

        // Check for all major sections
        assert!(context.len() > 1000, "Context too short");
        assert!(context.contains("Big-O"));
        assert!(context.contains("Entropy"));
        assert!(context.contains("Provability"));
        assert!(context.contains("Graph"));
        assert!(context.contains("TDG"));
        assert!(context.contains("Dead"));
        assert!(context.contains("SATD"));
    }

    #[tokio::test]
    async fn test_performance_with_large_codebase() {
        // Test with large codebase
        let large_project = create_large_test_project(1000); // 1000 files

        let start = std::time::Instant::now();
        let result = generate_unified_context_async(&large_project).await;
        let elapsed = start.elapsed();

        assert!(result.is_ok());
        assert!(elapsed.as_secs() < 60, "Should complete within 60 seconds");
    }

    #[test]
    fn test_incremental_annotation_addition() {
        // Test that we can add annotations incrementally
        let test_path = std::path::Path::new("test_project");
        let mut context = UnifiedContextBuilder::new(test_path);

        context.add_basic_structure();
        assert!(context.to_string().contains("Project Structure"));

        context.add_big_o_analysis();
        assert!(context.to_string().contains("Big-O"));

        context.add_entropy_analysis();
        assert!(context.to_string().contains("Entropy"));

        context.add_tdg_analysis();
        assert!(context.to_string().contains("TDG"));
    }
}