leindex 1.6.0

LeIndex MCP and semantic code search engine for AI tools and large codebases
// Integration and unit tests for leparse

use crate::parse::traits::LanguageConfig;

#[cfg(test)]
mod language_detection_tests {
    use super::*;

    #[test]
    fn test_python_extension_detection() {
        let config = LanguageConfig::from_extension("py");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Python");
    }

    #[test]
    fn test_javascript_extension_detection() {
        let config = LanguageConfig::from_extension("js");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "JavaScript");

        let config = LanguageConfig::from_extension("jsx");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "JavaScript");
    }

    #[test]
    fn test_typescript_extension_detection() {
        let config = LanguageConfig::from_extension("ts");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "TypeScript");

        let config = LanguageConfig::from_extension("tsx");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "TypeScript");
    }

    #[test]
    fn test_c_extension_detection() {
        let config = LanguageConfig::from_extension("c");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "C");

        let config = LanguageConfig::from_extension("h");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "C");
    }

    #[test]
    fn test_bash_extension_detection() {
        let config = LanguageConfig::from_extension("sh");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Bash");

        let config = LanguageConfig::from_extension("bash");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Bash");
    }

    #[test]
    fn test_json_extension_detection() {
        let config = LanguageConfig::from_extension("json");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "JSON");
    }

    #[test]
    fn test_go_extension_detection() {
        let config = LanguageConfig::from_extension("go");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Go");
    }

    #[test]
    fn test_rust_extension_detection() {
        let config = LanguageConfig::from_extension("rs");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Rust");
    }

    #[test]
    fn test_case_insensitive_extension_detection() {
        let config = LanguageConfig::from_extension("PY");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "Python");

        let config = LanguageConfig::from_extension("Js");
        assert!(config.is_some());
        assert_eq!(config.unwrap().name, "JavaScript");
    }

    #[test]
    fn test_unknown_extension_returns_none() {
        let config = LanguageConfig::from_extension("unknown");
        assert!(config.is_none());

        let config = LanguageConfig::from_extension("");
        assert!(config.is_none());
    }

    #[test]
    fn test_language_config_extensions() {
        let py_config = LanguageConfig::from_extension("py").unwrap();
        assert!(py_config.extensions.contains(&"py".to_string()));
        assert_eq!(py_config.extensions.len(), 1);

        let js_config = LanguageConfig::from_extension("js").unwrap();
        assert!(js_config.extensions.contains(&"js".to_string()));
        assert!(js_config.extensions.contains(&"jsx".to_string()));
    }
}

#[cfg(all(test, feature = "cli"))]
mod stack_overflow_regression_tests {
    use crate::cli::LeIndex;
    use std::env;
    use std::fs;
    use std::process::Command;
    use tempfile::TempDir;

    const HELPER_TEST_NAME: &str = "helper_index_deep_nested_project";
    const HELPER_ENV: &str = "LEINDEX_STACK_HELPER";
    const PROJECT_ENV: &str = "LEINDEX_STACK_PROJECT";

    fn write_deep_rust_file(temp_dir: &TempDir, depth: usize) {
        let mut source = String::from("fn main() {\n");
        let mut indent = String::from("    ");

        for _ in 0..depth {
            source.push_str(&format!("{indent}if true {{\n"));
            indent.push_str("    ");
        }

        source.push_str(&format!("{indent}let _x = 1;\n"));

        for _ in 0..depth {
            indent.truncate(indent.len() - 4);
            source.push_str(&format!("{indent}}}\n"));
        }

        source.push_str("}\n");

        fs::write(temp_dir.path().join("deep.rs"), source).unwrap();
    }

    fn write_deep_cpp_file(temp_dir: &TempDir, depth: usize) {
        let mut source = String::from("int main() {\n");
        let mut indent = String::from("    ");

        for _ in 0..depth {
            source.push_str(&format!("{indent}if (true) {{\n"));
            indent.push_str("    ");
        }

        source.push_str(&format!("{indent}return 0;\n"));

        for _ in 0..depth {
            indent.truncate(indent.len() - 4);
            source.push_str(&format!("{indent}}}\n"));
        }

        source.push_str("}\n");

        fs::write(temp_dir.path().join("deep.cpp"), source).unwrap();
    }

    #[test]
    fn helper_index_deep_nested_project() {
        if env::var_os(HELPER_ENV).is_none() {
            return;
        }

        let project_path = env::var(PROJECT_ENV).unwrap();
        let mut index = LeIndex::new(&project_path).unwrap();
        let stats = index.index_project(true).unwrap();

        assert_eq!(stats.failed_parses, 0);
        assert_eq!(stats.successful_parses, 1);
    }

    #[test]
    fn test_index_deep_nested_rust_project_does_not_overflow_stack() {
        let temp_dir = TempDir::new().unwrap();
        write_deep_rust_file(&temp_dir, 6000);

        let output = Command::new(env::current_exe().unwrap())
            .env(HELPER_ENV, "1")
            .env(PROJECT_ENV, temp_dir.path())
            .arg("--exact")
            .arg(HELPER_TEST_NAME)
            .arg("--nocapture")
            .output()
            .unwrap();

        assert!(
            output.status.success(),
            "child process failed\nstatus: {:?}\nstdout:\n{}\nstderr:\n{}",
            output.status.code(),
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );
    }

    #[test]
    fn test_index_deep_nested_cpp_project_does_not_overflow_stack() {
        let temp_dir = TempDir::new().unwrap();
        write_deep_cpp_file(&temp_dir, 6000);

        let output = Command::new(env::current_exe().unwrap())
            .env(HELPER_ENV, "1")
            .env(PROJECT_ENV, temp_dir.path())
            .arg("--exact")
            .arg(HELPER_TEST_NAME)
            .arg("--nocapture")
            .output()
            .unwrap();

        assert!(
            output.status.success(),
            "child process failed\nstatus: {:?}\nstdout:\n{}\nstderr:\n{}",
            output.status.code(),
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );
    }
}