sqlite-graphrag 1.1.2

Persistent GraphRAG memory for Claude Code, Codex, Cursor, and 24+ AI agents in a single 19 MiB Rust binary. LLM-only and one-shot in v1.0.78: every `remember` / `ingest` spawns a headless claude code or codex subprocess (OAuth, no MCP, no hooks). v1.0.93: optional OpenRouter API embedding backend (~100-500ms vs 20-60s subprocess). No daemon. No ONNX runtime. No model download. Graph-native retrieval with FTS5 + cosine + multi-hop traversal. OAuth-only enforcement for LLM backends: API keys ABORT the spawn.
Documentation
//! Integration test for the v1.1.02 `--prune-dead-entity-orphans` flag.
//! Guards the clap wiring (flag accepted, conflicts_with the memory variant)
//! and the envelope contract (action label). The row-level DELETE logic is
//! covered by the unit test `prune_dead_entity_orphans_removes_only_entity_dead_rows`
//! in `src/commands/enrich/queue.rs`.

use assert_cmd::Command;
use serial_test::serial;
use tempfile::TempDir;

#[path = "common/mod.rs"]
mod common;

fn cmd(temp: &TempDir) -> Command {
    let mock_dir = common::mock_llm_path();
    let mut c = Command::cargo_bin("sqlite-graphrag").expect("sqlite-graphrag binary not found");
    c.env_clear()
        .env("HOME", temp.path())
        .env("SQLITE_GRAPHRAG_HOME", temp.path())
        .env("SQLITE_GRAPHRAG_LANG", "en")
        .env("SQLITE_GRAPHRAG_LOG_LEVEL", "warn")
        .current_dir(temp.path());
    for var in &["LOCALAPPDATA", "APPDATA", "USERPROFILE", "SystemRoot"] {
        if let Ok(v) = std::env::var(var) {
            c.env(var, v);
        }
    }
    c.env("PATH", common::prepend_path(&mock_dir));
    c
}

#[test]
#[serial]
fn prune_dead_entity_orphans_emits_envelope() {
    let tmp = TempDir::new().unwrap();
    cmd(&tmp)
        .args(["init", "--embedding-dim", "64", "--json"])
        .assert()
        .success();

    let output = cmd(&tmp)
        .args([
            "enrich",
            "--operation",
            "re-embed",
            "--prune-dead-entity-orphans",
            "--json",
        ])
        .output()
        .expect("run enrich --prune-dead-entity-orphans");
    assert!(
        output.status.success(),
        "enrich --prune-dead-entity-orphans failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        stdout.contains("\"action\":\"prune-dead-entity-orphans\""),
        "action label missing in envelope: {stdout}"
    );
}

#[test]
#[serial]
fn prune_dead_entity_orphans_conflicts_with_memory_variant() {
    let tmp = TempDir::new().unwrap();
    cmd(&tmp)
        .args(["init", "--embedding-dim", "64", "--json"])
        .assert()
        .success();

    // Passing both prune flags must be rejected by clap (conflicts_with).
    cmd(&tmp)
        .args([
            "enrich",
            "--operation",
            "re-embed",
            "--prune-dead-orphans",
            "--prune-dead-entity-orphans",
            "--json",
        ])
        .assert()
        .failure()
        .code(2);
}