use bytes::Bytes;
use surrealmx::Database;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[test]
fn increment_pattern_ssi() {
let db = Database::new();
{
let mut tx = db.transaction(true);
tx.set("counter", "0").unwrap();
tx.commit().unwrap();
}
for expected_before in 0..5 {
let mut tx = db.transaction(true).with_serializable_snapshot_isolation();
let current = tx.get("counter").unwrap().unwrap();
let current_val: i32 = std::str::from_utf8(¤t).unwrap().parse().unwrap();
assert_eq!(current_val, expected_before, "Counter should be at expected value");
let new_val = current_val + 1;
tx.set("counter", new_val.to_string()).unwrap();
tx.commit().unwrap();
}
let tx = db.transaction(false);
let final_val: i32 =
std::str::from_utf8(&tx.get("counter").unwrap().unwrap()).unwrap().parse().unwrap();
assert_eq!(final_val, 5, "Counter should be 5 after 5 increments");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[test]
fn compare_and_swap_pattern() {
let db = Database::new();
{
let mut tx = db.transaction(true);
tx.set("cas_key", "initial").unwrap();
tx.commit().unwrap();
}
{
let mut tx = db.transaction(true);
let result = tx.putc("cas_key", "updated", Some::<&str>("initial"));
assert!(result.is_ok(), "CAS should succeed when expected value matches");
tx.commit().unwrap();
}
{
let tx = db.transaction(false);
assert_eq!(tx.get("cas_key").unwrap(), Some(Bytes::from("updated")));
}
{
let mut tx = db.transaction(true);
let result = tx.putc("cas_key", "should_not_be_set", Some::<&str>("initial"));
assert!(result.is_err(), "CAS should fail when expected value doesn't match");
tx.cancel().unwrap();
}
{
let tx = db.transaction(false);
assert_eq!(tx.get("cas_key").unwrap(), Some(Bytes::from("updated")));
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[test]
fn conditional_update_chain() {
let db = Database::new();
{
let mut tx = db.transaction(true);
tx.set("state", "pending").unwrap();
tx.commit().unwrap();
}
let transitions = [("pending", "processing"), ("processing", "completed")];
for (expected, new_state) in transitions {
let mut tx = db.transaction(true);
let result = tx.putc("state", new_state, Some::<&str>(expected));
assert!(result.is_ok(), "Transition from {} to {} should succeed", expected, new_state);
tx.commit().unwrap();
}
let tx = db.transaction(false);
assert_eq!(tx.get("state").unwrap(), Some(Bytes::from("completed")));
{
let mut tx = db.transaction(true);
let result = tx.putc("state", "pending", Some::<&str>("processing"));
assert!(result.is_err(), "Invalid transition should fail");
tx.cancel().unwrap();
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[test]
fn optimistic_locking_pattern() {
let db = Database::new();
{
let mut tx = db.transaction(true);
tx.set("data", "initial_data").unwrap();
tx.set("data_version", "1").unwrap();
tx.commit().unwrap();
}
{
let mut tx = db.transaction(true).with_serializable_snapshot_isolation();
let version = tx.get("data_version").unwrap().unwrap();
let version_num: i32 = std::str::from_utf8(&version).unwrap().parse().unwrap();
let _data = tx.get("data").unwrap().unwrap();
tx.set("data", "modified_data").unwrap();
tx.set("data_version", (version_num + 1).to_string()).unwrap();
tx.commit().unwrap();
}
let tx = db.transaction(false);
assert_eq!(tx.get("data").unwrap(), Some(Bytes::from("modified_data")));
assert_eq!(tx.get("data_version").unwrap(), Some(Bytes::from("2")));
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[test]
fn retry_on_conflict() {
let db = Database::new();
{
let mut tx = db.transaction(true);
tx.set("retry_counter", "0").unwrap();
tx.commit().unwrap();
}
{
let tx = db.transaction(false);
let val = tx.get("retry_counter").unwrap();
assert_eq!(val, Some(Bytes::from("0")), "Counter should be initialized to 0");
}
let num_increments = 5;
let mut total_retries = 0;
for _ in 0..num_increments {
let max_retries = 10;
let mut retries = 0;
loop {
let mut tx = db.transaction(true).with_serializable_snapshot_isolation();
let current = tx.get("retry_counter").unwrap().expect("Counter should exist");
let current_val: i32 = std::str::from_utf8(¤t).unwrap().parse().unwrap();
tx.set("retry_counter", (current_val + 1).to_string()).unwrap();
match tx.commit() {
Ok(_) => break,
Err(_) => {
retries += 1;
total_retries += 1;
if retries >= max_retries {
panic!("Exceeded max retries");
}
}
}
}
}
let tx = db.transaction(false);
let final_val: i32 =
std::str::from_utf8(&tx.get("retry_counter").unwrap().unwrap()).unwrap().parse().unwrap();
assert_eq!(final_val, num_increments, "Counter should equal number of increments");
assert_eq!(total_retries, 0, "Sequential execution should not need retries");
}