#[path = "common/harness.rs"]
mod harness;
#[path = "common/v11_schema.rs"]
mod v11_schema;
use harness::TestHarness;
use macrame::graph::EdgeAssertion;
use macrame::schema::{ddl, SCHEMA_VERSION};
use macrame::{ConceptUpsert, Database};
use v11_schema::wind_back_to_v11;
const TS: &str = "2026-01-01T00:00:00.000000Z";
const TS2: &str = "2026-02-01T00:00:00.000000Z";
const TS3: &str = "2026-03-01T00:00:00.000000Z";
const SENTINEL: &str = "9999-12-31T23:59:59.999999Z";
const CUTOFF: &str = "2099-01-01T00:00:00.000000Z";
async fn connect(harness: &TestHarness) -> libsql::Connection {
libsql::Builder::new_local(&harness.db_path)
.build()
.await
.unwrap()
.connect()
.unwrap()
}
async fn user_version(conn: &libsql::Connection) -> u32 {
conn.query("PRAGMA user_version", ())
.await
.unwrap()
.next()
.await
.unwrap()
.unwrap()
.get(0)
.unwrap()
}
async fn columns(conn: &libsql::Connection, schema: &str, table: &str) -> Vec<String> {
let mut rows = conn
.query(&format!("PRAGMA {schema}.table_info({table})"), ())
.await
.unwrap();
let mut out = Vec::new();
while let Some(r) = rows.next().await.unwrap() {
out.push(r.get::<String>(1).unwrap());
}
out
}
async fn count(
conn: &libsql::Connection,
sql: &str,
params: impl libsql::params::IntoParams,
) -> i64 {
conn.query(sql, params)
.await
.unwrap()
.next()
.await
.unwrap()
.unwrap()
.get(0)
.unwrap()
}
async fn text(
conn: &libsql::Connection,
sql: &str,
params: impl libsql::params::IntoParams,
) -> String {
conn.query(sql, params)
.await
.unwrap()
.next()
.await
.unwrap()
.unwrap()
.get(0)
.unwrap()
}
async fn register(conn: &libsql::Connection, branch: &str, parent: &str, forked_at: &str) {
conn.execute(
"INSERT INTO branches (branch_id, parent_id, forked_at, created_at) \
VALUES (?1, ?2, ?3, ?3)",
libsql::params![branch, parent, forked_at],
)
.await
.unwrap();
}
async fn seed_concepts(conn: &libsql::Connection) {
for id in ["c0", "c1"] {
conn.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at) VALUES (?1, 'N', ?2, ?2)",
libsql::params![id, TS],
)
.await
.unwrap();
}
}
#[tokio::test]
async fn a_v11_database_climbs_to_v12_and_every_existing_row_reads_as_trunk() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
wind_back_to_v11(&conn).await;
conn.execute("PRAGMA user_version = 11", ()).await.unwrap();
seed_concepts(&conn).await;
conn.execute(
"INSERT INTO links (source_id, target_id, edge_type, valid_from, valid_to, \
weight, properties, recorded_at) VALUES ('c0','c1','A',?1,?2,1.0,'{}',?1)",
libsql::params![TS, SENTINEL],
)
.await
.unwrap();
for table in ["concepts", "links", "transaction_log"] {
assert!(
!columns(&conn, "main", table)
.await
.contains(&"branch_id".into()),
"the fixture is not at v11: {table} already carries branch_id"
);
}
macrame::schema::run_migrations(&conn).await.unwrap();
assert_eq!(user_version(&conn).await, SCHEMA_VERSION);
for table in ["concepts", "links", "links_current", "transaction_log"] {
assert!(
columns(&conn, "main", table)
.await
.contains(&"branch_id".into()),
"the rung must add branch_id to {table}"
);
}
let root: (String, Option<String>) = {
let row = conn
.query("SELECT branch_id, parent_id FROM branches", ())
.await
.unwrap()
.next()
.await
.unwrap()
.unwrap();
(row.get(0).unwrap(), row.get(1).unwrap())
};
assert_eq!(root, ("main".to_string(), None), "the root has no parent");
for table in ["concepts", "links", "links_current", "transaction_log"] {
let strays: i64 = count(
&conn,
&format!("SELECT COUNT(*) FROM {table} WHERE branch_id <> 'main'"),
(),
)
.await;
assert_eq!(
strays, 0,
"a row written before lineage existed came back as something other \
than trunk in {table}"
);
}
assert_eq!(macrame::integrity::audit_current(&conn).await.unwrap(), 0);
}
#[tokio::test]
async fn links_current_is_keyed_per_lineage_and_the_sync_trigger_agrees() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
let sql: String = text(
&conn,
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'links_current'",
(),
)
.await;
assert!(
sql.contains("PRIMARY KEY (source_id, target_id, edge_type, valid_from, branch_id)"),
"links_current must be keyed per lineage: {sql}"
);
let target = ddl::CREATE_LINKS_CURRENT_SYNC
.split_once("ON CONFLICT(")
.and_then(|(_, rest)| rest.split_once(')'))
.map(|(cols, _)| cols)
.expect("the sync trigger declares an ON CONFLICT target");
assert!(
sql.contains(&format!("PRIMARY KEY ({target})")),
"the table's key and the trigger's conflict target have diverged: \
key in {sql}, target {target}"
);
}
#[tokio::test]
async fn the_log_triggers_stamp_the_lineage_the_write_actually_happened_on() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
register(&conn, "b", "main", TS2).await;
conn.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('cb', 'N', ?1, ?1, 'b')",
libsql::params![TS2],
)
.await
.unwrap();
conn.execute(
"UPDATE concepts SET title = 'N2', recorded_at = ?1 WHERE id = 'cb'",
libsql::params![TS3],
)
.await
.unwrap();
conn.execute(
"INSERT INTO links (source_id, target_id, edge_type, valid_from, valid_to, \
weight, properties, recorded_at, branch_id) \
VALUES ('c0','c1','A',?1,?2,1.0,'{}',?1,'b')",
libsql::params![TS2, SENTINEL],
)
.await
.unwrap();
let trunk_stamped: i64 = count(
&conn,
"SELECT COUNT(*) FROM transaction_log WHERE branch_id = 'main' \
AND (entity_id = 'cb' OR entity_id LIKE 'c0|c1|A|%')",
(),
)
.await;
assert_eq!(
trunk_stamped, 0,
"a write that happened on branch 'b' was logged against trunk. Every \
such row is invisible to the abandonment sweep and folds into the \
wrong lineage's history."
);
let concept_rows: i64 = count(
&conn,
"SELECT COUNT(*) FROM transaction_log WHERE entity_id = 'cb' AND branch_id = 'b'",
(),
)
.await;
assert_eq!(
concept_rows, 2,
"the mint and the same-lineage correction must both be logged on 'b'"
);
let edge_rows: i64 = count(
&conn,
"SELECT COUNT(*) FROM transaction_log WHERE table_name = 'links' AND branch_id = 'b'",
(),
)
.await;
assert_eq!(edge_rows, 1, "the edge assertion must be logged on 'b'");
}
#[tokio::test]
async fn two_lineages_asserting_one_edge_do_not_collapse_in_the_log() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
register(&conn, "b", "main", TS2).await;
for (branch, weight, ra) in [("main", 1.0, TS), ("b", 2.0, TS2)] {
conn.execute(
"INSERT INTO links (source_id, target_id, edge_type, valid_from, valid_to, \
weight, properties, recorded_at, branch_id) \
VALUES ('c0','c1','A',?1,?2,?3,'{}',?4,?5)",
libsql::params![TS, SENTINEL, weight, ra, branch],
)
.await
.unwrap();
}
let materialized: i64 = count(&conn, "SELECT COUNT(*) FROM links_current", ()).await;
assert_eq!(
materialized, 2,
"links_current collapsed two lineages' open beliefs into one row"
);
assert_eq!(macrame::integrity::audit_current(&conn).await.unwrap(), 0);
let logged: i64 = count(
&conn,
"SELECT COUNT(*) FROM transaction_log WHERE table_name = 'links'",
(),
)
.await;
assert_eq!(logged, 2);
}
#[tokio::test]
async fn a_reconstruction_keeps_both_lineages_beliefs() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
register(&conn, "b", "main", TS2).await;
conn.execute(
"INSERT INTO links (source_id, target_id, edge_type, valid_from, valid_to, \
weight, properties, recorded_at, branch_id) \
VALUES ('c0','c1','A',?1,?2,1.0,'{}',?1,'main')",
libsql::params![TS, SENTINEL],
)
.await
.unwrap();
conn.execute(
"INSERT INTO links (source_id, target_id, edge_type, valid_from, valid_to, \
weight, properties, recorded_at, branch_id) \
VALUES ('c0','c1','A',?1,?2,1.0,'{}',?2,'b')",
libsql::params![TS, TS2],
)
.await
.unwrap();
let state = macrame::temporal::reconstruct(&conn, TS3, None, None)
.await
.unwrap();
assert_eq!(
state.edges.len(),
2,
"one edge key, two lineages, two beliefs: {:?}",
state.edges
);
let mut got: Vec<(&str, &str)> = state
.edges
.iter()
.map(|e| (e.branch_id.as_str(), e.valid_to.as_str()))
.collect();
got.sort_unstable();
assert_eq!(
got,
vec![("b", TS2), (ddl::MAIN_BRANCH, SENTINEL)],
"the trunk still believes the edge open and the branch has closed it"
);
}
#[tokio::test]
async fn a_branch_cannot_restate_or_relabel_an_inherited_concept() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
register(&conn, "b", "main", TS2).await;
let err = conn
.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('c0', 'mine now', ?1, ?1, 'b')",
libsql::params![TS2],
)
.await
.expect_err("a second lineage restated an inherited concept");
assert!(
err.to_string().contains(ddl::ABORT_CROSS_LINEAGE),
"the refusal must name the rule, not just fail a unique index: {err}"
);
let err = conn
.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('c0', 'mine now', ?1, ?1, 'b') \
ON CONFLICT(id) DO UPDATE SET title = excluded.title",
libsql::params![TS2],
)
.await
.expect_err("an upsert reached DO UPDATE across a lineage boundary");
assert!(err.to_string().contains(ddl::ABORT_CROSS_LINEAGE), "{err}");
let err = conn
.execute(
"UPDATE concepts SET branch_id = 'b', recorded_at = ?1 WHERE id = 'c0'",
libsql::params![TS3],
)
.await
.expect_err("branch_id was moved by an UPDATE");
assert!(
err.to_string().contains(ddl::ABORT_BRANCH_IMMUTABLE),
"{err}"
);
}
#[tokio::test]
async fn branch_records_are_append_only_including_the_no_op_update() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
register(&conn, "b", "main", TS2).await;
for (label, sql) in [
(
"re-parent",
"UPDATE branches SET parent_id = NULL WHERE branch_id = 'b'",
),
(
"move the fork point",
"UPDATE branches SET forked_at = '2026-06-01T00:00:00.000000Z' WHERE branch_id = 'b'",
),
(
"a no-op",
"UPDATE branches SET branch_id = 'b' WHERE branch_id = 'b'",
),
] {
let err = conn.execute(sql, ()).await.unwrap_err_or_else_msg(label);
assert!(
err.contains(ddl::ABORT_BRANCHES_FROZEN),
"{label}: expected the append-only guard, got {err}"
);
}
let err = conn
.execute("DELETE FROM branches WHERE branch_id = 'b'", ())
.await
.expect_err("a lineage record was deleted");
assert!(
err.to_string().contains(ddl::ABORT_BRANCHES_FROZEN),
"branches are never archived, so there is no session in which this is \
legal: {err}"
);
}
#[tokio::test]
async fn a_row_cannot_name_a_lineage_that_was_never_registered() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
let err = conn
.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('ghost', 'N', ?1, ?1, 'never-registered')",
libsql::params![TS2],
)
.await
.expect_err("a concept named a lineage that does not exist");
assert!(
err.to_string().to_lowercase().contains("foreign key"),
"the lineage column must carry a real key, not a convention: {err}"
);
let strays: i64 = count(
&conn,
"SELECT COUNT(*) FROM concepts WHERE id = 'ghost'",
(),
)
.await;
assert_eq!(strays, 0);
}
#[tokio::test]
async fn concepts_are_shared_across_lineages_and_the_read_does_not_filter() {
let harness = TestHarness::new();
let conn = connect(&harness).await;
macrame::schema::run_migrations(&conn).await.unwrap();
seed_concepts(&conn).await;
register(&conn, "b", "main", TS2).await;
conn.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('cb', 'minted on b', ?1, ?1, 'b')",
libsql::params![TS2],
)
.await
.unwrap();
let visible: i64 = count(&conn, "SELECT COUNT(*) FROM concepts WHERE retired = 0", ()).await;
assert_eq!(
visible, 3,
"every lineage sees every concept under Option A. If this is red, a \
visibility predicate has landed on concepts — which is a change to \
what a branch *is*, not an optimisation, and belongs in §15.2 before \
it belongs in the schema."
);
let minted_on_b: i64 = count(
&conn,
"SELECT COUNT(*) FROM concepts WHERE branch_id = 'b'",
(),
)
.await;
assert_eq!(minted_on_b, 1, "shared visibility is not shared provenance");
}
async fn archived_pair(harness: &TestHarness) -> std::path::PathBuf {
let db = Database::open(&harness.db_path).await.unwrap();
for id in ["a", "b"] {
db.upsert_concept(
ConceptUpsert::new(id, "T")
.valid_from(TS)
.valid_to(TS2)
.retired(true),
)
.await
.unwrap();
}
db.assert_edge(
EdgeAssertion::new("a", "b", "KNOWS")
.valid_from(TS)
.valid_to(TS2),
)
.await
.unwrap();
db.archive(CUTOFF).await.unwrap();
db.close().await.unwrap();
let mut cold = harness.db_path.clone();
let stem = harness.db_path.file_stem().unwrap().to_str().unwrap();
cold.set_file_name(format!("{stem}_archive.db"));
cold
}
async fn attach_cold(conn: &libsql::Connection, cold: &std::path::Path) {
conn.execute(&format!("ATTACH DATABASE '{}' AS cold", cold.display()), ())
.await
.unwrap();
}
#[tokio::test]
async fn an_archive_carries_the_lineage_across_the_boundary() {
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
assert!(cold.exists(), "the archive must have produced a cold file");
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
for table in ["links", "transaction_log"] {
assert!(
columns(&conn, "cold", table)
.await
.contains(&"branch_id".into()),
"cold.{table} did not gain branch_id, so the lineage of every \
archived row is gone the moment it crosses"
);
}
let moved: i64 = count(&conn, "SELECT COUNT(*) FROM cold.links", ()).await;
assert!(
moved > 0,
"the fixture archived nothing, so this proves nothing"
);
let trunk: i64 = count(
&conn,
"SELECT COUNT(*) FROM cold.links WHERE branch_id = ?1",
libsql::params![ddl::MAIN_BRANCH],
)
.await;
assert_eq!(
trunk, moved,
"every row the fixture wrote was on the trunk, so every archived row \
must read as trunk — a different count means the write carried a \
literal rather than the row's own column"
);
}
#[tokio::test]
async fn a_pre_v15_cold_key_is_rebuilt_by_the_archive_that_meets_it() {
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
{
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
conn.execute("ALTER TABLE cold.links RENAME TO links_pre_v15", ())
.await
.unwrap();
conn.execute(
"CREATE TABLE cold.links (
source_id TEXT NOT NULL,
target_id TEXT NOT NULL,
edge_type TEXT NOT NULL,
valid_from TEXT NOT NULL,
recorded_at TEXT NOT NULL,
valid_to TEXT NOT NULL,
weight REAL NOT NULL,
properties TEXT NOT NULL,
branch_id TEXT NOT NULL DEFAULT 'main',
PRIMARY KEY (source_id, target_id, edge_type, valid_from, recorded_at)
)",
(),
)
.await
.unwrap();
conn.execute(
"INSERT INTO cold.links \
(source_id, target_id, edge_type, valid_from, recorded_at, \
valid_to, weight, properties, branch_id) \
SELECT source_id, target_id, edge_type, valid_from, recorded_at, \
valid_to, weight, properties, branch_id FROM cold.links_pre_v15",
(),
)
.await
.unwrap();
conn.execute("DROP TABLE cold.links_pre_v15", ())
.await
.unwrap();
assert!(
!cold_links_keyed_by_lineage(&conn).await,
"the wind-back did not take"
);
}
let db = Database::open(&harness.db_path).await.unwrap();
db.upsert_concept(
ConceptUpsert::new("c", "C")
.valid_from(TS)
.valid_to(TS2)
.retired(true),
)
.await
.unwrap();
db.archive(CUTOFF).await.unwrap();
db.close().await.unwrap();
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
assert!(
cold_links_keyed_by_lineage(&conn).await,
"the archive met a pre-v15 cold key and left it there, so the next \
cross-lineage row to cross the boundary will collide"
);
let kept: i64 = count(&conn, "SELECT COUNT(*) FROM cold.links", ()).await;
assert!(
kept > 0,
"the rebuild dropped the rows it was supposed to carry across"
);
}
async fn cold_links_keyed_by_lineage(conn: &libsql::Connection) -> bool {
let mut rows = conn
.query("PRAGMA cold.table_info(links)", ())
.await
.unwrap();
while let Some(row) = rows.next().await.unwrap() {
let named = row.get::<String>(1).is_ok_and(|n| n == "branch_id");
if named && row.get::<i64>(5).is_ok_and(|pk| pk > 0) {
return true;
}
}
false
}
#[tokio::test]
async fn a_cross_lineage_pair_written_at_one_instant_crosses_the_boundary() {
let harness = TestHarness::new();
let db = Database::open(&harness.db_path).await.unwrap();
for id in ["a", "b"] {
db.upsert_concept(
ConceptUpsert::new(id, "T")
.valid_from(TS)
.valid_to(TS2)
.retired(true),
)
.await
.unwrap();
}
db.fork(
macrame::branch::BranchId::new("b1").unwrap(),
macrame::branch::BranchId::new(ddl::MAIN_BRANCH).unwrap(),
)
.await
.unwrap();
let written = db
.write_bulk_atomic(vec![
EdgeAssertion::new("a", "b", "KNOWS")
.valid_from(TS)
.valid_to(TS2),
EdgeAssertion::new("a", "b", "KNOWS")
.valid_from(TS)
.valid_to(TS2)
.on_branch(macrame::branch::BranchId::new("b1").unwrap()),
])
.await
.expect("one batch, one edge key, two lineages");
assert_eq!(written, 2);
db.archive(CUTOFF).await.unwrap();
db.close().await.unwrap();
let mut cold = harness.db_path.clone();
let stem = harness.db_path.file_stem().unwrap().to_str().unwrap();
cold.set_file_name(format!("{stem}_archive.db"));
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
let lineages: i64 = count(
&conn,
"SELECT COUNT(DISTINCT branch_id) FROM cold.links \
WHERE source_id = 'a' AND target_id = 'b' AND edge_type = 'KNOWS'",
(),
)
.await;
let hot: i64 = count(
&conn,
"SELECT COUNT(DISTINCT branch_id) FROM links \
WHERE source_id = 'a' AND target_id = 'b' AND edge_type = 'KNOWS'",
(),
)
.await;
assert_eq!(
lineages + hot,
2,
"the pair did not survive the boundary intact: {lineages} lineage(s) \
cold and {hot} hot, and there were two"
);
}
#[tokio::test]
async fn a_pre_v12_cold_file_is_upgraded_by_the_archive_that_meets_it() {
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
{
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
conn.execute(
"DROP INDEX IF EXISTS cold.idx_cold_txlog_fold_partition",
(),
)
.await
.unwrap();
for table in ["concepts", "transaction_log"] {
conn.execute(
&format!("ALTER TABLE cold.{table} DROP COLUMN branch_id"),
(),
)
.await
.unwrap();
}
conn.execute("ALTER TABLE cold.links RENAME TO links_pre_v12", ())
.await
.unwrap();
conn.execute(
"CREATE TABLE cold.links (
source_id TEXT NOT NULL,
target_id TEXT NOT NULL,
edge_type TEXT NOT NULL,
valid_from TEXT NOT NULL,
recorded_at TEXT NOT NULL,
valid_to TEXT NOT NULL,
weight REAL NOT NULL,
properties TEXT NOT NULL,
PRIMARY KEY (source_id, target_id, edge_type, valid_from, recorded_at)
)",
(),
)
.await
.unwrap();
conn.execute(
"INSERT INTO cold.links \
(source_id, target_id, edge_type, valid_from, recorded_at, \
valid_to, weight, properties) \
SELECT source_id, target_id, edge_type, valid_from, recorded_at, \
valid_to, weight, properties FROM cold.links_pre_v12",
(),
)
.await
.unwrap();
conn.execute("DROP TABLE cold.links_pre_v12", ())
.await
.unwrap();
assert!(
!columns(&conn, "cold", "links")
.await
.contains(&"branch_id".into()),
"the wind-back did not take"
);
}
let db = Database::open(&harness.db_path).await.unwrap();
db.upsert_concept(ConceptUpsert::new("c", "C").valid_from(TS))
.await
.unwrap();
db.archive(CUTOFF).await.unwrap();
db.close().await.unwrap();
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
for table in ["links", "concepts", "transaction_log"] {
assert!(
columns(&conn, "cold", table)
.await
.contains(&"branch_id".into()),
"cold.{table} was not upgraded, so the next insert either fails \
loudly or drops the lineage silently — probe §10 and §11"
);
}
let orphaned: i64 = count(
&conn,
"SELECT COUNT(*) FROM cold.links WHERE branch_id <> ?1",
libsql::params![ddl::MAIN_BRANCH],
)
.await;
assert_eq!(
orphaned, 0,
"a row that predates the column must take the default, not a null or \
a blank"
);
}
#[tokio::test]
async fn an_archived_file_carries_the_folds_partition_index_at_every_vintage() {
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
assert!(
cold_has_fold_index(&harness, &cold).await,
"a freshly archived file has no idx_cold_txlog_fold_partition, so the \
cold half of the union sorts and the measured 96.3 ms is 110.1"
);
{
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
conn.execute("DROP INDEX cold.idx_cold_txlog_fold_partition", ())
.await
.unwrap();
conn.execute("ALTER TABLE cold.transaction_log DROP COLUMN branch_id", ())
.await
.unwrap();
}
assert!(
!cold_has_fold_index(&harness, &cold).await,
"the wind-back did not take, so the arm below would pass on a file \
that never lost the index"
);
let db = Database::open(&harness.db_path).await.unwrap();
db.upsert_concept(ConceptUpsert::new("c", "C").valid_from(TS))
.await
.unwrap();
db.archive(CUTOFF).await.unwrap();
db.close().await.unwrap();
assert!(
cold_has_fold_index(&harness, &cold).await,
"the archive that upgraded the file did not give it the index. If it \
failed instead, the index is being created before the column it \
names — see `COLD_LINEAGE_INDICES` for why the two are separate lists"
);
}
async fn cold_has_fold_index(harness: &TestHarness, cold: &std::path::Path) -> bool {
let conn = connect(harness).await;
attach_cold(&conn, cold).await;
count(
&conn,
"SELECT COUNT(*) FROM cold.sqlite_master \
WHERE type = 'index' AND name = 'idx_cold_txlog_fold_partition'",
(),
)
.await
== 1
}
#[tokio::test]
async fn a_reconstruction_spans_a_v11_cold_file_and_a_v12_hot_one() {
const AS_OF: &str = "2099-06-01T00:00:00.000000Z";
const LATE: &str = "2099-03-01T00:00:00.000000Z";
const LATER: &str = "2099-09-01T00:00:00.000000Z";
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
{
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
conn.execute(
"DROP INDEX IF EXISTS cold.idx_cold_txlog_fold_partition",
(),
)
.await
.unwrap();
conn.execute("ALTER TABLE cold.transaction_log DROP COLUMN branch_id", ())
.await
.unwrap();
conn.execute(
"INSERT INTO cold.transaction_log \
(table_name, entity_id, operation, payload, recorded_at) \
VALUES ('concepts', 'only_in_cold', 'I', \
json_object('v', 2, 'title', 'Cold', 'content', '', \
'valid_from', ?1, 'valid_to', NULL, \
'retired', 0, 'embedding_model', NULL), ?2)",
libsql::params![TS, LATE],
)
.await
.unwrap();
conn.execute("DETACH DATABASE cold", ()).await.unwrap();
}
{
let conn = connect(&harness).await;
conn.execute(
"INSERT INTO branches (branch_id, parent_id, forked_at, created_at) \
VALUES ('b', ?1, ?2, ?2)",
libsql::params![ddl::MAIN_BRANCH, TS2],
)
.await
.unwrap();
conn.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('only_on_b', 'B', ?1, ?2, 'b')",
libsql::params![TS3, LATE],
)
.await
.unwrap();
conn.execute(
"INSERT INTO concepts (id, title, valid_from, recorded_at, branch_id) \
VALUES ('after_the_question', 'Later', ?1, ?2, ?3)",
libsql::params![TS3, LATER, ddl::MAIN_BRANCH],
)
.await
.unwrap();
}
let conn = connect(&harness).await;
let state = macrame::temporal::reconstruct(&conn, AS_OF, Some(&cold), None)
.await
.expect(
"the fold must span a cold file without branch_id and a hot one \
with it; a column-count failure here means the two-shape \
projection is not being selected",
);
assert!(
state.concepts.contains_key("only_in_cold"),
"the row held only by the pre-v12 cold file is missing, so the cold arm \
contributed nothing and whatever this test proved, it was not about \
the projection"
);
assert!(
state.concepts.contains_key("only_on_b"),
"the hot lineage's own concept is missing from the reconstruction"
);
assert!(
!state.concepts.contains_key("after_the_question"),
"a row recorded after the instant asked about is in the answer, so the \
fold is not bounded by recorded_at and the two assertions above prove \
less than they appear to"
);
}
#[tokio::test]
async fn rehydration_restores_the_lineage_and_does_not_upgrade_what_it_reads() {
let harness = TestHarness::new();
let cold = archived_pair(&harness).await;
{
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
conn.execute("ALTER TABLE cold.concepts DROP COLUMN branch_id", ())
.await
.unwrap();
}
let archived: Vec<String> = {
let conn = connect(&harness).await;
attach_cold(&conn, &cold).await;
let mut rows = conn
.query("SELECT id FROM cold.concepts", ())
.await
.unwrap();
let mut out = Vec::new();
while let Some(r) = rows.next().await.unwrap() {
out.push(r.get::<String>(0).unwrap());
}
out
};
if archived.is_empty() {
panic!("the fixture archived no concepts, so this test proves nothing");
}
let db = Database::open(&harness.db_path).await.unwrap();
let ids: Vec<&str> = archived.iter().map(String::as_str).collect();
db.rehydrate(&ids).await.unwrap();
db.close().await.unwrap();
let conn = connect(&harness).await;
for id in &archived {
let branch: String = text(
&conn,
"SELECT branch_id FROM concepts WHERE id = ?1",
libsql::params![id.as_str()],
)
.await;
assert_eq!(
branch,
ddl::MAIN_BRANCH,
"a concept rehydrated from a cold file that predates the column \
must come back on the trunk, not with a null the NOT NULL would \
have refused"
);
}
attach_cold(&conn, &cold).await;
assert!(
!columns(&conn, "cold", "concepts")
.await
.contains(&"branch_id".into()),
"rehydration wrote to the cold file it was only supposed to read"
);
}
trait ExpectErrMsg {
fn unwrap_err_or_else_msg(self, label: &str) -> String;
}
impl<T> ExpectErrMsg for Result<T, libsql::Error> {
fn unwrap_err_or_else_msg(self, label: &str) -> String {
match self {
Ok(_) => panic!("{label}: the statement was accepted and should not have been"),
Err(e) => e.to_string(),
}
}
}