sqlitegraph 2.2.2

Embedded graph database with full ACID transactions, HNSW vector search, dual backend support, and comprehensive graph algorithms library
Documentation
use rusqlite::Connection;
use serde_json::json;
use sqlitegraph::{GraphEntity, SqliteGraph, SqliteGraphError, schema::ensure_schema};

fn sample_entity(kind: &str, name: &str) -> GraphEntity {
    GraphEntity {
        id: 0,
        kind: kind.to_string(),
        name: name.to_string(),
        file_path: None,
        data: json!({ "kind": kind, "name": name }),
    }
}

#[test]
fn test_create_schema_creates_tables() {
    let conn = Connection::open_in_memory().expect("in-memory db");
    ensure_schema(&conn).expect("schema");

    let mut stmt = conn
        .prepare(
            "SELECT name FROM sqlite_master WHERE type='table' AND name IN ('graph_entities', 'graph_edges')",
        )
        .expect("prepare");
    let mut rows = stmt.query([]).expect("query");

    let mut found = Vec::new();
    while let Some(row) = rows.next().expect("rows") {
        found.push(row.get::<_, String>(0).expect("name"));
    }

    assert!(found.contains(&"graph_entities".to_string()));
    assert!(found.contains(&"graph_edges".to_string()));
}

#[test]
fn test_insert_and_get_entity_roundtrip() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let entity = sample_entity("Function", "alpha");
    let id = graph.insert_entity(&entity).expect("insert");
    let stored = graph.get_entity(id).expect("get");
    assert_eq!(stored.kind, "Function");
    assert_eq!(stored.name, "alpha");
    assert_eq!(stored.file_path, None);
}

#[test]
fn test_entity_update_persists_changes() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let mut entity = sample_entity("Struct", "Beta");
    let id = graph.insert_entity(&entity).expect("insert");
    entity.id = id;
    entity.name = "BetaRenamed".to_string();
    entity.file_path = Some("src/lib.rs".to_string());
    graph.update_entity(&entity).expect("update");
    let stored = graph.get_entity(id).expect("get");
    assert_eq!(stored.name, "BetaRenamed");
    assert_eq!(stored.file_path.as_deref(), Some("src/lib.rs"));
}

#[test]
fn test_entity_delete_removes_record() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let id = graph
        .insert_entity(&sample_entity("Mod", "gamma"))
        .expect("insert");
    graph.delete_entity(id).expect("delete");
    let err = graph.get_entity(id).expect_err("missing");
    match err {
        SqliteGraphError::NotFound(_) => {}
        other => panic!("expected NotFound, got {other:?}"),
    }
}

#[test]
fn test_bulk_insert_entities_is_deterministic() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let ids: Vec<_> = ["a", "b", "c", "d", "e"]
        .iter()
        .map(|name| {
            graph
                .insert_entity(&sample_entity("Node", name))
                .expect("insert")
        })
        .collect();
    assert_eq!(ids, vec![1, 2, 3, 4, 5]);
}

#[test]
fn test_insert_entity_requires_name() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let mut entity = sample_entity("Node", "");
    entity.name.clear();
    let err = graph.insert_entity(&entity).expect_err("invalid");
    match err {
        SqliteGraphError::InvalidInput(_) => {}
        other => panic!("expected InvalidInput, got {other:?}"),
    }
}

#[test]
fn test_insert_entity_assigns_incrementing_ids() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let id1 = graph.insert_entity(&sample_entity("Node", "one")).unwrap();
    let id2 = graph.insert_entity(&sample_entity("Node", "two")).unwrap();
    let id3 = graph
        .insert_entity(&sample_entity("Node", "three"))
        .unwrap();
    assert!(id1 < id2 && id2 < id3);
    assert_eq!(id1, 1);
    assert_eq!(id3, 3);
}

#[test]
fn test_get_entity_not_found_returns_error() {
    let graph = SqliteGraph::open_in_memory().expect("graph");
    let err = graph.get_entity(999).expect_err("missing");
    assert!(matches!(err, SqliteGraphError::NotFound(_)));
}