use amethystate::Field;
use amethystate::store::StoreBackend;
use amethystate::store::builder::{StoreBuilder, default_backend};
use amethystate::store::{
KvWrite, LoadMap, OpenStruct, ReadValue, StorageError, reactive_map_with_path,
};
use amethystate_core::path::StorePath;
use amethystate_core::test_utils::TempPath;
use error_stack::Report;
use std::collections::HashMap;
use uuid::Uuid;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;
mod common;
use common::{per_engine, shape};
#[amethystate::amethystate(prefix = "panel")]
pub struct Panel {
#[amestate(default = 800u32)]
pub width: u32,
}
fn store(name: &str) -> (TempPath, amethystate::Store) {
let path = TempPath::new(name);
let store = StoreBuilder::new(path.path()).build().unwrap();
(path, store)
}
fn from_the_map(why: LoadMap) -> Report<StorageError> {
match why {
LoadMap::Store(report) | LoadMap::EntryWillNotRead { why: report, .. } => {
report.into_report()
}
other => panic!("the store was expected to be at fault: {other}"),
}
}
#[test]
fn bytes_that_are_not_the_type() {
let (_dir, store) = store("report_decode");
store.set(["port"], &"not a number".to_string()).unwrap();
let raw = StoreBackend::get_raw(&store, &StorePath::from_segments(["port"]))
.unwrap()
.unwrap();
let err = store.decode::<u16>(&raw).unwrap_err();
insta::assert_snapshot!(
per_engine(default_backend(), "decode_wrong_type"),
shape(&err)
);
}
#[test]
fn a_stored_value_read_as_the_wrong_type() {
let (_dir, store) = store("report_get_wrong_type");
store
.set(["cfg", "name"], &"not a number".to_string())
.unwrap();
let err = store.get::<u32>(["cfg", "name"]).unwrap_err();
let ReadValue::WillNotRead { why, .. } = &err else {
panic!("{err}")
};
insta::assert_snapshot!(per_engine(default_backend(), "get_wrong_type"), shape(why));
}
#[test]
fn a_map_entry_that_will_not_read() {
let (_dir, store) = store("report_map_entry");
store.set(["cols", "cpu"], &"wide".to_string()).unwrap();
store.save_now().unwrap();
let err =
reactive_map_with_path::<String, u32>(&store, ["cols"], HashMap::new(), Uuid::new_v4())
.unwrap_err();
insta::assert_snapshot!(
per_engine(default_backend(), "map_entry_wrong_type"),
shape(&from_the_map(err))
);
}
#[test]
fn a_strict_write_to_an_absent_key() {
let (_dir, store) = store("report_map_absent");
let widths = store.kv().map::<String, u64>("cols").unwrap();
let err = widths.update("cpu", &1).unwrap_err();
insta::assert_snapshot!("map_update_on_an_absent_key", err.to_string());
}
#[test]
fn a_read_modify_write_on_an_absent_key() {
let (_dir, store) = store("report_map_rmw_absent");
let widths = store.kv().map::<String, u64>("cols").unwrap();
let refused = [
widths
.update_with("cpu", |w| w + 1)
.map(|_| ())
.unwrap_err(),
widths.modify("cpu", |w| *w += 1).unwrap_err(),
];
for (way, err) in ["update_with", "modify"].into_iter().zip(refused) {
insta::assert_snapshot!(format!("map_{way}_on_an_absent_key"), err.to_string());
}
}
#[test]
fn a_write_an_interceptor_turned_down() {
let (_dir, store) = store("report_map_intercepted");
let widths = store.kv().map::<String, u64>("cols").unwrap();
let _guard = widths.intercept(|_| None);
let err = widths.insert("cpu".to_string(), &1).unwrap_err();
insta::assert_snapshot!("map_insert_an_interceptor_refused", err.to_string());
}
#[test]
fn a_clear_an_interceptor_turned_down() {
let (_dir, store) = store("report_map_clear_intercepted");
let widths = store.kv().map::<String, u64>("cols").unwrap();
widths.insert("cpu".to_string(), &1).unwrap();
let _guard = widths.intercept(|_| None);
let err = widths.clear().unwrap_err();
insta::assert_snapshot!("map_clear_an_interceptor_refused", err.to_string());
}
#[test]
fn a_field_write_an_interceptor_turned_down() {
let (_dir, store) = store("report_field_intercepted");
let panel = Panel::new_with(&store).unwrap();
let width = panel.width();
let _guard = width.intercept(|_| None);
let err = width.set(9).unwrap_err();
insta::assert_snapshot!("field_an_interceptor_refused", err.to_string());
}
#[test]
fn a_volatile_field_write_an_interceptor_turned_down() {
let session: Field<String> = Field::new_volatile(
StorePath::from_segments(["app", "session"]),
"anonymous".to_string(),
);
let _guard = session.intercept(|_| None);
let err = session.set("alice".to_string()).unwrap_err();
insta::assert_snapshot!("volatile_field_an_interceptor_refused", err.to_string());
}
#[test]
fn a_kv_name_under_a_namespace() {
let (_dir, store) = store("report_kv_namespace");
let _panel = Panel::new_with(&store).unwrap();
let err = store
.kv()
.namespace("panel")
.set("width", &1u32)
.unwrap_err();
insta::assert_snapshot!("kv_over_a_declared_field_in_a_namespace", err.to_string());
}
#[test]
fn a_kv_write_over_a_declared_field_names_its_owner() {
let (_dir, store) = store("report_kv_owner");
let _panel = Panel::new_with(&store).unwrap();
let err: KvWrite = store
.kv()
.namespace("panel")
.set("width", &1u32)
.unwrap_err();
insta::assert_snapshot!(
"kv_over_a_declared_field_names_its_owner",
shape(&Report::<StorageError>::from(err))
);
}
#[amethystate::amethystate(prefix = "report_rows")]
pub struct Rows {
#[amestate(path = "left.visible", default = true)]
pub left_visible: bool,
}
#[amethystate::amethystate(prefix = "report_rows.left")]
pub struct LeftRow {
#[amestate(default = true)]
pub visible: bool,
}
#[test]
fn a_place_taken_twice_names_both_owners() {
let (_dir, store) = store("report_taken");
let _rows = Rows::new_with(&store).unwrap();
let err: OpenStruct = LeftRow::new_with(&store).map(|_| ()).unwrap_err();
insta::assert_snapshot!(
"a_place_taken_twice_names_both_owners",
shape(&Report::<StorageError>::from(err))
);
}