pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
//! Extreme TDD tests for Ruchy language support
//! RED phase: Write failing tests first

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
    use crate::models::AstItem;

    // Placeholder struct until we import the real one
    struct RuchyAstExtractor {
        items: Vec<AstItem>,
    }

    impl RuchyAstExtractor {
        fn new() -> Self {
            Self { items: Vec::new() }
        }

        fn analyze_ruchy_source(&self, _source: &str) -> Result<Vec<AstItem>, String> {
            // This will fail initially (RED phase)
            Err("Not implemented".to_string())
        }

        fn analyze_pattern_complexity(&self, _source: &str) -> Result<u32, String> {
            Err("Not implemented".to_string())
        }
    }

    #[test]
    fn test_extract_ruchy_function_simple() {
        let source = "let add(x, y) = x + y";
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse simple function");
        let items = result.unwrap();
        assert_eq!(items.len(), 1, "Should extract one function");
        assert_eq!(items[0].name, "add");
        assert_eq!(items[0].item_type, "function");
        assert_eq!(items[0].complexity, 1); // Base complexity
    }

    #[test]
    fn test_extract_ruchy_type_definition() {
        let source = "type PositiveInt = { x: Int | x > 0 }";
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse type definition");
        let items = result.unwrap();
        assert_eq!(items.len(), 1, "Should extract one type");
        assert_eq!(items[0].name, "PositiveInt");
        assert_eq!(items[0].item_type, "type");
        assert_eq!(items[0].complexity, 0); // Types have no complexity
    }

    #[test]
    fn test_extract_ruchy_actor() {
        let source = r#"
actor Counter {
    state count = 0

    message Increment -> {
        count := count + 1
    }

    message GetCount -> Int {
        return count
    }
}"#;
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse actor definition");
        let items = result.unwrap();
        assert!(items.len() >= 1, "Should extract at least the actor");

        let actor = items.iter().find(|i| i.item_type == "actor").unwrap();
        assert_eq!(actor.name, "Counter");
        assert_eq!(actor.complexity, 3); // Base actor complexity
    }

    #[test]
    fn test_extract_ruchy_module_with_functions() {
        let source = r#"
module Math

let square(x) = x * x

let cube(x) = x * x * x
"#;
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse module with functions");
        let items = result.unwrap();
        assert_eq!(items.len(), 3, "Should extract module and 2 functions");

        assert_eq!(items[0].name, "Math");
        assert_eq!(items[0].item_type, "module");

        assert_eq!(items[1].name, "Math::square"); // Qualified name
        assert_eq!(items[1].item_type, "function");

        assert_eq!(items[2].name, "Math::cube"); // Qualified name
        assert_eq!(items[2].item_type, "function");
    }

    #[test]
    fn test_pattern_matching_complexity() {
        let source = r#"
let fibonacci(n) =
    match n with
    | 0 -> 0
    | 1 -> 1
    | n -> fibonacci(n - 1) + fibonacci(n - 2)
"#;
        let extractor = RuchyAstExtractor::new();
        let complexity = extractor.analyze_pattern_complexity(source);

        assert!(complexity.is_ok(), "Should analyze pattern complexity");
        assert_eq!(complexity.unwrap(), 4); // 1 for match + 3 for arms
    }

    #[test]
    fn test_ruchy_proof_construct() {
        let source = r#"
theorem sum_commutative:
    forall a b: Int, a + b = b + a

proof by induction on a {
    base: 0 + b = b + 0
    step: (a + b = b + a) => (S(a) + b = b + S(a))
}
"#;
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse proof constructs");
        let items = result.unwrap();
        assert!(items.iter().any(|i| i.name == "sum_commutative" && i.item_type == "theorem"));

        // Proof complexity should be higher
        let theorem = items.iter().find(|i| i.name == "sum_commutative").unwrap();
        assert!(theorem.complexity >= 5, "Proof should have high complexity");
    }

    #[test]
    fn test_empty_ruchy_source() {
        let source = "";
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should handle empty source");
        let items = result.unwrap();
        assert_eq!(items.len(), 0, "Should return empty list for empty source");
    }

    #[test]
    fn test_invalid_ruchy_syntax() {
        let source = "{{{ !!! INVALID RUCHY CODE";
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_err(), "Should reject invalid syntax");
        assert!(result.unwrap_err().contains("Invalid"));
    }

    #[test]
    fn test_ruchy_function_with_type_annotations() {
        let source = "let safe_divide(a: Int, b: PositiveInt) -> Float = a.to_float() / b.to_float()";
        let extractor = RuchyAstExtractor::new();
        let result = extractor.analyze_ruchy_source(source);

        assert!(result.is_ok(), "Should parse typed function");
        let items = result.unwrap();
        assert_eq!(items.len(), 1);
        assert_eq!(items[0].name, "safe_divide");
        assert_eq!(items[0].item_type, "function");
    }

    #[test]
    fn test_ruchy_nested_pattern_matching() {
        let source = r#"
let classify(x) =
    match x with
    | Some(n) ->
        match n with
        | 0 -> "zero"
        | n if n > 0 -> "positive"
        | _ -> "negative"
    | None -> "nothing"
"#;
        let extractor = RuchyAstExtractor::new();
        let complexity = extractor.analyze_pattern_complexity(source);

        assert!(complexity.is_ok(), "Should handle nested patterns");
        assert!(complexity.unwrap() >= 6); // 2 matches + multiple arms
    }
}

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

    // Placeholder for property tests
    struct RuchyAstExtractor;

    impl RuchyAstExtractor {
        fn new() -> Self { Self }
        fn analyze_ruchy_source(&self, _: &str) -> Result<Vec<crate::models::AstItem>, String> {
            Ok(vec![])
        }
    }

    proptest! {
        #[test]
        fn ruchy_parsing_never_panics(s in ".*") {
            let extractor = RuchyAstExtractor::new();
            let _ = extractor.analyze_ruchy_source(&s);
        }

        #[test]
        fn ruchy_empty_strings_produce_empty_ast(s in "\\s*") {
            let extractor = RuchyAstExtractor::new();
            let result = extractor.analyze_ruchy_source(&s);
            prop_assert!(result.unwrap().is_empty());
        }

        #[test]
        fn ruchy_function_names_preserved(
            name in "[a-z][a-zA-Z0-9_]*",
            params in "[a-z, ]*"
        ) {
            let source = format!("let {}({}) = 42", name, params);
            let extractor = RuchyAstExtractor::new();
            let result = extractor.analyze_ruchy_source(&source);

            if let Ok(items) = result {
                if !items.is_empty() {
                    prop_assert_eq!(items[0].name, name);
                }
            }
        }

        #[test]
        fn ruchy_type_names_preserved(
            name in "[A-Z][a-zA-Z0-9_]*"
        ) {
            let source = format!("type {} = Int", name);
            let extractor = RuchyAstExtractor::new();
            let result = extractor.analyze_ruchy_source(&source);

            if let Ok(items) = result {
                if !items.is_empty() {
                    prop_assert_eq!(items[0].name, name);
                }
            }
        }

        #[test]
        fn ruchy_actor_names_preserved(
            name in "[A-Z][a-zA-Z0-9_]*"
        ) {
            let source = format!("actor {} {{\n  state x = 0\n}}", name);
            let extractor = RuchyAstExtractor::new();
            let result = extractor.analyze_ruchy_source(&source);

            if let Ok(items) = result {
                if !items.is_empty() {
                    prop_assert_eq!(items[0].name, name);
                }
            }
        }
    }
}