#![allow(clippy::unwrap_used, clippy::expect_used)]
use sqlite_core::{Database, Value};
use sqlite_forensic::{carve_deleted_records, AnomalyKind};
const DB: &[u8] = include_bytes!("../../tests/data/deleted_places.db");
const MOZ_PLACES_COLS: usize = 6;
#[test]
fn smoke_recovers_deleted_rows_from_freed_pages() {
let db = Database::open(DB.to_vec()).expect("open deleted_places.db");
let carved = carve_deleted_records(&db, MOZ_PLACES_COLS);
assert!(
!carved.is_empty(),
"carver returned nothing on a fixture with ~200 deleted rows"
);
for rec in &carved {
assert!(!rec.allocated, "carved record must be flagged unallocated");
assert!(rec.page >= 1, "carved record carries its source page");
assert!(
rec.confidence > 0.0 && rec.confidence <= 1.0,
"confidence is a probability"
);
assert_eq!(rec.values.len(), MOZ_PLACES_COLS);
}
let r300 = carved
.iter()
.find(|r| r.rowid == 300)
.expect("deleted row 300 must be carved");
assert_eq!(
r300.values[1],
Value::Text("https://site-300.example.com/path/page".into())
);
assert_eq!(
r300.values[2],
Value::Text("Title for record number 300 SECRETMARKER".into())
);
assert!(
carved.iter().all(|r| r.rowid > 200),
"carver must not return live (allocated) rows"
);
}
#[test]
fn carving_is_a_residue_anomaly_finding() {
let db = Database::open(DB.to_vec()).expect("open");
let carved = carve_deleted_records(&db, MOZ_PLACES_COLS);
assert!(!carved.is_empty());
let kind = AnomalyKind::DeletedRecordRecovered {
page: carved[0].page,
offset: carved[0].offset,
rowid: carved[0].rowid,
};
assert_eq!(kind.code(), "SQLITE-DELETED-RECORD-RECOVERED");
}