#![allow(clippy::unwrap_used, clippy::expect_used, clippy::pedantic)]
use sqlite_core::{Database, Value};
use sqlite_forensic::carve_all_deleted_records;
fn recovered_texts(db: &Database) -> Vec<String> {
carve_all_deleted_records(db)
.into_iter()
.flat_map(|r| r.values)
.filter_map(|v| match v {
Value::Text(t) => Some(t),
_ => None,
})
.collect()
}
#[test]
fn recovers_deleted_rows_with_two_byte_rowid() {
let bytes = std::fs::read("../tests/data/freeblock_2byte_rowid.db")
.or_else(|_| std::fs::read("tests/data/freeblock_2byte_rowid.db"))
.expect("fixture readable");
let db = Database::open(bytes).expect("fixture opens");
let texts = recovered_texts(&db);
for id in [1050, 1130, 1210] {
let want = format!("line text for record {id}");
assert!(
texts.iter().any(|t| t == &want),
"2-byte-rowid row {id} must recover (freeblock-clobbered, all serials \
survive); got {texts:?}"
);
}
}
#[test]
fn recovers_coalesced_two_byte_rowid_rows() {
let bytes = std::fs::read("../tests/data/freeblock_coalesced.db")
.or_else(|_| std::fs::read("tests/data/freeblock_coalesced.db"))
.expect("fixture readable");
let db = Database::open(bytes).expect("fixture opens");
let texts = recovered_texts(&db);
let recovered = (1100..=1104)
.filter(|id| {
texts
.iter()
.any(|t| t == &format!("line text for record {id}"))
})
.count();
assert!(
recovered >= 4,
"coalesced freeblock: expected >= 4 of ids 1100..=1104 recovered, got {recovered}; \
texts={texts:?}"
);
for t in &texts {
if t.contains("line text for record") {
assert!(
t.starts_with("line text for record") && !t.chars().any(char::is_control),
"coalesced recovery emitted a misaligned phantom: {t:?}"
);
}
}
}
#[test]
fn two_byte_rowid_recovery_emits_no_misaligned_phantom() {
let bytes = std::fs::read("../tests/data/freeblock_2byte_rowid.db")
.or_else(|_| std::fs::read("tests/data/freeblock_2byte_rowid.db"))
.expect("fixture readable");
let db = Database::open(bytes).expect("fixture opens");
for t in recovered_texts(&db) {
if t.contains("line text for record") {
assert!(
t.starts_with("line text for record"),
"misaligned phantom (leading junk before the real value): {t:?}"
);
assert!(
!t.contains('\u{FFFD}') && !t.chars().any(|c| c.is_control()),
"misaligned phantom (control/replacement char in body): {t:?}"
);
}
}
}