use dson::{
CausalDotStore, Identifier, OrArray, OrMap,
crdts::mvreg::MvRegValue,
transaction::{ArrayTransaction, CrdtValue, MapTransaction},
};
#[test]
fn map_transaction_rollback_register() {
let mut store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("name", MvRegValue::String("Alice".to_string()));
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("name", MvRegValue::String("Bob".to_string()));
tx.write_register("age", MvRegValue::U64(30));
}
assert_eq!(store, original_store);
let tx = MapTransaction::new(&mut store, id);
match tx.get(&"name".to_string()) {
Some(CrdtValue::Register(reg)) => {
use dson::crdts::snapshot::ToValue;
assert_eq!(
reg.value().unwrap(),
&MvRegValue::String("Alice".to_string())
);
}
_ => panic!("Expected register with original value"),
}
assert!(tx.get(&"age".to_string()).is_none());
}
#[test]
fn map_transaction_rollback_nested_map() {
let mut store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
{
let mut tx = MapTransaction::new(&mut store, id);
tx.in_map("config", |cfg_tx| {
cfg_tx.write_register("version", MvRegValue::U64(1));
});
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = MapTransaction::new(&mut store, id);
tx.in_map("config", |cfg_tx| {
cfg_tx.write_register("version", MvRegValue::U64(2));
cfg_tx.write_register("debug", MvRegValue::Bool(true));
});
}
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
let config = store.store.get(&"config".to_string()).unwrap();
let version = config.map.get(&"version".to_string()).unwrap();
assert_eq!(version.reg.value().unwrap(), &MvRegValue::U64(1));
assert!(config.map.get(&"debug".to_string()).is_none());
}
#[test]
fn map_transaction_rollback_array() {
let mut store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
{
let mut tx = MapTransaction::new(&mut store, id);
tx.in_array("items", |arr_tx| {
arr_tx.insert_register(0, MvRegValue::String("first".to_string()));
});
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = MapTransaction::new(&mut store, id);
tx.in_array("items", |arr_tx| {
arr_tx.insert_register(1, MvRegValue::String("second".to_string()));
arr_tx.insert_register(2, MvRegValue::String("third".to_string()));
});
}
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
let items = store.store.get(&"items".to_string()).unwrap();
assert_eq!(items.array.len(), 1);
assert_eq!(
items.array.get(0).unwrap().reg.value().unwrap(),
&MvRegValue::String("first".to_string())
);
}
#[test]
fn array_transaction_rollback_register() {
let mut store = CausalDotStore::<OrArray>::default();
let id = Identifier::new(0, 0);
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_register(0, MvRegValue::U64(1));
tx.insert_register(1, MvRegValue::U64(2));
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_register(2, MvRegValue::U64(3));
tx.insert_register(3, MvRegValue::U64(4));
}
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
assert_eq!(store.store.len(), 2);
assert_eq!(
store.store.get(0).unwrap().reg.value().unwrap(),
&MvRegValue::U64(1)
);
assert_eq!(
store.store.get(1).unwrap().reg.value().unwrap(),
&MvRegValue::U64(2)
);
}
#[test]
fn array_transaction_rollback_nested_array() {
let mut store = CausalDotStore::<OrArray>::default();
let id = Identifier::new(0, 0);
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_array(0, |inner_tx| {
inner_tx.insert_register(0, MvRegValue::U64(1));
});
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_array(1, |inner_tx| {
inner_tx.insert_register(0, MvRegValue::U64(2));
});
}
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
assert_eq!(store.store.len(), 1);
let nested = &store.store.get(0).unwrap().array;
assert_eq!(nested.len(), 1);
assert_eq!(
nested.get(0).unwrap().reg.value().unwrap(),
&MvRegValue::U64(1)
);
}
#[test]
fn array_transaction_rollback_map() {
let mut store = CausalDotStore::<OrArray>::default();
let id = Identifier::new(0, 0);
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_map(0, |map_tx| {
map_tx.write_register("id", MvRegValue::U64(1));
});
let _delta = tx.commit();
}
let original_store = store.clone();
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_map(1, |map_tx| {
map_tx.write_register("id", MvRegValue::U64(2));
});
}
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
assert_eq!(store.store.len(), 1);
let map = &store.store.get(0).unwrap().map;
let id_val = map.get(&"id".to_string()).unwrap();
assert_eq!(id_val.reg.value().unwrap(), &MvRegValue::U64(1));
}
#[test]
fn map_transaction_commit_after_rollback() {
let mut store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("count", MvRegValue::U64(1));
let _delta = tx.commit();
}
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("count", MvRegValue::U64(999));
}
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("count", MvRegValue::U64(2));
let _delta = tx.commit();
}
let tx = MapTransaction::new(&mut store, id);
match tx.get(&"count".to_string()) {
Some(CrdtValue::Register(reg)) => {
use dson::crdts::snapshot::ToValue;
assert_eq!(reg.value().unwrap(), &MvRegValue::U64(2));
}
_ => panic!("Expected register"),
}
}
#[test]
fn array_transaction_commit_after_rollback() {
let mut store = CausalDotStore::<OrArray>::default();
let id = Identifier::new(0, 0);
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_register(0, MvRegValue::U64(1));
let _delta = tx.commit();
}
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_register(1, MvRegValue::U64(999));
}
{
let mut tx = ArrayTransaction::new(&mut store, id);
tx.insert_register(1, MvRegValue::U64(2));
let _delta = tx.commit();
}
use dson::crdts::snapshot::ToValue;
assert_eq!(store.store.len(), 2);
assert_eq!(
store.store.get(0).unwrap().reg.value().unwrap(),
&MvRegValue::U64(1)
);
assert_eq!(
store.store.get(1).unwrap().reg.value().unwrap(),
&MvRegValue::U64(2)
);
}
#[test]
fn nested_transaction_panic_safety() {
use dson::{
CausalDotStore, Identifier, OrMap, crdts::mvreg::MvRegValue, transaction::MapTransaction,
};
let mut store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
{
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("root", MvRegValue::U64(1));
let _delta = tx.commit();
}
let original_store = store.clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut tx = MapTransaction::new(&mut store, id);
tx.write_register("root", MvRegValue::U64(2));
tx.in_map("nested", |nested_tx| {
nested_tx.write_register("field", MvRegValue::String("test".to_string()));
panic!("Simulated panic in nested transaction");
});
#[allow(unreachable_code)]
tx.commit()
}));
assert!(result.is_err());
assert_eq!(store, original_store);
use dson::crdts::snapshot::ToValue;
let val = store.store.get(&"root".to_string()).unwrap();
assert_eq!(val.reg.value().unwrap(), &MvRegValue::U64(1));
assert!(store.store.get(&"nested".to_string()).is_none());
}