#![allow(clippy::unwrap_used, clippy::expect_used)]
use sqlite_core::{CohortTopology, Database, MaterializationSafety, WalLsn, WalValidationError};
const CARVE_MAIN: &[u8] = include_bytes!("../../tests/data/wal_carve.db");
const CARVE_WAL: &[u8] = include_bytes!("../../tests/data/wal_carve.db-wal");
#[test]
fn timeline_enumerates_one_segment_two_commit_snapshots() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
assert_eq!(tl.segments().len(), 1, "single salt segment");
let seg = &tl.segments()[0];
assert_eq!(seg.frame_count, 2, "two frames in the segment");
assert!(
seg.salt1 != 0 && seg.salt2 != 0,
"segment carries WAL salts"
);
assert_eq!(seg.page_size, db.header().page_size);
let snaps = tl.commit_snapshots();
assert_eq!(snaps.len(), 2, "two materializable commit snapshots");
assert_eq!(snaps[0].id().commit_frame_index, 0);
assert_eq!(snaps[1].id().commit_frame_index, 1);
assert_eq!(snaps[0].id().db_size_after_commit, 2);
assert_eq!(snaps[1].id().db_size_after_commit, 2);
assert_eq!(snaps[0].id().segment, seg.id);
assert_eq!(snaps[1].id().segment, seg.id);
}
#[test]
fn single_segment_topology_is_linear() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
assert_eq!(
tl.topology(),
CohortTopology::LinearSegment,
"one salt epoch with no reset is a linear cohort"
);
}
#[test]
fn materialize_last_commit_recovers_surviving_rows_not_deleted_ones() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let snaps = tl.commit_snapshots();
assert_eq!(snaps[1].db_size_after_commit(), 2);
let page2 = snaps[1]
.page_version(2)
.expect("page 2 present at last commit");
assert_eq!(page2.page_no, 2);
assert_eq!(page2.bytes.len(), db.header().page_size as usize);
let surviving = b"secret WAL body 101";
assert!(
page2.bytes.windows(surviving.len()).any(|w| w == surviving),
"surviving row 101 present in the materialized last-commit page"
);
}
#[test]
fn materialize_first_commit_holds_rows_before_the_delete() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let snaps = tl.commit_snapshots();
let page2 = snaps[0]
.page_version(2)
.expect("page 2 present at first commit");
let deleted_later = b"secret WAL body 130";
assert!(
page2
.bytes
.windows(deleted_later.len())
.any(|w| w == deleted_later),
"row 130 is live at the INSERT commit (deleted only at the second commit)"
);
}
#[test]
fn snapshot_enumerates_its_materialized_page_numbers() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let snaps = tl.commit_snapshots();
for snap in snaps {
let pages = snap.page_numbers();
assert!(
pages.windows(2).all(|w| w[0] < w[1]),
"page numbers ascending & unique: {pages:?}"
);
for &p in &pages {
assert!(
snap.page_version(p).is_some(),
"page {p} enumerated but page_version returned None"
);
assert!(p <= snap.db_size_after_commit(), "page {p} within db size");
}
}
assert_eq!(snaps[0].page_numbers(), vec![1, 2]);
assert_eq!(snaps[1].page_numbers(), vec![1, 2]);
}
#[test]
fn materialize_by_commit_id_is_stable() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let snaps = tl.commit_snapshots();
let id = snaps[0].id();
let resolved = tl.snapshot_at(id).expect("resolve by CommitId");
assert_eq!(resolved.id(), id);
assert_eq!(
resolved.db_size_after_commit(),
snaps[0].db_size_after_commit()
);
}
#[test]
fn diff_base_vs_last_commit_identifies_changed_pages() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let diff = tl.diff_base_to_last_commit().expect("diff present");
assert!(
diff.changed_pages().contains(&2),
"page 2 changed between the acquired base and the last WAL commit"
);
assert!(
!diff.changed_pages().contains(&1),
"page 1 (schema) is unchanged across the segment"
);
}
#[test]
fn valid_wal_clears_all_three_validation_tiers() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
assert_eq!(
tl.safety(),
MaterializationSafety::ReplaySafe,
"a well-formed single-segment WAL is replay-safe"
);
}
#[test]
fn page_size_mismatch_is_a_hard_stop_at_physical_validation() {
let mut bad_wal = CARVE_WAL.to_vec();
bad_wal[8..12].copy_from_slice(&8192u32.to_be_bytes());
let err = Database::wal_timeline_from(CARVE_MAIN, &bad_wal)
.expect_err("page-size mismatch must be rejected");
assert_eq!(
err,
WalValidationError::PageSizeMismatch {
db: 4096,
wal: 8192
}
);
}
#[test]
fn checksum_break_in_header_magic_is_rejected_at_physical_validation() {
let mut bad_wal = CARVE_WAL.to_vec();
bad_wal[0] ^= 0xFF;
let err =
Database::wal_timeline_from(CARVE_MAIN, &bad_wal).expect_err("bad magic must be rejected");
assert_eq!(err, WalValidationError::BadMagic);
}
#[test]
fn absent_wal_yields_no_timeline_not_an_error() {
let db = Database::open(CARVE_MAIN.to_vec()).expect("open");
assert!(db.wal_timeline().is_none(), "no WAL → no timeline");
}
#[test]
fn lsn_seam_exposes_salt_qualified_frame_index() {
let db = Database::open_with_wal(CARVE_MAIN.to_vec(), CARVE_WAL).expect("open with wal");
let tl = db.wal_timeline().expect("timeline present");
let snaps = tl.commit_snapshots();
let lsn: WalLsn = snaps[1].lsn();
assert_eq!(lsn.frame_index, 1);
let seg = &tl.segments()[0];
assert_eq!(lsn.salt1, seg.salt1, "LSN is salt-qualified, not bare");
assert_eq!(lsn.salt2, seg.salt2);
assert!(
!tl.checksums_are_tamper_evident(),
"WAL checksums are non-cryptographic — corruption detection only"
);
}
const PS: usize = 4096;
fn wal_header(salt1: u32, salt2: u32, ckpt_seq: u32) -> Vec<u8> {
let mut h = vec![0u8; 32];
h[0..4].copy_from_slice(&0x377f_0682u32.to_be_bytes());
h[4..8].copy_from_slice(&3_007_000u32.to_be_bytes());
h[8..12].copy_from_slice(&(PS as u32).to_be_bytes());
h[12..16].copy_from_slice(&ckpt_seq.to_be_bytes());
h[16..20].copy_from_slice(&salt1.to_be_bytes());
h[20..24].copy_from_slice(&salt2.to_be_bytes());
h
}
fn push_frame(
buf: &mut Vec<u8>,
page_no: u32,
db_size: u32,
salt1: u32,
salt2: u32,
marker: &[u8],
) {
let mut fh = vec![0u8; 24];
fh[0..4].copy_from_slice(&page_no.to_be_bytes());
fh[4..8].copy_from_slice(&db_size.to_be_bytes());
fh[8..12].copy_from_slice(&salt1.to_be_bytes());
fh[12..16].copy_from_slice(&salt2.to_be_bytes());
buf.extend_from_slice(&fh);
let mut page = vec![0u8; PS];
page[..marker.len()].copy_from_slice(marker);
buf.extend_from_slice(&page);
}
fn synth_main() -> Vec<u8> {
let mut db = vec![0u8; PS * 2];
db[..16].copy_from_slice(b"SQLite format 3\0");
db[16..18].copy_from_slice(&(PS as u16).to_be_bytes());
db[28..32].copy_from_slice(&2u32.to_be_bytes()); db
}
#[test]
fn salt_reset_opens_a_second_disconnected_segment() {
let main = synth_main();
let mut wal = wal_header(0x1111_1111, 0x2222_2222, 1);
push_frame(&mut wal, 2, 2, 0x1111_1111, 0x2222_2222, b"SEG-A-COMMIT");
push_frame(&mut wal, 2, 2, 0x3333_3333, 0x4444_4444, b"SEG-B-COMMIT");
let tl = Database::wal_timeline_from(&main, &wal).expect("timeline");
assert_eq!(tl.segments().len(), 2, "salt reset opens a second segment");
assert_eq!(
tl.topology(),
CohortTopology::Disconnected,
"two salt epochs are a disconnected cohort"
);
let snaps = tl.commit_snapshots();
assert_eq!(snaps.len(), 2);
assert_eq!(snaps[0].id().segment, tl.segments()[0].id);
assert_eq!(snaps[1].id().segment, tl.segments()[1].id);
assert_ne!(
tl.segments()[0].salt1,
tl.segments()[1].salt1,
"the second segment is a different checkpoint generation"
);
assert_eq!(tl.page_size(), PS as u32);
}
#[test]
fn uncommitted_tail_is_surfaced_as_residue_not_history() {
let main = synth_main();
let mut wal = wal_header(0xAAAA_AAAA, 0xBBBB_BBBB, 1);
push_frame(&mut wal, 2, 2, 0xAAAA_AAAA, 0xBBBB_BBBB, b"COMMITTED");
push_frame(
&mut wal,
2,
0,
0xAAAA_AAAA,
0xBBBB_BBBB,
b"UNCOMMITTED-TAIL",
);
let tl = Database::wal_timeline_from(&main, &wal).expect("timeline");
assert_eq!(tl.commit_snapshots().len(), 1, "one commit snapshot");
let res = tl.residue();
assert_eq!(res.len(), 1, "the uncommitted tail is one residue span");
assert_eq!(res[0].first_frame_index, 1, "residue starts at frame 1");
assert_eq!(res[0].frame_count, 1);
assert!(matches!(
res[0].reason,
sqlite_core::ResidueReason::BeyondLastCommit
));
assert_eq!(tl.safety(), MaterializationSafety::ReplaySafe);
}
#[test]
fn no_commit_frames_yields_physical_validated_only() {
let main = synth_main();
let mut wal = wal_header(0xCAFE_BABE, 0xDEAD_BEEF, 1);
push_frame(&mut wal, 2, 0, 0xCAFE_BABE, 0xDEAD_BEEF, b"NO-COMMIT");
let tl = Database::wal_timeline_from(&main, &wal).expect("timeline");
assert!(tl.commit_snapshots().is_empty(), "no materializable state");
assert_eq!(
tl.safety(),
MaterializationSafety::PhysicalValidated,
"physically valid but no commit → no replay"
);
assert_eq!(tl.residue().len(), 1);
}
#[test]
fn header_only_wal_has_no_segments_no_snapshots() {
let main = synth_main();
let wal = wal_header(0x0102_0304, 0x0506_0708, 1);
let tl = Database::wal_timeline_from(&main, &wal).expect("timeline");
assert!(tl.segments().is_empty(), "no frames → no segment");
assert!(tl.commit_snapshots().is_empty());
assert!(tl.residue().is_empty());
assert_eq!(tl.safety(), MaterializationSafety::PhysicalValidated);
assert_eq!(
tl.topology(),
CohortTopology::LinearSegment,
"zero/one segment is linear"
);
assert!(tl.diff_base_to_last_commit().is_none(), "no commit to diff");
}
#[test]
fn zero_page_no_frame_halts_the_scan() {
let main = synth_main();
let mut wal = wal_header(0x1212_1212, 0x3434_3434, 1);
push_frame(&mut wal, 2, 2, 0x1212_1212, 0x3434_3434, b"GOOD-COMMIT");
push_frame(&mut wal, 0, 0, 0x1212_1212, 0x3434_3434, b"BAD-PAGE-ZERO");
let tl = Database::wal_timeline_from(&main, &wal).expect("timeline");
assert_eq!(tl.commit_snapshots().len(), 1, "the good commit survives");
assert_eq!(
tl.segments()[0].frame_count,
1,
"the zero-page frame is dropped"
);
}