use codelore_lib::facts::FactsDb;
#[test]
fn creates_v1_schema() {
let db = FactsDb::new_in_memory().expect("create");
let tables = db.list_tables().expect("list");
let expected = [
"commits",
"changes",
"hunks",
"entities",
"complexity_metrics",
"author_aliases",
"provenance",
];
for t in &expected {
assert!(tables.iter().any(|n| n == t), "table {t} missing");
}
}
#[test]
fn provenance_records_schema_version() {
let db = FactsDb::new_in_memory().expect("create");
let v: String = db
.query_one_value("SELECT value FROM provenance WHERE key = 'schema_version'")
.expect("query");
assert_eq!(v, codelore_lib::facts::schema::CURRENT_SCHEMA_VERSION);
}
#[test]
fn open_read_only_validates_schema_version_match() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("ok.duckdb");
{
let _ = FactsDb::open(&path).expect("create");
}
let reopen = FactsDb::open_read_only(&path);
assert!(
reopen.is_ok(),
"open_read_only on current schema should succeed"
);
}
#[test]
fn open_read_only_rejects_mismatched_schema_version() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("stale.duckdb");
{
let db = FactsDb::open(&path).expect("create");
db.execute_batch("UPDATE provenance SET value = '999' WHERE key = 'schema_version'")
.expect("bump schema_version");
}
let msg = match FactsDb::open_read_only(&path) {
Ok(_) => panic!("stale schema should fail open_read_only"),
Err(e) => format!("{e}"),
};
let expects = format!(
"expects {}",
codelore_lib::facts::schema::CURRENT_SCHEMA_VERSION
);
assert!(
msg.contains("schema_version=999") && msg.contains(&expects),
"expected mismatch error, got: {msg}"
);
}
#[test]
fn open_read_only_rejects_non_factstore_file() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("foreign.duckdb");
{
let conn = duckdb::Connection::open(&path).expect("open foreign");
conn.execute_batch("CREATE TABLE foo(x INTEGER)")
.expect("create foreign schema");
}
let msg = match FactsDb::open_read_only(&path) {
Ok(_) => panic!("foreign DB should fail open_read_only"),
Err(e) => format!("{e}"),
};
assert!(
msg.contains("missing the provenance schema_version row"),
"expected missing-row error, got: {msg}"
);
}
#[test]
fn low_memory_limit_spills_to_temp_dir_instead_of_erroring() {
let dir = tempfile::tempdir().expect("tempdir");
let spill_dir = dir.path().join("spill");
let db = FactsDb::new_in_memory_with_memory_limit("128MB", &spill_dir)
.expect("in-memory db with a low memory_limit must still open + create schema");
db.execute_batch(
"CREATE TABLE spill_probe AS \
SELECT i, random() AS a, random() AS b, random() AS c, random() AS d \
FROM range(5000000) AS t(i);",
)
.expect("bulk table load must succeed by spilling, not by erroring/OOM-ing");
let count: i64 = db
.query_row("SELECT count(*) FROM spill_probe", [], |r| r.get(0))
.expect("count spilled rows");
assert_eq!(
count, 5_000_000,
"spilled table load must still see every row"
);
assert!(
spill_dir.is_dir(),
"temp_directory PRAGMA target must exist: {}",
spill_dir.display()
);
let spilled_files = std::fs::read_dir(&spill_dir)
.expect("read spill dir")
.count();
assert!(
spilled_files > 0,
"expected DuckDB to write spill file(s) under temp_dir at {}; found none — \
the memory_limit may not have been low enough to force an external sort",
spill_dir.display()
);
}
#[test]
fn file_backed_db_persists_and_reopens() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("test.duckdb");
{
let db = FactsDb::open(&path).expect("create file-backed");
let tables = db.list_tables().expect("list");
assert!(
tables.iter().any(|n| n == "commits"),
"commits table should exist"
);
}
{
let db = FactsDb::open(&path).expect("reopen file-backed");
let schema_version: String = db
.query_one_value("SELECT value FROM provenance WHERE key = 'schema_version'")
.expect("query");
assert_eq!(
schema_version,
codelore_lib::facts::schema::CURRENT_SCHEMA_VERSION
);
}
}