use pagedb::vfs::memory::MemVfs;
use pagedb::{Db, OpenOptions, RealmId};
const PAGE: usize = 4096;
const REALM: RealmId = RealmId::new([0xEE; 16]);
const KEK: [u8; 32] = [0x33; 32];
async fn fresh_db() -> Db<MemVfs> {
Db::open(MemVfs::new(), KEK, PAGE, REALM, OpenOptions::default())
.await
.unwrap()
}
#[tokio::test(flavor = "current_thread")]
async fn large_value_write_read() {
let db = fresh_db().await;
let value = vec![0xABu8; 8192];
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"big-key", &value).await.unwrap();
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
let got = txn.get(b"big-key").await.unwrap().unwrap();
assert_eq!(got.as_ref(), value.as_slice());
}
#[tokio::test(flavor = "current_thread")]
async fn overwrite_frees_old_chain() {
let db = fresh_db().await;
let value_a = vec![0xAAu8; 8192];
let value_b = vec![0xBBu8; 8192];
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"key", &value_a).await.unwrap();
txn.commit().await.unwrap();
}
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"key", &value_b).await.unwrap();
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
let got = txn.get(b"key").await.unwrap().unwrap();
assert_eq!(got.as_ref(), value_b.as_slice());
}
#[tokio::test(flavor = "current_thread")]
async fn delete_frees_overflow_chain() {
let db = fresh_db().await;
let value = vec![0xCCu8; 8192];
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"del-key", &value).await.unwrap();
txn.commit().await.unwrap();
}
{
let mut txn = db.begin_write().await.unwrap();
let deleted = txn.delete(b"del-key").await.unwrap();
assert!(deleted);
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
let got = txn.get(b"del-key").await.unwrap();
assert!(got.is_none(), "key should be gone after delete");
}
#[tokio::test(flavor = "current_thread")]
async fn inline_and_overflow_coexist() {
let db = fresh_db().await;
let small = b"tiny";
let large = vec![0xDDu8; 8192];
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"small", small).await.unwrap();
txn.put(b"large", &large).await.unwrap();
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
assert_eq!(
txn.get(b"small").await.unwrap().unwrap().as_ref(),
small.as_ref()
);
assert_eq!(
txn.get(b"large").await.unwrap().unwrap().as_ref(),
large.as_slice()
);
}
#[tokio::test(flavor = "current_thread")]
async fn overflow_chain_three_pages() {
let db = fresh_db().await;
let value = vec![0xFFu8; 12_500];
{
let mut txn = db.begin_write().await.unwrap();
txn.put(b"three-page", &value).await.unwrap();
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
let got = txn.get(b"three-page").await.unwrap().unwrap();
assert_eq!(got.as_ref(), value.as_slice());
}
#[tokio::test(flavor = "current_thread")]
async fn many_overflow_writes_no_corruption() {
let db = fresh_db().await;
const N: usize = 50;
let large = vec![0xA0u8; 8192];
{
let mut txn = db.begin_write().await.unwrap();
for i in 0..N {
let key = format!("big-{i:04}");
txn.put(key.as_bytes(), &large).await.unwrap();
}
txn.commit().await.unwrap();
}
{
let mut txn = db.begin_write().await.unwrap();
for i in (0..N).step_by(2) {
let key = format!("big-{i:04}");
txn.delete(key.as_bytes()).await.unwrap();
}
txn.commit().await.unwrap();
}
let txn = db.begin_read().await.unwrap();
for i in 0..N {
let key = format!("big-{i:04}");
let got = txn.get(key.as_bytes()).await.unwrap();
if i % 2 == 0 {
assert!(got.is_none(), "key {key} should be deleted");
} else {
assert_eq!(
got.unwrap().as_ref(),
large.as_slice(),
"key {key} value mismatch"
);
}
}
}