#[path = "common/harness.rs"]
mod harness;
use harness::TestHarness;
use macrame::graph::{EdgeAssertion, TraversalBuilder};
use macrame::temporal::EdgeBelief;
use macrame::{BranchId, ConceptUpsert, Database, DbError, ReadPlan};
use std::time::Duration;
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 NOW: &str = "2026-06-01T00:00:00.000000Z";
fn edge(source: &str, target: &str, to: Option<&str>) -> EdgeAssertion {
let e = EdgeAssertion::new(source, target, "KNOWS").valid_from(TS);
match to {
Some(t) => e.valid_to(t),
None => e,
}
}
fn harness() -> TestHarness {
TestHarness::starting_at(macrame::util::parse_iso8601_utc(TS).unwrap())
}
async fn via_plan(db: &Database, plan: ReadPlan) -> Vec<(String, String, String)> {
let mut out: Vec<_> = db
.edges(plan)
.await
.unwrap()
.into_iter()
.map(|e: EdgeBelief| (e.source_id, e.target_id, e.branch_id))
.collect();
out.sort();
out
}
async fn via_free_fn(db: &Database, ts: &str, branch: Option<&str>) -> Vec<(String, String)> {
let mut out: Vec<_> = macrame::temporal::query_as_of_edges_on(db.read_conn(), ts, branch)
.await
.unwrap()
.into_iter()
.map(|(s, t, _, _, _)| (s, t))
.collect();
out.sort();
out
}
fn keys(rows: &[(String, String, String)]) -> Vec<(String, String)> {
rows.iter()
.map(|(s, t, _)| (s.clone(), t.clone()))
.collect()
}
async fn unforked(h: &TestHarness) -> Database {
let db = h.db_with_fake_clock().await;
for id in ["a", "b", "c", "d"] {
db.upsert_concept(ConceptUpsert::new(id, "N").valid_from(TS))
.await
.unwrap();
}
db.assert_edge(edge("a", "b", None)).await.unwrap();
db.assert_edge(edge("b", "c", Some(TS3))).await.unwrap();
db
}
#[tokio::test]
async fn an_empty_plan_reads_the_trunk_now_under_current_belief() {
let h = harness();
let db = unforked(&h).await;
h.advance(Duration::from_secs(180 * 86_400));
let now = db.clock().now();
assert_eq!(
keys(&via_plan(&db, ReadPlan::new()).await),
via_free_fn(&db, &now, None).await,
"an empty plan must be the read every other surface takes by default"
);
assert_eq!(
keys(&via_plan(&db, ReadPlan::new()).await),
vec![("a".to_string(), "b".to_string())],
"`b -> c` closed at TS3 and the clock is past it"
);
assert_eq!(
via_plan(&db, ReadPlan::new()).await,
via_plan(&db, ReadPlan::new().valid_at(&now)).await
);
db.close().await.unwrap();
}
#[tokio::test]
async fn the_window_is_half_open_at_both_ends() {
let h = harness();
let db = unforked(&h).await;
h.advance(Duration::from_secs(86_400));
let alt = BranchId::new("alt").unwrap();
db.fork(alt.clone(), BranchId::main()).await.unwrap();
for branch in [None, Some(alt.clone())] {
let plan = |ts: &str| {
let p = ReadPlan::new().valid_at(ts);
match &branch {
Some(b) => p.on(b.clone()),
None => p,
}
};
let at = |ts: &'static str| {
let p = plan(ts);
let db = &db;
async move { keys(&via_plan(db, p).await) }
};
assert_eq!(
at(TS).await,
vec![
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
],
"{branch:?}: `valid_from < ?1` would drop an interval at its own start"
);
assert_eq!(
at(TS3).await,
vec![("a".to_string(), "b".to_string())],
"{branch:?}: `?1 <= valid_to` would admit `b -> c` at the instant \
it closed, and a half-open interval is the crate's whole \
definition of one"
);
}
db.close().await.unwrap();
}
#[tokio::test]
async fn a_plan_and_the_free_function_answer_alike_on_every_shape() {
let h = harness();
let db = unforked(&h).await;
let plan = ReadPlan::new().valid_at(TS2);
assert_eq!(
keys(&via_plan(&db, plan.clone()).await),
via_free_fn(&db, TS2, None).await
);
h.advance(Duration::from_secs(86_400));
let alt = BranchId::new("alt").unwrap();
db.fork(alt.clone(), BranchId::main()).await.unwrap();
h.advance(Duration::from_secs(86_400));
db.assert_edge(edge("a", "b", None).weight(4.0).on_branch(alt.clone()))
.await
.unwrap();
db.assert_edge(edge("c", "d", None)).await.unwrap();
assert_eq!(
keys(&via_plan(&db, plan.clone()).await),
via_free_fn(&db, TS2, None).await
);
let on_alt = plan.on(alt.clone());
assert_eq!(
keys(&via_plan(&db, on_alt.clone()).await),
via_free_fn(&db, TS2, Some("alt")).await
);
assert_ne!(
keys(&via_plan(&db, on_alt).await),
keys(&via_plan(&db, ReadPlan::new().valid_at(TS2)).await),
"the trunk wrote `c -> d` after the fork; `alt` must not have it"
);
db.close().await.unwrap();
}
#[tokio::test]
async fn a_resolved_row_carries_the_lineage_that_holds_it() {
let h = harness();
let db = unforked(&h).await;
h.advance(Duration::from_secs(86_400));
let alt = BranchId::new("alt").unwrap();
db.fork(alt.clone(), BranchId::main()).await.unwrap();
h.advance(Duration::from_secs(86_400));
db.assert_edge(edge("a", "b", None).weight(4.0).on_branch(alt.clone()))
.await
.unwrap();
assert_eq!(
via_plan(&db, ReadPlan::new().valid_at(TS2).on(alt)).await,
vec![
("a".to_string(), "b".to_string(), "alt".to_string()),
("b".to_string(), "c".to_string(), "main".to_string()),
],
"the corrected edge is the branch's own row and the other is inherited"
);
db.close().await.unwrap();
}
#[tokio::test]
async fn a_recorded_instant_names_a_belief_no_other_edge_read_could_ask_for() {
let h = harness();
let db = unforked(&h).await;
h.advance(Duration::from_secs(86_400));
let alt = BranchId::new("alt").unwrap();
db.fork(alt.clone(), BranchId::main()).await.unwrap();
h.advance(Duration::from_secs(86_400));
db.assert_edge(edge("a", "b", None).weight(4.0).on_branch(alt.clone()))
.await
.unwrap();
let believed = db.clock().now();
h.advance(Duration::from_secs(86_400));
db.assert_edge(edge("c", "d", None)).await.unwrap();
db.assert_edge(edge("d", "a", None).on_branch(alt.clone()))
.await
.unwrap();
let at_ts2 = ReadPlan::new().valid_at(TS2);
assert_eq!(
keys(&via_plan(&db, at_ts2.clone().recorded_at(&believed)).await),
vec![
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
]
);
assert_eq!(
keys(&via_plan(&db, at_ts2.clone()).await),
vec![
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
("c".to_string(), "d".to_string()),
]
);
assert_eq!(
keys(&via_plan(&db, at_ts2.clone().on(alt.clone()).recorded_at(&believed)).await),
vec![
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
]
);
assert_eq!(
keys(&via_plan(&db, at_ts2.clone().on(alt.clone())).await),
vec![
("a".to_string(), "b".to_string()),
("b".to_string(), "c".to_string()),
("d".to_string(), "a".to_string()),
]
);
for plan in [at_ts2.valid_at(NOW), ReadPlan::new().valid_at(NOW)] {
for p in [plan.clone(), plan.recorded_at(&believed)] {
assert!(
!keys(&via_plan(&db, p.clone()).await)
.contains(&("b".to_string(), "c".to_string())),
"a closed interval is closed under every belief: {p:?}"
);
}
}
db.close().await.unwrap();
}
#[tokio::test]
async fn a_plan_survives_a_traversal_builder_in_both_directions() {
let full = ReadPlan::new()
.on(BranchId::new("alt").unwrap())
.valid_at(TS2)
.recorded_at(TS3);
for plan in [
ReadPlan::new(),
ReadPlan::new().valid_at(TS2),
ReadPlan::new().on(BranchId::new("alt").unwrap()),
full.clone(),
] {
let back = TraversalBuilder::new("a")
.plan(plan.clone())
.read_plan()
.unwrap();
assert_eq!(back, plan, "a plan changed shape going through a builder");
}
let b = TraversalBuilder::new("a")
.max_depth(7)
.on_branch("alt")
.as_of_valid(TS2);
let same = b.clone().plan(b.read_plan().unwrap());
assert_eq!(same.branch, b.branch);
assert_eq!(same.as_of_valid, b.as_of_valid);
assert_eq!(same.as_of_recorded, b.as_of_recorded);
assert_eq!(
same.max_depth, 7,
"a plan carries no depth and must not set one"
);
let cleared = TraversalBuilder::new("a").plan(full).plan(ReadPlan::new());
assert_eq!(cleared.branch, None);
assert_eq!(cleared.as_of_valid, None);
assert_eq!(cleared.as_of_recorded, None);
}
#[tokio::test]
async fn an_unregistered_lineage_is_refused_by_name() {
let h = harness();
let db = unforked(&h).await;
let err = db
.edges(ReadPlan::new().on(BranchId::new("ghost").unwrap()))
.await
.expect_err("a lineage that was never registered must not read as the trunk");
match &err {
DbError::UnknownBranch(branch) => assert_eq!(branch, "ghost"),
other => panic!("got {other:?}"),
}
db.close().await.unwrap();
}
#[tokio::test]
async fn a_malformed_instant_is_refused_on_either_axis() {
let h = harness();
let db = unforked(&h).await;
for plan in [
ReadPlan::new().valid_at("last Tuesday"),
ReadPlan::new().recorded_at("last Tuesday"),
] {
match db.edges(plan.clone()).await {
Err(DbError::InvalidTimestamp { .. }) => {}
other => panic!("{plan:?} gave {other:?}"),
}
}
db.close().await.unwrap();
}