feat(store): V008 pdf_ocr_events migration + record/prune API (Enhancement 2)

Add migrations/V008__pdf_ocr_events.sql with the events table + 3
indices (doc_id, run_id, ts). SqliteStore gains two pub fn:
record_pdf_ocr_event (insert one OCR sample) and prune_pdf_ocr_events
(delete rows older than retention_days; returns the affected row
count). Both follow the existing Mutex<Connection> lock pattern.

Wiring into ingest path lands in the next commit.

Closure r1 F2: explicit lock acquisition in both methods.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 05:56:54 +00:00
parent 5977c8cdf1
commit 6482bf1321
3 changed files with 168 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
//! Smoke tests for V008 pdf_ocr_events migration + record/prune API (Enhancement 2).
//! AC-2, AC-3, AC-8.
mod common;
use kebab_store_sqlite::SqliteStore;
use rusqlite::OptionalExtension;
fn open_migrated() -> (common::TestEnv, SqliteStore) {
let env = common::TestEnv::new();
let store = SqliteStore::open(&env.config()).expect("open");
store.run_migrations().expect("run migrations");
(env, store)
}
/// AC-2: V008 migration creates the pdf_ocr_events table.
#[test]
fn v008_pdf_ocr_events_table_exists() {
let (env, _store) = open_migrated();
let name: Option<String> = env.with_conn(|c| {
c.query_row(
"SELECT name FROM sqlite_master WHERE type='table' AND name='pdf_ocr_events'",
[],
|r| r.get(0),
)
.optional()
});
assert_eq!(name.as_deref(), Some("pdf_ocr_events"), "pdf_ocr_events table must exist after V008");
}
/// AC-8: insert 2 rows with different timestamps; prune with retention_days=0
/// (cutoff = now) → the old row is deleted, count returns 1.
#[test]
fn record_and_prune_pdf_ocr_event() {
let (_env, store) = open_migrated();
// Row 1: very old timestamp (1970)
store
.record_pdf_ocr_event(
"run-old",
"1970-01-01T00:00:00Z",
Some("doc-old"),
"path/old.pdf",
1,
Some(12345),
Some(100),
Some(80),
250,
42,
true,
None,
"qwen2.5vl",
)
.expect("insert old row");
// Row 2: future timestamp (far future, so it survives prune)
store
.record_pdf_ocr_event(
"run-new",
"2099-01-01T00:00:00Z",
Some("doc-new"),
"path/new.pdf",
1,
None,
None,
None,
180,
30,
true,
None,
"qwen2.5vl",
)
.expect("insert future row");
// prune with retention_days=0 → cutoff=now → deletes any row with ts < now.
// The 1970 row should be deleted; the 2099 row survives.
let pruned = store
.prune_pdf_ocr_events(0)
.expect("prune");
assert_eq!(pruned, 1, "should have deleted exactly 1 old row");
// Verify only the future row remains
let count: i64 = {
let conn = store.read_conn();
conn.query_row("SELECT COUNT(*) FROM pdf_ocr_events", [], |r| r.get(0))
.expect("count")
};
assert_eq!(count, 1, "exactly 1 row should survive after prune");
}