|
| 1 | +"""Tests for ``agentscore_commerce.identity.tokens.hash_operator_token``. |
| 2 | +
|
| 3 | +The expected digests below are hardcoded — locked as the cross-language |
| 4 | +contract with the Node sibling at ``node-commerce/tests/identity/tokens.test.ts``. |
| 5 | +Both files reference the same fixture inputs and the same expected output bytes. |
| 6 | +A drift in either language (algorithm swap, encoding change, accidental truncation) |
| 7 | +fails that language's test against the locked digest. |
| 8 | +""" |
| 9 | + |
| 10 | +from __future__ import annotations |
| 11 | + |
| 12 | +import pytest |
| 13 | + |
| 14 | +from agentscore_commerce.identity import hash_operator_token |
| 15 | + |
| 16 | +# Cross-language fixture inputs + expected digests. The digests are |
| 17 | +# sha256(<input>.encode("utf-8")).hexdigest() computed once and locked here so |
| 18 | +# the Python and Node sibling tests assert against identical bytes. |
| 19 | +# |
| 20 | +# Parametrized so each fixture gets its own test invocation: if multiple |
| 21 | +# fixtures drift simultaneously, every failure is reported (a for-loop inside |
| 22 | +# one test would short-circuit on the first failure). |
| 23 | +_FIXTURES = [ |
| 24 | + ("opc_test", "97c30e2a512b5968772c2930705bdafff4831d672556dce26c92b83f7e58508d"), |
| 25 | + ("opc_cross_lang_fixture", "96690dd2659bc1e33227e943d5f8a526c7c95a0ede5775a1573abab6578ca8ec"), |
| 26 | + ("opc_anything", "e6ba517ac96ee39190c4d703b2d968fec96e87827374e56095a2f443d870730d"), |
| 27 | + ("opc_42", "731985dd676ea0702b3e6f6cbb107eaf467319e2801e6f953f08cbcc7dd71684"), |
| 28 | + # Non-ASCII fixture — UTF-8 encoding of "é" is 0xC3 0xA9; locks the encoding |
| 29 | + # contract so a future implementation that drops the explicit "utf-8" arg |
| 30 | + # still produces the same bytes. |
| 31 | + ("opc_é", "c1dba11d60cbfc1264d115e07a74a0355b6a66ded4ee3f930024a1733ba6942f"), |
| 32 | + # Empty-string sha256 is a canonical value documented in many specs; locking |
| 33 | + # it here catches an implementation that silently rejects or transforms "". |
| 34 | + ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), |
| 35 | +] |
| 36 | + |
| 37 | + |
| 38 | +@pytest.mark.parametrize(("plaintext", "expected"), _FIXTURES, ids=[repr(p) for p, _ in _FIXTURES]) |
| 39 | +def test_known_digest_locked(plaintext: str, expected: str) -> None: |
| 40 | + """Each fixture input maps to the locked cross-language digest.""" |
| 41 | + assert hash_operator_token(plaintext) == expected |
| 42 | + |
| 43 | + |
| 44 | +def test_output_is_64_char_lowercase_hex() -> None: |
| 45 | + """sha256 hex digests are always 64 characters of lowercase hex.""" |
| 46 | + out = hash_operator_token("opc_anything") |
| 47 | + assert len(out) == 64 |
| 48 | + assert out == out.lower() |
| 49 | + assert all(c in "0123456789abcdef" for c in out) |
| 50 | + |
| 51 | + |
| 52 | +def test_deterministic_across_calls() -> None: |
| 53 | + """Same input always yields the same digest (no salt, no nonce).""" |
| 54 | + assert hash_operator_token("opc_42") == hash_operator_token("opc_42") |
| 55 | + |
| 56 | + |
| 57 | +def test_distinct_inputs_distinct_outputs() -> None: |
| 58 | + """Different plaintexts produce different digests.""" |
| 59 | + assert hash_operator_token("opc_a") != hash_operator_token("opc_b") |
0 commit comments