use emdb::{EmdbBuilder, Error, Result};
fn temp_root(label: &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-storage-path-{label}-{nanos}"));
p
}
#[test]
fn explicit_data_root_with_named_app_and_db_round_trips() -> Result<()> {
let root = temp_root("v06-roundtrip");
{
let db = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hive")
.database_name("sessions.emdb")
.build()?;
db.insert(b"k", b"v")?;
db.flush()?;
}
let expected = root.join("hive").join("sessions.emdb");
assert!(
expected.exists(),
"database file should exist at {expected:?}"
);
{
let reopened = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hive")
.database_name("sessions.emdb")
.build()?;
assert_eq!(reopened.get(b"k")?.as_deref(), Some(b"v".as_slice()));
}
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn dash_joined_app_name_round_trips() -> Result<()> {
let root = temp_root("dash-joined");
{
let db = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hivedb-kv")
.database_name("sessions.emdb")
.build()?;
db.insert(b"session-1", b"alpha")?;
db.flush()?;
}
let expected = root.join("hivedb-kv").join("sessions.emdb");
assert!(
expected.exists(),
"dash-joined app folder should land at {expected:?}"
);
let reopened = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hivedb-kv")
.database_name("sessions.emdb")
.build()?;
assert_eq!(
reopened.get(b"session-1")?.as_deref(),
Some(b"alpha".as_slice())
);
drop(reopened);
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn explicit_data_root_round_trips_through_v07() -> Result<()> {
let root = temp_root("v07-roundtrip");
{
let db = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hive")
.database_name("events.emdb")
.build()?;
db.insert(b"alpha", b"one")?;
db.insert(b"beta", b"two")?;
db.flush()?;
}
let expected = root.join("hive").join("events.emdb");
assert!(expected.exists(), "v0.7 db should land at {expected:?}");
let reopened = EmdbBuilder::new()
.data_root(root.clone())
.app_name("hive")
.database_name("events.emdb")
.build()?;
assert_eq!(reopened.get(b"alpha")?.as_deref(), Some(b"one".as_slice()));
assert_eq!(reopened.get(b"beta")?.as_deref(), Some(b"two".as_slice()));
drop(reopened);
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn defaults_apply_when_only_data_root_is_set() -> Result<()> {
let root = temp_root("defaults");
let _db = EmdbBuilder::new().data_root(root.clone()).build()?;
let expected = root.join("emdb").join("emdb-default.emdb");
assert!(
expected.exists(),
"default-named database should exist at {expected:?}"
);
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn explicit_path_with_os_resolution_methods_is_rejected() {
let result = EmdbBuilder::new()
.path("/tmp/should-not-be-used.emdb")
.app_name("hive")
.build();
assert!(matches!(result, Err(Error::InvalidConfig(_))));
let result_db = EmdbBuilder::new()
.path("/tmp/should-not-be-used.emdb")
.database_name("foo.emdb")
.build();
assert!(matches!(result_db, Err(Error::InvalidConfig(_))));
let result_root = EmdbBuilder::new()
.path("/tmp/should-not-be-used.emdb")
.data_root("/tmp/some-root")
.build();
assert!(matches!(result_root, Err(Error::InvalidConfig(_))));
}
#[test]
fn path_separator_in_app_name_is_rejected() {
let root = temp_root("sep-app");
for bad in ["hive/inner", "hive\\inner", "..", "data/.."] {
let result = EmdbBuilder::new()
.data_root(root.clone())
.app_name(bad)
.build();
assert!(
matches!(result, Err(Error::InvalidConfig(_))),
"expected InvalidConfig for app_name={bad:?}"
);
}
let _removed = std::fs::remove_dir_all(&root);
}
#[test]
fn path_separator_in_database_name_is_rejected() {
let root = temp_root("sep-db");
let result = EmdbBuilder::new()
.data_root(root.clone())
.database_name("nested/file.emdb")
.build();
assert!(matches!(result, Err(Error::InvalidConfig(_))));
let _removed = std::fs::remove_dir_all(&root);
}
#[test]
fn missing_intermediate_directory_is_created() -> Result<()> {
let root = temp_root("mkdir-p");
assert!(!root.exists(), "precondition: root should not pre-exist");
let _db = EmdbBuilder::new()
.data_root(root.clone())
.app_name("acme")
.database_name("billing.emdb")
.build()?;
let expected = root.join("acme").join("billing.emdb");
assert!(expected.exists(), "expected {expected:?} to exist");
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn two_apps_under_one_root_do_not_collide() -> Result<()> {
let root = temp_root("multi-app");
let billing = EmdbBuilder::new()
.data_root(root.clone())
.app_name("acme-billing")
.database_name("invoices.emdb")
.build()?;
billing.insert(b"id-1", b"$100")?;
let analytics = EmdbBuilder::new()
.data_root(root.clone())
.app_name("acme-analytics")
.database_name("events.emdb")
.build()?;
analytics.insert(b"id-1", b"signup")?;
assert_eq!(billing.get(b"id-1")?.as_deref(), Some(b"$100".as_slice()));
assert_eq!(
analytics.get(b"id-1")?.as_deref(),
Some(b"signup".as_slice())
);
assert!(root.join("acme-billing").is_dir());
assert!(root.join("acme-analytics").is_dir());
drop(billing);
drop(analytics);
let _removed = std::fs::remove_dir_all(&root);
Ok(())
}
#[test]
fn no_path_methods_set_yields_in_memory_database() -> Result<()> {
let db = EmdbBuilder::new().build()?;
db.insert(b"k", b"v")?;
assert_eq!(db.get(b"k")?.as_deref(), Some(b"v".as_slice()));
let other = EmdbBuilder::new().build()?;
assert!(other.get(b"k")?.is_none());
Ok(())
}