#[path = "common/harness.rs"]
mod harness;
use std::time::Duration;
use harness::TestHarness;
use macrame::graph::EdgeAssertion;
use macrame::integrity::{audit_current, rebuild_current};
use macrame::{ConceptUpsert, Database};
const EPOCH: &str = "1970-01-01T00:00:00.000000Z";
const OPEN: &str = "9999-12-31T23:59:59.999999Z";
const CLOSED: &str = "1970-01-01T00:30:00.000000Z";
const HOUR: Duration = Duration::from_secs(3_600);
async fn projection(
conn: &libsql::Connection,
) -> Vec<(String, String, String, String, f64, String)> {
let mut out = Vec::new();
let mut rows = conn
.query(
"SELECT source_id, target_id, edge_type, valid_to, weight, branch_id \
FROM links_current ORDER BY source_id, target_id, edge_type, valid_from, branch_id",
(),
)
.await
.unwrap();
while let Some(row) = rows.next().await.unwrap() {
out.push((
row.get(0).unwrap(),
row.get(1).unwrap(),
row.get(2).unwrap(),
row.get(3).unwrap(),
row.get(4).unwrap(),
row.get(5).unwrap(),
));
}
out
}
async fn seed(db: &Database, harness: &TestHarness) {
db.write_concepts(
["a", "b", "c", "d", "e"]
.iter()
.map(|id| ConceptUpsert::new(*id, "n").valid_from(EPOCH))
.collect(),
)
.await
.unwrap();
db.bulk_import(vec![
EdgeAssertion::new("a", "b", "LINKS")
.valid_from(EPOCH)
.valid_to(OPEN)
.weight(1.0),
EdgeAssertion::new("a", "c", "LINKS")
.valid_from(EPOCH)
.valid_to(CLOSED)
.weight(1.0),
EdgeAssertion::new("a", "d", "LINKS")
.valid_from(EPOCH)
.valid_to(CLOSED)
.weight(1.0),
EdgeAssertion::new("a", "e", "LINKS")
.valid_from(EPOCH)
.valid_to(OPEN)
.weight(1.0),
])
.await
.unwrap();
harness.advance(HOUR);
db.bulk_import(vec![
EdgeAssertion::new("a", "b", "LINKS")
.valid_from(EPOCH)
.valid_to(OPEN)
.weight(2.0),
EdgeAssertion::new("a", "d", "LINKS")
.valid_from(EPOCH)
.valid_to(CLOSED)
.weight(2.0),
])
.await
.unwrap();
harness.advance(HOUR);
}
#[tokio::test]
async fn the_keyed_repair_leaves_what_the_full_rebuild_would() {
let harness = TestHarness::new();
let db = harness.db_with_fake_clock().await;
seed(&db, &harness).await;
let report = db.archive(&harness.clock.peek()).await.unwrap();
assert!(
report.links_archived >= 4,
"the fixture archived nothing to repair around: {report:?}"
);
let after_keyed = projection(db.read_conn()).await;
assert_eq!(audit_current(db.read_conn()).await.unwrap(), 0);
db.close().await.unwrap();
let raw = libsql::Builder::new_local(&harness.db_path)
.build()
.await
.unwrap()
.connect()
.unwrap();
rebuild_current(&raw).await.unwrap();
let after_full = projection(&raw).await;
assert_eq!(
after_keyed, after_full,
"the keyed repair and the full rebuild disagree about current belief"
);
}
#[tokio::test]
async fn a_key_whose_last_belief_is_archived_leaves_and_a_superseded_one_stays() {
let harness = TestHarness::new();
let db = harness.db_with_fake_clock().await;
seed(&db, &harness).await;
db.archive(&harness.clock.peek()).await.unwrap();
let rows = projection(db.read_conn()).await;
let targets: Vec<&str> = rows.iter().map(|r| r.1.as_str()).collect();
assert_eq!(
targets,
vec!["b", "e"],
"a → c and a → d had their last belief archived and must be gone; \
a → b shed a superseded row and must remain: {rows:?}"
);
assert_eq!(
rows[0].4, 2.0,
"the surviving belief at a → b is the later one, not the archived one"
);
assert_eq!(rows[1].4, 1.0, "a → e was never touched");
db.close().await.unwrap();
}
#[tokio::test]
async fn an_archive_with_nothing_to_delete_leaves_the_projection_untouched() {
let harness = TestHarness::new();
let db = harness.db_with_fake_clock().await;
seed(&db, &harness).await;
let before = projection(db.read_conn()).await;
let report = db.archive(EPOCH).await.unwrap();
assert_eq!(report.links_archived, 0, "the cutoff admitted something");
assert_eq!(projection(db.read_conn()).await, before);
assert_eq!(audit_current(db.read_conn()).await.unwrap(), 0);
db.close().await.unwrap();
}