use xpct::{be_err, be_none, be_ok, be_some, consist_of, equal, expect};
use liteboxfs::{Connection, CreateOptions, Error, UserMetadata};
mod filesystem_metadata {
use super::*;
#[test]
fn returns_empty_by_default() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
expect!(fs.filesystem_metadata())
.to(be_ok())
.map(|metadata| metadata.iter().count())
.to(equal(0));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn set_and_get_roundtrip() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
metadata.set("key2".to_string(), b"value2".to_vec())?;
fs.set_filesystem_metadata(&metadata)?;
let retrieved = fs.filesystem_metadata()?;
expect!(retrieved.get("key1"))
.to(be_some())
.to(equal(b"value1"));
expect!(retrieved.get("key2"))
.to(be_some())
.to(equal(b"value2"));
expect!(retrieved.iter().count()).to(equal(2));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn set_replaces_existing() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
metadata.set("key2".to_string(), b"value2".to_vec())?;
fs.set_filesystem_metadata(&metadata)?;
let mut new_metadata = UserMetadata::new();
new_metadata.set("key3".to_string(), b"value3".to_vec())?;
fs.set_filesystem_metadata(&new_metadata)?;
let retrieved = fs.filesystem_metadata()?;
expect!(retrieved.get("key1")).to(be_none());
expect!(retrieved.get("key2")).to(be_none());
expect!(retrieved.get("key3"))
.to(be_some())
.to(equal(b"value3"));
expect!(retrieved.iter().count()).to(equal(1));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn setting_empty_metadata_clears_existing() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
fs.set_filesystem_metadata(&metadata)?;
let empty_metadata = UserMetadata::new();
fs.set_filesystem_metadata(&empty_metadata)?;
let retrieved = fs.filesystem_metadata()?;
expect!(retrieved.iter().count()).to(equal(0));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
}
mod root_metadata {
use super::*;
#[test]
fn returns_empty_by_default() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
expect!(fs.root_metadata())
.to(be_ok())
.map(|metadata| metadata.iter().count())
.to(equal(0));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn set_and_get_roundtrip() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("root_key".to_string(), b"root_value".to_vec())?;
fs.set_root_metadata(&metadata)?;
let retrieved = fs.root_metadata()?;
expect!(retrieved.get("root_key"))
.to(be_some())
.to(equal(b"root_value"));
expect!(retrieved.iter().count()).to(equal(1));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn set_replaces_existing() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
fs.set_root_metadata(&metadata)?;
let mut new_metadata = UserMetadata::new();
new_metadata.set("key2".to_string(), b"value2".to_vec())?;
fs.set_root_metadata(&new_metadata)?;
let retrieved = fs.root_metadata()?;
expect!(retrieved.get("key1")).to(be_none());
expect!(retrieved.get("key2"))
.to(be_some())
.to(equal(b"value2"));
expect!(retrieved.iter().count()).to(equal(1));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn setting_empty_metadata_clears_existing() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
fs.set_root_metadata(&metadata)?;
let empty_metadata = UserMetadata::new();
fs.set_root_metadata(&empty_metadata)?;
expect!(fs.root_metadata())
.to(be_ok())
.map(|metadata| metadata.iter().count())
.to(equal(0));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
#[test]
fn metadata_is_isolated_per_root() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let default_root_id = fs.root_id();
let mut default_metadata = UserMetadata::new();
default_metadata.set("key".to_string(), b"default_value".to_vec())?;
fs.set_root_metadata(&default_metadata)?;
let new_root = fs.create_root(Some("other"))?;
fs.switch_root(new_root.id)?;
let retrieved = fs.root_metadata()?;
expect!(retrieved.iter().count()).to(equal(0));
let mut other_metadata = UserMetadata::new();
other_metadata.set("key".to_string(), b"other_value".to_vec())?;
fs.set_root_metadata(&other_metadata)?;
fs.switch_root(default_root_id)?;
let default_retrieved = fs.root_metadata()?;
expect!(default_retrieved.get("key"))
.to(be_some())
.to(equal(b"default_value"));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
}
mod isolation {
use super::*;
#[test]
fn filesystem_metadata_is_distinct_from_root_metadata() -> liteboxfs::Result<()> {
let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
conn.exec(|fs| {
let mut fs_metadata = UserMetadata::new();
fs_metadata.set("key".to_string(), b"filesystem_value".to_vec())?;
fs.set_filesystem_metadata(&fs_metadata)?;
let mut root_metadata = UserMetadata::new();
root_metadata.set("key".to_string(), b"root_value".to_vec())?;
fs.set_root_metadata(&root_metadata)?;
let fs_retrieved = fs.filesystem_metadata()?;
expect!(fs_retrieved.get("key"))
.to(be_some())
.to(equal(b"filesystem_value"));
let root_retrieved = fs.root_metadata()?;
expect!(root_retrieved.get("key"))
.to(be_some())
.to(equal(b"root_value"));
liteboxfs::Result::Ok(())
})?;
Ok(())
}
}
mod limits {
use super::*;
#[test]
fn exceeds_key_length_limit() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
let key = "k".repeat(UserMetadata::MAX_KEY_LEN + 1);
expect!(metadata.set(key, b"value".to_vec()))
.to(be_err())
.to(equal(Error::MetadataLimitExceeded));
Ok(())
}
#[test]
fn exceeds_value_length_limit() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
let long_value = vec![0u8; UserMetadata::MAX_VALUE_LEN + 1];
expect!(metadata.set("key".to_string(), long_value))
.to(be_err())
.to(equal(Error::MetadataLimitExceeded));
Ok(())
}
}
mod types {
use super::*;
#[test]
fn remove_key() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
metadata.set("key".to_string(), b"value".to_vec())?;
metadata.remove("key");
expect!(metadata.get("key")).to(be_none());
Ok(())
}
#[test]
fn clear_keys() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
metadata.set("key2".to_string(), b"value2".to_vec())?;
metadata.clear();
expect!(metadata.get("key1")).to(be_none());
expect!(metadata.get("key2")).to(be_none());
Ok(())
}
#[test]
fn iter_values() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
metadata.set("key2".to_string(), b"value2".to_vec())?;
expect!(metadata.iter().len()).to(equal(2));
expect!(metadata.iter().collect::<Vec<_>>()).to(consist_of([
("key1", b"value1".as_slice()),
("key2", b"value2".as_slice()),
]));
Ok(())
}
#[test]
fn into_iter_values() -> liteboxfs::Result<()> {
let mut metadata = UserMetadata::new();
metadata.set("key1".to_string(), b"value1".to_vec())?;
metadata.set("key2".to_string(), b"value2".to_vec())?;
expect!(metadata.clone().into_iter().len()).to(equal(2));
expect!(metadata.into_iter().collect::<Vec<_>>()).to(consist_of([
("key1".to_string(), b"value1".to_vec()),
("key2".to_string(), b"value2".to_vec()),
]));
Ok(())
}
}