#![allow(clippy::unwrap_used, clippy::expect_used, clippy::pedantic)]
use std::path::{Path, PathBuf};
use sqlite_core::{Database, Error};
fn cfreds(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../tests/data/cfreds")
.join(name)
}
fn prior_table_rows(snapshot: &sqlite_core::PriorSnapshot, table: &str) -> usize {
let Some(t) = snapshot.tables().into_iter().find(|t| t.name == table) else {
return 0;
};
snapshot
.read_table(t.rootpage, t.columns.len())
.map(|rows| rows.len())
.unwrap_or(0)
}
#[test]
fn prior_snapshot_holds_pre_delete_invoice_items() {
for platform in ["ios", "android"] {
let main = std::fs::read(cfreds(&format!("SFT-03_PERSIST_{platform}.sqlite"))).unwrap();
let journal =
std::fs::read(cfreds(&format!("SFT-03_PERSIST_{platform}.sqlite-journal"))).unwrap();
let db = Database::open(main).expect("open main");
let live = db
.live_table_rows()
.iter()
.find(|d| d.name == "invoice_items")
.map_or(0, |d| d.rows.len());
assert_eq!(live, 2140, "{platform}: live db is the post-delete state");
let prior = db
.rollback_prior(&journal)
.expect("materialize prior snapshot");
assert_eq!(
prior_table_rows(&prior, "invoice_items"),
2240,
"{platform}: the prior snapshot holds all 2240 pre-delete rows"
);
assert!(
!prior.grew_db(),
"{platform}: a delete-only transaction does not grow the database"
);
}
}
const OVERFLOW_DB: &[u8] = include_bytes!("../../tests/data/overflow.db");
const NOTES_ROOT: u32 = 2;
const NOTES_COLS: usize = 2;
#[test]
fn prior_snapshot_reassembles_overflow_rows() {
let db = Database::open(OVERFLOW_DB.to_vec()).expect("open overflow.db");
let prior = db
.rollback_prior(&[0u8; 512])
.expect("empty journal → prior == live");
let via_pages = prior
.read_table_with_pages(NOTES_ROOT, NOTES_COLS)
.expect("walk notes with pages");
assert_eq!(via_pages.len(), 2, "two rows including the spilled one");
let spilled = via_pages.iter().find(|(rowid, _, _)| *rowid == 1).unwrap();
if let sqlite_core::Value::Text(body) = &spilled.1[1] {
assert_eq!(body.len(), 12017, "the spilled body reassembles fully");
} else {
panic!("expected a TEXT body");
}
let plain = prior
.read_table(NOTES_ROOT, NOTES_COLS)
.expect("walk notes");
assert_eq!(plain.len(), 2);
}
#[test]
fn rollback_prior_rejects_wal_applied_database() {
let main = std::fs::read(cfreds("sft-03-WAL_ios.sqlite")).unwrap();
let wal = std::fs::read(cfreds("sft-03-WAL_ios.sqlite-wal")).unwrap();
let db = Database::open_with_wal(main, &wal).expect("open_with_wal");
assert!(db.wal_applied());
let journal = std::fs::read(cfreds("SFT-03_PERSIST_ios.sqlite-journal")).unwrap();
let err = db
.rollback_prior(&journal)
.expect_err("a WAL-applied db must not accept a rollback journal");
assert_eq!(err, Error::JournalModeConflict);
}