"""Tests for `hash_tip_data` — the dismissal-expiry fingerprint. See `app.entities.tip_dismissal.TipDismissal` and `TipsService._drop_stale_dismissed`: a dismissal only stays silent while the rule's `data` still hashes the same. These tests pin the two properties that matter for that contract: stability (same data → same hash, regardless of key order) and sensitivity (any value change → a different hash). """ from app.utils.tip_predicates import hash_tip_data class TestHashTipData: def test_stable_regardless_of_key_order(self) -> None: a = hash_tip_data({"parent_name": "Alice", "expiring_days": 12}) b = hash_tip_data({"expiring_days": 12, "parent_name": "Alice"}) assert a == b def test_changes_when_a_value_changes(self) -> None: a = hash_tip_data({"parent_name": "Alice", "expiring_days": 12}) b = hash_tip_data({"parent_name": "Alice", "expiring_days": 13}) assert a != b def test_changes_when_a_key_is_added_or_removed(self) -> None: a = hash_tip_data({"parent_name": "Alice"}) b = hash_tip_data({"parent_name": "Alice", "expiring_days": 12}) assert a != b def test_empty_data_hashes_consistently(self) -> None: assert hash_tip_data({}) == hash_tip_data({})