use knf::{Map, MergeOptions, Number, Value, merge, merge_into};
use proptest::prelude::*;
fn arb_value() -> impl Strategy<Value = Value> {
let leaf = prop_oneof![
Just(Value::Null),
any::<bool>().prop_map(Value::Bool),
any::<i64>().prop_map(|n| Value::Number(Number::I64(n))),
"[a-z]{0,3}".prop_map(Value::String),
Just(Value::Datetime("1979-05-27T07:32:00Z".to_string())),
];
leaf.prop_recursive(4, 24, 3, |inner| {
prop_oneof![
prop::collection::vec(inner.clone(), 0..3).prop_map(Value::Array),
arb_object(inner),
]
})
}
fn arb_object(inner: impl Strategy<Value = Value>) -> impl Strategy<Value = Value> {
prop::collection::vec(("[a-c]{1,2}", inner), 0..3)
.prop_map(|entries| Value::Object(entries.into_iter().collect::<Map>()))
}
fn arb_doc() -> impl Strategy<Value = Value> {
arb_object(arb_value())
}
fn merged(mut base: Value, over: Value) -> Value {
merge_into(&mut base, over, &MergeOptions::LAST_WINS).expect("non-strict merge cannot fail");
base
}
fn shallow(mut base: Value, over: Value) -> Value {
merge_into(&mut base, over, &MergeOptions::SHALLOW).expect("non-strict merge cannot fail");
base
}
proptest! {
#[test]
fn merging_a_layer_with_itself_is_a_no_op(a in arb_doc()) {
prop_assert_eq!(merged(a.clone(), a.clone()), a);
}
#[test]
fn overriding_twice_is_the_same_as_once(a in arb_doc(), b in arb_doc()) {
let once = merged(a, b.clone());
prop_assert_eq!(merged(once.clone(), b), once);
}
#[test]
fn strict_agrees_with_default_when_it_succeeds(a in arb_doc(), b in arb_doc()) {
let mut strict = a.clone();
if merge_into(&mut strict, b.clone(), &MergeOptions::STRICT).is_ok() {
prop_assert_eq!(strict, merged(a, b));
}
}
#[test]
fn a_single_layer_is_identity_under_shallow(a in arb_doc()) {
let got = merge([a.clone()], &MergeOptions::SHALLOW).expect("non-strict");
prop_assert_eq!(got, a);
}
#[test]
fn shallow_assigns_top_level_keys(a in arb_doc(), b in arb_doc()) {
let (Value::Object(mut want), Value::Object(over)) = (a.clone(), b.clone()) else {
unreachable!("arb_doc generates objects")
};
for (k, v) in over {
want.insert(k, v);
}
prop_assert_eq!(shallow(a, b), Value::Object(want));
}
#[test]
fn shallow_is_associative(a in arb_doc(), b in arb_doc(), c in arb_doc()) {
let left = shallow(shallow(a.clone(), b.clone()), c.clone());
let right = shallow(a, shallow(b, c));
prop_assert_eq!(left, right);
}
}