use bytes::Bytes;
use seerdb::SyncPolicy;
use seerdb::{DBOptions, DB};
use tempfile::tempdir;
#[test]
fn test_db_full_lifecycle() {
let dir = tempdir().unwrap();
{
let db = DB::open(dir.path()).unwrap();
for i in 0..1000 {
let key = format!("key_{:04}", i);
let value = format!("value_{:04}", i);
db.put(key.as_bytes(), value.as_bytes()).unwrap();
}
for i in 0..1000 {
let key = format!("key_{:04}", i);
let value = format!("value_{:04}", i);
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
{
let db = DB::open(dir.path()).unwrap();
for i in 0..1000 {
let key = format!("key_{:04}", i);
let value = format!("value_{:04}", i);
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
}
#[test]
fn test_db_with_deletes() {
let dir = tempdir().unwrap();
{
let db = DB::open(dir.path()).unwrap();
for i in 0..100 {
let key = format!("key_{}", i);
db.put(key.as_bytes(), b"value").unwrap();
}
for i in (0..100).step_by(2) {
let key = format!("key_{}", i);
db.delete(key.as_bytes()).unwrap();
}
for i in 0..100 {
let key = format!("key_{}", i);
if i % 2 == 0 {
assert_eq!(db.get(key.as_bytes()).unwrap(), None);
} else {
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("value")));
}
}
}
{
let db = DB::open(dir.path()).unwrap();
for i in 0..100 {
let key = format!("key_{}", i);
if i % 2 == 0 {
assert_eq!(db.get(key.as_bytes()).unwrap(), None);
} else {
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("value")));
}
}
}
}
#[test]
fn test_db_overwrites() {
let dir = tempdir().unwrap();
{
let db = DB::open(dir.path()).unwrap();
for i in 0..50 {
let key = format!("key_{}", i);
db.put(key.as_bytes(), b"old_value").unwrap();
}
for i in 0..50 {
let key = format!("key_{}", i);
db.put(key.as_bytes(), b"new_value").unwrap();
}
for i in 0..50 {
let key = format!("key_{}", i);
assert_eq!(
db.get(key.as_bytes()).unwrap(),
Some(Bytes::from("new_value"))
);
}
}
{
let db = DB::open(dir.path()).unwrap();
for i in 0..50 {
let key = format!("key_{}", i);
assert_eq!(
db.get(key.as_bytes()).unwrap(),
Some(Bytes::from("new_value"))
);
}
}
}
#[test]
fn test_db_multiple_flushes() {
let dir = tempdir().unwrap();
{
let db = DBOptions::default()
.memtable_capacity(10 * 1024) .open(dir.path())
.unwrap();
for i in 0..200 {
let key = format!("key_{:04}", i);
let value = vec![b'x'; 100]; db.put(key.as_bytes(), &value).unwrap();
}
for i in 0..200 {
let key = format!("key_{:04}", i);
let value = vec![b'x'; 100];
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
{
let db = DB::open(dir.path()).unwrap();
for i in 0..200 {
let key = format!("key_{:04}", i);
let value = vec![b'x'; 100];
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
}
#[test]
fn test_db_large_values() {
let dir = tempdir().unwrap();
{
let db = DB::open(dir.path()).unwrap();
for i in 0..100 {
let key = format!("doc_{}", i);
let value = vec![i as u8; 4096]; db.put(key.as_bytes(), &value).unwrap();
}
for i in 0..100 {
let key = format!("doc_{}", i);
let value = vec![i as u8; 4096];
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
}
#[test]
fn test_db_crash_recovery_with_uncommitted_data() {
let dir = tempdir().unwrap();
{
let db = DBOptions::default()
.sync_policy(SyncPolicy::SyncData)
.open(dir.path())
.unwrap();
for i in 0..200 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
db.put(key.as_bytes(), value.as_bytes()).unwrap();
}
std::mem::drop(db);
}
{
let db = DB::open(dir.path()).unwrap();
for i in 0..200 {
let key = format!("key_{}", i);
let value = format!("value_{}", i);
assert_eq!(
db.get(key.as_bytes()).unwrap(),
Some(Bytes::from(value)),
"Failed to recover key_{} after crash",
i
);
}
}
}
#[test]
fn test_db_mixed_operations() {
let dir = tempdir().unwrap();
{
let db = DB::open(dir.path()).unwrap();
for i in 0..100 {
let key = format!("key_{}", i);
db.put(key.as_bytes(), b"value1").unwrap();
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("value1")));
db.put(key.as_bytes(), b"value2").unwrap();
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("value2")));
if i % 2 == 1 {
db.delete(key.as_bytes()).unwrap();
assert_eq!(db.get(key.as_bytes()).unwrap(), None);
}
}
for i in 0..100 {
let key = format!("key_{}", i);
if i % 2 == 1 {
assert_eq!(db.get(key.as_bytes()).unwrap(), None);
} else {
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("value2")));
}
}
}
}
#[test]
fn test_db_empty_database() {
let dir = tempdir().unwrap();
let db = DB::open(dir.path()).unwrap();
assert_eq!(db.get(b"nonexistent").unwrap(), None);
assert_eq!(db.memtable_len(), 0);
assert_eq!(db.memtable_size(), 0);
}
#[test]
#[ignore] fn test_db_reopen_multiple_times() {
let dir = tempdir().unwrap();
for round in 0..5 {
let db = DB::open(dir.path()).unwrap();
for i in 0..20 {
let key = format!("round_{}_{}", round, i);
let value = format!("value_{}_{}", round, i);
db.put(key.as_bytes(), value.as_bytes()).unwrap();
}
for prev_round in 0..=round {
for i in 0..20 {
let key = format!("round_{}_{}", prev_round, i);
let value = format!("value_{}_{}", prev_round, i);
assert_eq!(
db.get(key.as_bytes()).unwrap(),
Some(Bytes::from(value)),
"Failed at round {} checking data from round {}",
round,
prev_round
);
}
}
}
let db = DB::open(dir.path()).unwrap();
for round in 0..5 {
for i in 0..20 {
let key = format!("round_{}_{}", round, i);
let value = format!("value_{}_{}", round, i);
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from(value)));
}
}
}
#[test]
fn test_db_sequential_vs_random_keys() {
let dir = tempdir().unwrap();
let db = DB::open(dir.path()).unwrap();
for i in 0..100 {
let key = format!("seq_{:04}", i);
db.put(key.as_bytes(), b"sequential").unwrap();
}
for i in 0..100 {
let key = format!("rand_{:04}", (i * 97) % 1000); db.put(key.as_bytes(), b"random").unwrap();
}
for i in 0..100 {
let key = format!("seq_{:04}", i);
assert_eq!(
db.get(key.as_bytes()).unwrap(),
Some(Bytes::from("sequential"))
);
}
for i in 0..100 {
let key = format!("rand_{:04}", (i * 97) % 1000);
assert_eq!(db.get(key.as_bytes()).unwrap(), Some(Bytes::from("random")));
}
}