"""Merge-patch semantics (RFC 7386) — the rule every overlay section relies on."""
from navi.overlay.merge import merge_patch
def test_missing_key_leaves_target_alone():
assert merge_patch({"a": 1, "b": 2}, {"a": 9}) == {"a": 9, "b": 2}
def test_null_removes_the_key():
assert merge_patch({"a": 1, "b": 2}, {"b": None}) == {"a": 1}
def test_null_on_missing_key_is_a_noop():
assert merge_patch({"a": 1}, {"zzz": None}) == {"a": 1}
def test_objects_merge_recursively():
target = {"settings": {"x": 1, "nested": {"keep": True, "change": 1}}}
patch = {"settings": {"nested": {"change": 2}}}
assert merge_patch(target, patch) == {
"settings": {"x": 1, "nested": {"keep": True, "change": 2}}
}
def test_arrays_replace_wholesale():
# A list is a value, not a container to merge: "append one allowed command"
# is not expressible, which is intentional — partial list edits are how a
# policy ends up with values nobody ordered.
assert merge_patch({"list": [1, 2, 3]}, {"list": [9]}) == {"list": [9]}
def test_scalar_patch_replaces_target():
assert merge_patch({"a": {"deep": 1}}, {"a": "flat"}) == {"a": "flat"}
def test_dict_patch_over_scalar_target_starts_from_empty():
assert merge_patch({"a": "flat"}, {"a": {"deep": 1}}) == {"a": {"deep": 1}}
def test_non_dict_patch_replaces_everything():
assert merge_patch({"a": 1}, [1, 2]) == [1, 2]
assert merge_patch({"a": 1}, None) is None
def test_inputs_are_not_mutated():
target = {"a": {"b": 1}}
patch = {"a": {"c": 2}}
merge_patch(target, patch)
assert target == {"a": {"b": 1}}
assert patch == {"a": {"c": 2}}