Files
2026-05-27 13:33:52 +02:00

233 lines
12 KiB
Python

"""Tests for diff."""
import pytest
from copy import deepcopy
from jaydiff.diff import diff_full, diff_add, diff_del, diff_upd, diff_upd_add
# ── diff_add ──────────────────────────────────────────────────────────────
class TestJayDiffAdd:
def test_add_to_empty(self):
assert diff_add({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}) \
== {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}
def test_no_new_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {}
def test_old_has_extra_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}) == {}
def test_one_new_key(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) == {'d': 'icecream'}
def test_new_key_among_overlap(self):
assert diff_add({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {'b': 'fries'}
def test_nested_new_key(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}},
{'c': {'f': 'coffee'}}) == {'c': {'f': 'coffee'}}
def test_nested_no_new_keys(self):
assert diff_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}) == {}
# ── diff_del ──────────────────────────────────────────────────────────────
class TestJayDiffDel:
def test_one_deleted_key(self):
assert diff_del({'a': 'hamburger', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {'d': 'icecream'}
def test_no_deleted_keys_same_values(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}) == {}
def test_no_deleted_keys_different_values(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}) == {}
def test_key_absent_from_new(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries'}) == {'c': 'pepsi'}
def test_nested_deleted_key(self):
# 'c' is absent from _new → None signals "delete entire key"
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}) == {'c': None}
def test_nested_partial_delete(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'coke'}}) == {'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}
def test_nested_no_delete_different_value(self):
assert diff_del({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}}) == {}
# ── diff_upd ──────────────────────────────────────────────────────────────
class TestJayDiffUpd:
def test_multiple_updates(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}) == {'a': 'hotdog', 'b': 'potatoes'}
def test_nested_update(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi'}}) == {'c': {'e': 'pepsi'}}
def test_one_update(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'fries'}) == {'a': 'hotdog'}
def test_no_update_new_key_ignored(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) == {'c': 'coke'}
def test_nested_no_update_new_subkey_ignored(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke'}},
{'c': {'f': 'coffee'}}) == {}
def test_nested_update_one_of_two(self):
assert diff_upd({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee'}}) \
== {'c': {'e': 'pepsi'}}
# ── diff_upd_add ──────────────────────────────────────────────────────────
class TestJayDiffUpdAdd:
def test_update_and_add(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}) \
== {'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}
def test_nested_update_and_add(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}, 'b': 'potatoes'}) \
== {'b': 'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}}
def test_update_only(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'fries'}) == {'a': 'hotdog'}
def test_nested_update_and_new_subkey(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}}) \
== {'c': {'e': 'pepsi', 'h': 'cake'}}
def test_add_new_top_and_update(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}) \
== {'c': 'coke', 'd': 'icecream'}
def test_identical(self):
assert diff_upd_add({'a': 'hamburger', 'b': 'fries'},
{'a': 'hamburger', 'b': 'fries'}) == {}
# ── diff_full (combine_upd_add=False) ─────────────────────────────────────
class TestJayDiffFullSeparate:
F = False
def test_add_all(self):
assert diff_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, self.F) \
== {'add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}
def test_update_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.F) \
== {'update': {'c': 'coke'}}
def test_delete_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, self.F) \
== {'delete': {'d': 'icecream'}}
def test_add_delete_update(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}, self.F) \
== {'add': {'d': 'icecream'}, 'delete': {'b': 'fries'}, 'update': {'c': 'coke'}}
def test_identical(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.F) == {}
def test_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}, self.F) \
== {'delete': {'c': None}}
def test_nested_partial_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'f': 'coffee'}}, self.F) \
== {'delete': {'c': {'e': 'coke'}}}
def test_update_and_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}, self.F) \
== {'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}
def test_nested_add_delete_update(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes', 'd': 'icecream'}, self.F) \
== {'add': {'d': 'icecream'}, 'delete': {'c': None}, 'update': {'a': 'hotdog', 'b': 'potatoes'}}
# ── diff_full (combine_upd_add=True) ──────────────────────────────────────
class TestJayDiffFullCombined:
T = True
def test_add_all(self):
assert diff_full({}, {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}, self.T) \
== {'update_add': {'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}}}
def test_update_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.T) \
== {'update_add': {'c': 'coke'}}
def test_delete_one(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi', 'd': 'icecream'},
{'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'}, self.T) \
== {'delete': {'d': 'icecream'}}
def test_delete_and_update_add(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'pepsi'},
{'a': 'hamburger', 'd': 'icecream', 'c': 'coke'}, self.T) \
== {'delete': {'b': 'fries'}, 'update_add': {'c': 'coke', 'd': 'icecream'}}
def test_identical(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': 'coke'},
{'a': 'hamburger', 'b': 'fries', 'c': 'coke'}, self.T) == {}
def test_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries'}, self.T) \
== {'delete': {'c': None}}
def test_update_and_delete_nested(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hotdog', 'b': 'potatoes'}, self.T) \
== {'delete': {'c': None}, 'update_add': {'a': 'hotdog', 'b': 'potatoes'}}
def test_nested_update_add_and_delete(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}, 'b': 'potatoes'}, self.T) \
== {'delete': {'a': 'hamburger', 'c': {'f': 'coffee'}},
'update_add': {'b': 'potatoes', 'c': {'e': 'pepsi', 'g': 'tea', 'h': 'cake'}}}
def test_nested_add_subkey(self):
assert diff_full({'a': 'hamburger', 'b': 'fries', 'c': {'e': 'coke', 'f': 'coffee'}},
{'a': 'hamburger', 'b': 'fries', 'c': {'e': 'pepsi', 'f': 'coffee', 'h': 'cake'}}, self.T) \
== {'update_add': {'c': {'e': 'pepsi', 'h': 'cake'}}}