use emdb::{Emdb, Result};
fn tmp_path(name: &str) -> std::path::PathBuf {
let mut p = std::env::temp_dir();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0_u128, |d| d.as_nanos());
p.push(format!("emdb-{name}-{nanos}.emdb"));
p
}
fn cleanup(path: &std::path::Path) {
let _ = std::fs::remove_file(path);
let display = path.display();
let _ = std::fs::remove_file(format!("{display}.lock"));
let _ = std::fs::remove_file(format!("{display}.compact.tmp"));
let _ = std::fs::remove_file(format!("{display}.encbak"));
}
#[test]
fn namespace_records_survive_reopen() -> Result<()> {
let path = tmp_path("ns-roundtrip");
{
let db = Emdb::open(&path)?;
let users = db.namespace("users")?;
let sessions = db.namespace("sessions")?;
users.insert(b"alice", b"1")?;
users.insert(b"bob", b"2")?;
sessions.insert(b"sid-1", b"abc")?;
db.flush()?;
}
let db = Emdb::open(&path)?;
let mut names = db.list_namespaces()?;
names.sort();
assert_eq!(
names,
vec!["".to_string(), "sessions".to_string(), "users".to_string()],
"named namespaces should reappear after reopen"
);
let users = db.namespace("users")?;
let sessions = db.namespace("sessions")?;
assert_eq!(users.get(b"alice")?, Some(b"1".to_vec()));
assert_eq!(users.get(b"bob")?, Some(b"2".to_vec()));
assert_eq!(sessions.get(b"sid-1")?, Some(b"abc".to_vec()));
drop(db);
cleanup(&path);
Ok(())
}
#[test]
fn namespace_id_stable_across_reopens() -> Result<()> {
let path = tmp_path("ns-id-stable");
let alice_id = {
let db = Emdb::open(&path)?;
let alice = db.namespace("alice")?;
let bob = db.namespace("bob")?;
alice.insert(b"k", b"alice-value")?;
bob.insert(b"k", b"bob-value")?;
db.flush()?;
let names = db.list_namespaces()?;
let alice_pos = names.iter().position(|n| n == "alice").unwrap_or(0);
let bob_pos = names.iter().position(|n| n == "bob").unwrap_or(0);
assert!(alice_pos < bob_pos, "alice was registered before bob");
alice_pos
};
let db = Emdb::open(&path)?;
let bob = db.namespace("bob")?;
let alice = db.namespace("alice")?;
assert_eq!(alice.get(b"k")?, Some(b"alice-value".to_vec()));
assert_eq!(bob.get(b"k")?, Some(b"bob-value".to_vec()));
let _ = alice_id;
drop(db);
cleanup(&path);
Ok(())
}
#[test]
fn namespace_name_record_survives_compaction() -> Result<()> {
let path = tmp_path("ns-compact");
{
let db = Emdb::open(&path)?;
let users = db.namespace("users")?;
users.insert(b"alice", b"1")?;
users.insert(b"bob", b"2")?;
let _removed = users.remove(b"alice")?;
db.flush()?;
db.compact()?;
db.flush()?;
}
let db = Emdb::open(&path)?;
let names = db.list_namespaces()?;
assert!(
names.iter().any(|n| n == "users"),
"users namespace must reappear after compaction + reopen, got {names:?}"
);
let users = db.namespace("users")?;
assert_eq!(users.get(b"alice")?, None);
assert_eq!(users.get(b"bob")?, Some(b"2".to_vec()));
drop(users);
drop(db);
cleanup(&path);
Ok(())
}
#[test]
fn namespace_name_persists_with_inserts_only() -> Result<()> {
let path = tmp_path("ns-empty");
{
let db = Emdb::open(&path)?;
let _empty = db.namespace("empty-ns")?;
db.flush()?;
}
let db = Emdb::open(&path)?;
assert!(
db.list_namespaces()?.iter().any(|n| n == "empty-ns"),
"empty namespace should still appear after reopen"
);
drop(db);
cleanup(&path);
Ok(())
}
#[cfg(feature = "encrypt")]
#[test]
fn namespace_persistence_works_with_encryption() -> Result<()> {
use emdb::EmdbBuilder;
let path = tmp_path("ns-encrypted");
let key = [7_u8; 32];
{
let db = EmdbBuilder::new()
.path(path.clone())
.encryption_key(key)
.build()?;
let secrets = db.namespace("secrets")?;
secrets.insert(b"k", b"v")?;
db.flush()?;
}
let db = EmdbBuilder::new()
.path(path.clone())
.encryption_key(key)
.build()?;
let names = db.list_namespaces()?;
assert!(names.iter().any(|n| n == "secrets"));
let secrets = db.namespace("secrets")?;
assert_eq!(secrets.get(b"k")?, Some(b"v".to_vec()));
drop(db);
cleanup(&path);
Ok(())
}