car-memgine 0.16.0

Memgine — graph-based memory engine for Common Agent Runtime
Documentation
//! Integration test: ingest the actual car-ast crate into the memory graph
//! and verify we can query it with graph retrieval.

#[cfg(feature = "ast")]
mod ast_tests {
    use car_memgine::MemgineEngine;
    use std::path::Path;

    #[test]
    fn ingest_car_ast_crate() {
        let mut engine = MemgineEngine::new(None);
        let ast_src = Path::new(env!("CARGO_MANIFEST_DIR"))
            .parent()
            .unwrap()
            .join("car-ast/src");

        let count = engine.ingest_codebase(&ast_src);

        // car-ast/src has: lib.rs, types.rs, index.rs, languages/{mod,rust,python,typescript,javascript,go}.rs
        // Should find many symbols
        assert!(
            count >= 20,
            "expected >= 20 symbols from car-ast, got {}",
            count
        );
        assert_eq!(engine.code_symbol_count(), count);

        // Query for specific symbols we know exist
        let results = engine.query_code_symbols("ParsedFile", 10);
        assert!(!results.is_empty(), "should find ParsedFile in graph");
        let names: Vec<_> = results.iter().map(|r| r.0.name.as_str()).collect();
        assert!(
            names.contains(&"ParsedFile"),
            "results should include ParsedFile, got {:?}",
            names
        );

        // Query for a function
        let results = engine.query_code_symbols("extract", 10);
        assert!(!results.is_empty(), "should find extract functions");

        // Query for cross-file concepts (Span is defined in types.rs, used everywhere)
        let results = engine.query_code_symbols("Span", 10);
        assert!(!results.is_empty(), "should find Span");

        println!("Ingested {} symbols from car-ast/src", count);
        println!("Top results for 'ParsedFile':");
        for (meta, score) in engine.query_code_symbols("ParsedFile", 5) {
            println!(
                "  [{:.3}] {} {} ({}:{})",
                score, meta.kind, meta.name, meta.file_path, meta.start_line
            );
        }
    }

    #[test]
    fn ingest_and_query_graph_traversal() {
        // Ingest a small codebase and verify graph edges work for retrieval
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(
            dir.path().join("models.rs"),
            r#"
pub struct User {
    pub id: u64,
    pub name: String,
}

impl User {
    pub fn new(id: u64, name: String) -> Self {
        Self { id, name }
    }

    pub fn display_name(&self) -> &str {
        &self.name
    }
}

pub struct UserStore {
    users: Vec<User>,
}

impl UserStore {
    pub fn new() -> Self {
        Self { users: Vec::new() }
    }

    pub fn add(&mut self, user: User) {
        self.users.push(user);
    }

    pub fn find_by_id(&self, id: u64) -> Option<&User> {
        self.users.iter().find(|u| u.id == id)
    }
}
"#,
        )
        .unwrap();

        std::fs::write(
            dir.path().join("handler.rs"),
            r#"
use crate::models::{User, UserStore};

pub fn handle_create_user(store: &mut UserStore, id: u64, name: &str) {
    let user = User::new(id, name.to_string());
    store.add(user);
}

pub fn handle_get_user(store: &UserStore, id: u64) -> Option<String> {
    store.find_by_id(id).map(|u| u.display_name().to_string())
}
"#,
        )
        .unwrap();

        let mut engine = MemgineEngine::new(None);
        let count = engine.ingest_codebase(dir.path());

        // Should have User, UserStore, new, display_name, add, find_by_id,
        // handle_create_user, handle_get_user, and their impl blocks
        assert!(count >= 8, "expected >= 8 symbols, got {}", count);

        // Querying "User" should find User-related symbols
        let results = engine.query_code_symbols("User", 10);
        assert!(!results.is_empty());

        // Querying "handle" should find handler functions
        let results = engine.query_code_symbols("handle", 10);
        let names: Vec<_> = results.iter().map(|r| r.0.name.as_str()).collect();
        assert!(
            names.iter().any(|n| n.starts_with("handle_")),
            "should find handler functions, got {:?}",
            names
        );
    }
}