"""Idempotency and replay-safety tests for ``compute_tag_uuid`` (E.2.2). The deterministic UUIDv5 derivation is the load-bearing primitive behind ``INSERT OR IGNORE`` replay safety: the worker must be able to re-process the same source events any number of times — crash recovery, backfill, manual re-run — and converge to the same tag set. The replay-safety lock asserts the *exact* parameter set so a future contributor adding ``created_at``, ``os.getpid()``, ``random.random()`` or any other non-deterministic input cannot silently break replay safety; the test breaks first. """ from __future__ import annotations import inspect from typing import Optional from hypothesis import given, settings from hypothesis import strategies as st from decnet.web.db.models.ttp import _TTP_TAG_NS, compute_tag_uuid # ── Determinism ───────────────────────────────────────────────────── _input_tuple = st.tuples( st.text(min_size=1, max_size=32), st.text(min_size=1, max_size=64), st.text(min_size=1, max_size=32), st.integers(min_value=0, max_value=10_000), st.text(min_size=1, max_size=16), st.one_of(st.none(), st.text(min_size=1, max_size=16)), ) @given(t=_input_tuple) @settings(max_examples=100, deadline=None) def test_compute_tag_uuid_is_deterministic( t: tuple[str, str, str, int, str, Optional[str]], ) -> None: a = compute_tag_uuid(*t) b = compute_tag_uuid(*t) assert a == b # ── Collision resistance ──────────────────────────────────────────── @given( tuples=st.lists(_input_tuple, min_size=2, max_size=200, unique=True), ) @settings(max_examples=50, deadline=None) def test_distinct_inputs_yield_distinct_uuids( tuples: list[tuple[str, str, str, int, str, Optional[str]]], ) -> None: uuids = {compute_tag_uuid(*t) for t in tuples} # Distinct input tuples → distinct UUIDs in the practical input # space. UUIDv5 is 122-bit; collisions in N≤200 are # vanishingly unlikely. assert len(uuids) == len(tuples) # ── Golden-value lock ─────────────────────────────────────────────── def test_compute_tag_uuid_golden_value() -> None: """Pinned input → pinned UUID. Drift = breaking change. Worked example from the design doc (``find_recursive_root`` / R0014 on ``cmd_42``). If this assertion ever needs to flip, every existing tag UUID in production has been silently invalidated — treat as a migration event. """ assert ( compute_tag_uuid("command", "cmd_42", "R0014", 2, "T1083", None) == "9aa491e5-f03b-5d8f-9eb5-161becedcdd6" ) assert ( compute_tag_uuid("command", "cmd_42", "R0015", 1, "T1548", "T1548.001") == "4fd57b14-c135-544c-97d9-4fabc1051584" ) def test_namespace_constant_is_pinned() -> None: """The namespace UUID itself is part of the contract — regenerating it would invalidate every tag UUID.""" assert str(_TTP_TAG_NS) == "1ca31f08-5522-5aae-8371-fe81f0e39de3" # ── Replay-safety lock: parameter set ─────────────────────────────── def test_compute_tag_uuid_parameter_set_is_locked() -> None: """The accepted inputs MUST be exactly the six identity fields. Adding ``created_at``, a process PID, a random salt, or any other non-deterministic input silently breaks replay safety. This test is the trip-wire: a contributor must update it deliberately to change the input set. """ sig = inspect.signature(compute_tag_uuid) names = tuple(sig.parameters) assert names == ( "source_kind", "source_id", "rule_id", "rule_version", "technique_id", "sub_technique_id", )