use ahash::{HashMap, HashMapExt};
use rust_decimal_macros::dec;
use serde_json::json;
use std::cell::RefCell;
use std::error::Error;
use std::rc::Rc;
use zen_types::rcvalue::RcValue;
use zen_types::variable::Variable;
type TestResult = Result<(), Box<dyn Error>>;
#[test]
fn serialize_deserialize_simple() -> TestResult {
let var = Variable::from(json!({
"name": "Alice",
"age": 30,
"active": true
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_deserialize_with_refs() -> TestResult {
let shared_string = "shared_value";
let var = Variable::from(json!({
"user1": {
"name": shared_string,
"status": shared_string
},
"user2": {
"name": shared_string,
"friend": shared_string
},
"metadata": {
"type": shared_string
}
}));
let serialized = var.serialize_ref();
if let RcValue::Object(ref obj) = serialized {
assert!(obj.contains_key(&Rc::from("$refs")));
assert!(obj.contains_key(&Rc::from("$root")));
} else {
panic!("Expected object");
}
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_deserialize_array_refs() -> TestResult {
let shared_array = vec![1, 2, 3];
let var = Variable::from(json!({
"data1": shared_array,
"data2": shared_array,
"backup": shared_array
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_deserialize_at_string_escaping() -> TestResult {
let var = Variable::from(json!({
"normal": "hello",
"at_string": "@special",
"double_at": "@@escaped"
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_deserialize_nested_structure() -> TestResult {
let var = Variable::from(json!({
"level1": {
"level2": {
"level3": {
"data": "deep_value",
"numbers": [1, 2, 3, 4, 5]
}
},
"shared": "common_string"
},
"other": {
"ref": "common_string"
},
"array": [
{"shared": "common_string"},
{"different": "unique"}
]
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn no_refs_when_below_threshold() -> TestResult {
let var = Variable::from(json!({
"a": "hi",
"b": "hi",
"c": "hi"
}));
let serialized = var.serialize_ref();
if let RcValue::Object(ref obj) = serialized {
assert!(!obj.contains_key(&Rc::from("$refs")));
}
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_circular_references() -> TestResult {
let shared_obj = Rc::new(RefCell::new({
let mut map = HashMap::new();
map.insert(
Rc::from("shared_data"),
Variable::String(Rc::from("important_value")),
);
map.insert(Rc::from("id"), Variable::Number(dec!(42.0)));
map
}));
let mut root_map = HashMap::new();
root_map.insert(Rc::from("first_ref"), Variable::Object(shared_obj.clone()));
root_map.insert(Rc::from("second_ref"), Variable::Object(shared_obj.clone()));
root_map.insert(Rc::from("third_ref"), Variable::Object(shared_obj));
let var = Variable::Object(Rc::new(RefCell::new(root_map)));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_same_array_multiple_locations() -> TestResult {
use std::cell::RefCell;
let shared_array = Rc::new(RefCell::new(vec![
Variable::String(Rc::from("item_one")),
Variable::String(Rc::from("item_two")),
Variable::Number(dec!(123.0)),
]));
let var = Variable::from(json!({
"list1": shared_array.clone(),
"backup_list": shared_array.clone(),
"nested": {
"inner_list": shared_array
}
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_mixed_shared_references() -> TestResult {
let shared_string: Rc<str> = Rc::from("shared_between_key_and_value");
let mut obj_map = HashMap::new();
obj_map.insert(
shared_string.clone(),
Variable::String(shared_string.clone()),
);
obj_map.insert(
Rc::from("other_key"),
Variable::String(shared_string.clone()),
);
let shared_obj = Rc::new(RefCell::new(obj_map));
let var = Variable::from(json!({
"container1": shared_obj.clone(),
"container2": shared_obj.clone(),
"metadata": {
"reference": shared_obj
}
}));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}
#[test]
fn serialize_shared_array_with_shared_strings() -> TestResult {
let shared_string: Rc<str> = Rc::from("shared_string_value");
let shared_array = Rc::new(RefCell::new(vec![
Variable::String(shared_string.clone()),
Variable::Number(dec!(42.0)),
Variable::String(shared_string.clone()),
]));
let mut root_map = HashMap::new();
root_map.insert(Rc::from("array1"), Variable::Array(shared_array.clone()));
root_map.insert(Rc::from("array2"), Variable::Array(shared_array.clone()));
root_map.insert(Rc::from("array3"), Variable::Array(shared_array));
let var = Variable::Object(Rc::new(RefCell::new(root_map)));
let serialized = var.serialize_ref();
let deserialized = Variable::deserialize_ref(serialized)?;
assert_eq!(var, deserialized);
Ok(())
}