#[path = "common/harness.rs"]
mod harness;
use harness::TestHarness;
use macrame::prelude::*;
use std::sync::Arc;
const T1: &str = "2026-01-01T00:00:00.000000Z";
fn eager() -> SnapshotCadence {
SnapshotCadence {
every_entries: 1,
poll_interval: std::time::Duration::from_millis(5),
}
}
async fn write_one(db: &Database) {
db.write_concepts(vec![ConceptUpsert::new("a", "A").valid_from(T1)])
.await
.unwrap();
}
fn anchors(harness: &TestHarness) -> usize {
std::fs::read_dir(harness.db_path.with_file_name(format!(
"{}_snapshots",
harness.db_path.file_stem().unwrap().to_str().unwrap()
)))
.map(|d| d.count())
.unwrap_or(0)
}
#[test]
fn the_default_tuning_asks_for_the_default_cadence() {
let tuning = Tuning::default();
assert_eq!(
tuning.cadence,
CadencePolicy::Default,
"Tuning::default() must mean what Database::open() means. If this is \
Disabled, every caller who wrote `..Default::default()` has silently \
stopped writing snapshot anchors."
);
assert!(
tuning.clock.is_none(),
"the default clock is the SystemClock, chosen by absence"
);
}
#[tokio::test]
async fn a_disabled_cadence_leaves_the_snapshot_directory_to_close() {
let harness = TestHarness::new();
let db = Database::open_tuned(
&harness.db_path,
Tuning {
cadence: CadencePolicy::Disabled,
..Default::default()
},
)
.await
.unwrap();
write_one(&db).await;
assert_eq!(anchors(&harness), 0, "nothing but close() writes an anchor");
db.close().await.unwrap();
assert_eq!(anchors(&harness), 1, "close() still writes the final one");
}
#[tokio::test]
async fn an_explicit_cadence_anchors_without_being_closed() {
let harness = TestHarness::new();
let db = Database::open_tuned(
&harness.db_path,
Tuning {
cadence: CadencePolicy::Every(eager()),
..Default::default()
},
)
.await
.unwrap();
for _ in 0..3 {
write_one(&db).await;
}
let mut seen = 0;
for _ in 0..100 {
seen = anchors(&harness);
if seen > 0 {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
assert!(seen > 0, "an eager cadence wrote no anchor in two seconds");
db.close().await.unwrap();
}
#[tokio::test]
async fn a_clock_injected_through_tuning_stamps_the_ledger() {
let harness = TestHarness::new();
let t0 = std::time::UNIX_EPOCH + std::time::Duration::from_secs(1_900_000_000);
let clock = Arc::new(FakeClock::new(t0));
let expected = clock.peek();
let db = Database::open_tuned(
&harness.db_path,
Tuning {
cadence: CadencePolicy::Disabled,
clock: Some(clock),
wal_autocheckpoint: WalCheckpointPolicy::Default,
writer_cache_size: None,
reader_cache_size: None,
future_stamps: FutureStampPolicy::Default,
},
)
.await
.unwrap();
write_one(&db).await;
let mut rows = db
.read_conn()
.query("SELECT recorded_at FROM concepts WHERE id = 'a'", ())
.await
.unwrap();
let stamp: String = rows.next().await.unwrap().unwrap().get(0).unwrap();
assert_eq!(
stamp, expected,
"the injected clock did not reach the actor through Tuning"
);
db.close().await.unwrap();
}
#[tokio::test]
async fn the_legacy_none_still_means_disabled() {
let harness = TestHarness::new();
let db = Database::open_with_cadence(&harness.db_path, None)
.await
.unwrap();
write_one(&db).await;
assert_eq!(
anchors(&harness),
0,
"open_with_cadence(path, None) acquired a cadence it declined"
);
db.close().await.unwrap();
}
const A_STAMP_FROM_THE_FUTURE: u64 = 3_000_000_000;
async fn seed_at(path: &std::path::Path, secs: u64) {
let clock = Arc::new(FakeClock::new(
std::time::UNIX_EPOCH + std::time::Duration::from_secs(secs),
));
let db = Database::open_tuned(
path,
Tuning {
cadence: CadencePolicy::Disabled,
clock: Some(clock),
..Default::default()
},
)
.await
.expect("seeding must succeed: an empty database has no floor to refuse");
db.upsert_concept(ConceptUpsert::new("a", "A").valid_from(T1))
.await
.unwrap();
db.close().await.unwrap();
}
#[tokio::test]
async fn a_recorded_at_from_the_future_is_refused_at_open() {
let harness = TestHarness::new();
seed_at(&harness.db_path, A_STAMP_FROM_THE_FUTURE).await;
let err = match Database::open(&harness.db_path).await {
Ok(_) => panic!("a floor from 2065 must not be absorbed"),
Err(e) => e,
};
match &err {
DbError::FutureRecordedAt { stamp, limit } => {
assert!(stamp.starts_with("2065-"), "the stored stamp: {stamp}");
assert!(
limit.as_str() > "2026-",
"the limit must be a real timestamp: {limit}"
);
}
other => panic!("expected FutureRecordedAt, got {other:?}"),
}
assert!(err.to_string().contains("future_stamps"), "{err}");
assert!(err.to_string().contains("allow"), "{err}");
}
#[tokio::test]
async fn allow_opens_a_database_the_default_refuses() {
let harness = TestHarness::new();
seed_at(&harness.db_path, A_STAMP_FROM_THE_FUTURE).await;
let db = Database::open_tuned(
&harness.db_path,
Tuning {
cadence: CadencePolicy::Disabled,
future_stamps: FutureStampPolicy::Allow,
..Default::default()
},
)
.await
.expect("Allow must waive the bound");
let n: i64 = db
.read_conn()
.query("SELECT COUNT(*) FROM concepts", ())
.await
.unwrap()
.next()
.await
.unwrap()
.unwrap()
.get(0)
.unwrap();
assert_eq!(n, 1, "the file must be readable, which is the whole point");
db.close().await.unwrap();
}
#[tokio::test]
async fn a_database_stamped_now_reopens_under_the_default() {
let harness = TestHarness::new();
let db = Database::open(&harness.db_path).await.unwrap();
db.upsert_concept(ConceptUpsert::new("a", "A").valid_from(T1))
.await
.unwrap();
db.close().await.unwrap();
let db = Database::open(&harness.db_path)
.await
.expect("wall-clock stamps are not from the future");
db.close().await.unwrap();
}
#[tokio::test]
async fn a_zero_tolerance_refuses_a_stamp_that_the_default_would_accept() {
let harness = TestHarness::new();
let soon = std::time::SystemTime::now() + std::time::Duration::from_secs(60 * 60);
let secs = soon
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
seed_at(&harness.db_path, secs).await;
Database::open(&harness.db_path)
.await
.expect("an hour is within the default tolerance")
.close()
.await
.unwrap();
let err = Database::open_tuned(
&harness.db_path,
Tuning {
cadence: CadencePolicy::Disabled,
future_stamps: FutureStampPolicy::Tolerance(std::time::Duration::ZERO),
..Default::default()
},
)
.await;
let err = match err {
Ok(_) => panic!("zero tolerance admits nothing ahead of the wall clock"),
Err(e) => e,
};
assert!(matches!(err, DbError::FutureRecordedAt { .. }), "{err:?}");
}