use std::sync::Arc;
use fathomdb_embedder_api::{Embedder, EmbedderError, EmbedderIdentity, Vector};
use fathomdb_engine::{Engine, PreparedWrite, SourceId};
use fathomdb_schema::SQLITE_SUFFIX;
use rusqlite::Connection;
use tempfile::TempDir;
const PRUNE_MARKER_KEY: &str = "tc33_edge_vector_prune_complete";
#[derive(Clone, Debug)]
struct DeterministicEmbedder {
identity: EmbedderIdentity,
vector: Vector,
}
impl DeterministicEmbedder {
fn new(dim: u32) -> Self {
let mut vector = vec![0.0_f32; dim as usize];
vector[0] = 1.0;
Self { identity: EmbedderIdentity::new("fix6-test", "rev-a", dim), vector }
}
}
impl Embedder for DeterministicEmbedder {
fn identity(&self) -> EmbedderIdentity {
self.identity.clone()
}
fn embed(&self, _text: &str) -> Result<Vector, EmbedderError> {
Ok(self.vector.clone())
}
}
fn db_path(dir: &TempDir, name: &str) -> std::path::PathBuf {
dir.path().join(format!("{name}{SQLITE_SUFFIX}"))
}
fn count(conn: &Connection, sql: &str, cursor: i64) -> u64 {
conn.query_row(sql, [cursor], |row| row.get(0)).expect("count query")
}
fn write_fact_edge(engine: &Engine, logical: &str, body: &str) -> i64 {
let edge = PreparedWrite::Edge {
kind: "works_for".to_string(),
from: "bob".to_string(),
to: "acme".to_string(),
source_id: SourceId::new("doc-1").expect("source id"),
logical_id: Some(logical.to_string()),
body: Some(body.to_string()),
t_valid: Some(1_577_836_800), t_invalid: None,
confidence: Some(0.9),
extractor_model_id: Some("stub-extractor-v1".to_string()),
temporal_fallback: None,
};
engine.write(&[edge]).expect("write edge").row_cursors[0] as i64
}
fn write_doc_node(engine: &Engine, body: &str, source: &str) -> i64 {
engine
.write(&[PreparedWrite::Node {
kind: "doc".to_string(),
body: body.to_string(),
source_id: SourceId::new(source).expect("source id"),
logical_id: None,
state: fathomdb_engine::InitialState::Active,
reason: None,
valid_from: None,
valid_until: None,
}])
.expect("write node")
.cursor as i64
}
#[test]
fn fix6_orphaned_edge_vector_is_pruned_on_open() {
let dir = TempDir::new().unwrap();
let path = db_path(&dir, "fix6_prune");
let embedder = Arc::new(DeterministicEmbedder::new(8));
let (edge_cursor, node_cursor) = {
let opened = Engine::open_with_embedder_for_test(&path, embedder.clone()).expect("open");
opened.engine.configure_vector_kind_for_test("doc").expect("vector kind doc");
opened.engine.configure_vector_kind_for_test("edge_fact").expect("vector kind edge_fact");
let node_cursor = write_doc_node(&opened.engine, "node body survives", "S-node");
let edge_cursor = write_fact_edge(&opened.engine, "edge-fact-1", "Bob works for Acme");
opened.engine.drain(10_000).expect("drain");
opened.engine.close().unwrap();
(edge_cursor, node_cursor)
};
{
let conn = Connection::open(&path).expect("open sqlite");
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", edge_cursor),
1,
"seed: edge must have a vec0 row (cursor {edge_cursor})"
);
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", node_cursor),
1,
"seed: node must have a vec0 row (cursor {node_cursor})"
);
}
{
let conn = Connection::open(&path).expect("open sqlite");
conn.execute("DELETE FROM canonical_edges WHERE write_cursor = ?1", [edge_cursor])
.expect("drop edge canonical row");
conn.execute("DELETE FROM _fathomdb_vector_rows WHERE write_cursor = ?1", [edge_cursor])
.expect("drop edge vector sidecar");
conn.execute("DELETE FROM _fathomdb_open_state WHERE key = ?1", [PRUNE_MARKER_KEY])
.expect("clear prune marker to re-arm");
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", edge_cursor),
1,
"injection: the orphaned edge vec0 row must still be present pre-prune"
);
}
{
let opened = Engine::open_with_embedder_for_test(&path, embedder.clone()).expect("reopen");
opened.engine.close().unwrap();
}
let conn = Connection::open(&path).expect("open sqlite");
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", edge_cursor),
0,
"the orphaned edge vec0 row (cursor {edge_cursor}) must be pruned on open — an \
orphan consumes a KNN candidate slot and is discarded at hydration"
);
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", node_cursor),
1,
"the node's vec0 row (cursor {node_cursor}) must survive — the prune targets \
only sidecar-less orphans, never node recall"
);
assert_eq!(
count(
&conn,
"SELECT COUNT(*) FROM _fathomdb_vector_rows WHERE write_cursor = ?1",
node_cursor
),
1,
"the node's vector sidecar (cursor {node_cursor}) must survive the prune"
);
}
#[test]
fn fix6_prune_is_a_noop_without_orphans() {
let dir = TempDir::new().unwrap();
let path = db_path(&dir, "fix6_noop");
let embedder = Arc::new(DeterministicEmbedder::new(8));
let node_cursor = {
let opened = Engine::open_with_embedder_for_test(&path, embedder.clone()).expect("open");
opened.engine.configure_vector_kind_for_test("doc").expect("vector kind doc");
let node_cursor = write_doc_node(&opened.engine, "healthy node body", "S-node");
opened.engine.drain(10_000).expect("drain");
opened.engine.close().unwrap();
node_cursor
};
{
let conn = Connection::open(&path).expect("open sqlite");
conn.execute("DELETE FROM _fathomdb_open_state WHERE key = ?1", [PRUNE_MARKER_KEY])
.expect("clear prune marker");
}
let before = {
let conn = Connection::open(&path).expect("open sqlite");
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid >= ?1", 0)
};
{
let opened = Engine::open_with_embedder_for_test(&path, embedder.clone()).expect("reopen");
opened.engine.close().unwrap();
}
let conn = Connection::open(&path).expect("open sqlite");
let after = count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid >= ?1", 0);
assert_eq!(before, after, "the prune must delete NOTHING when there are no orphans");
assert_eq!(
count(&conn, "SELECT COUNT(*) FROM vector_default WHERE rowid = ?1", node_cursor),
1,
"the healthy node vec0 row must survive a re-armed prune (cursor {node_cursor})"
);
}