use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use bison_db::{Db, Document};
use proptest::prelude::*;
fn temp_path() -> PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
std::env::temp_dir().join(format!("bison_db_robust_{pid}_{n}.bison"))
}
fn valid_store_bytes(doc_count: usize) -> Vec<u8> {
let path = temp_path();
let _ = std::fs::remove_file(&path);
{
let mut db = Db::open(&path).unwrap();
for i in 0..doc_count {
let mut d = Document::new();
d.set("i", i as i64).set("name", "robustness");
db.insert(d).unwrap();
}
db.flush().unwrap();
}
let bytes = std::fs::read(&path).unwrap();
let _ = std::fs::remove_file(&path);
bytes
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(96))]
#[test]
fn prop_open_arbitrary_bytes_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..1024)) {
let path = temp_path();
let _ = std::fs::remove_file(&path);
std::fs::write(&path, &bytes).unwrap();
if let Ok(db) = Db::open(&path) {
prop_assert_eq!(db.len(), db.stats().live_documents);
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn prop_corrupted_store_open_never_panics(
doc_count in 1usize..16,
mutations in proptest::collection::vec((any::<u16>(), any::<u8>()), 1..16),
) {
let mut bytes = valid_store_bytes(doc_count);
if bytes.is_empty() {
return Ok(());
}
for (pos, val) in mutations {
let idx = (pos as usize) % bytes.len();
bytes[idx] = val;
}
let path = temp_path();
let _ = std::fs::remove_file(&path);
std::fs::write(&path, &bytes).unwrap();
if let Ok(db) = Db::open(&path) {
prop_assert!(db.len() <= doc_count + 1);
for id in db.ids() {
let _ = db.get(id);
}
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn prop_truncated_store_open_never_panics(doc_count in 1usize..16, cut in any::<u16>()) {
let bytes = valid_store_bytes(doc_count);
let keep = (cut as usize) % (bytes.len() + 1);
let path = temp_path();
let _ = std::fs::remove_file(&path);
{
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(&bytes[..keep]).unwrap();
}
if let Ok(db) = Db::open(&path) {
prop_assert_eq!(db.len(), db.stats().live_documents);
}
let _ = std::fs::remove_file(&path);
}
}