tests/pi_star: 21 tests for protocol + registry (foundation, was untested)

arborist/pi_star/protocol.py + arborist/pi_star/registry.py are
the foundation every concrete π* kernel rides on. Both shipped
in #000015 Phase 1 with zero direct tests; concrete kernels
(arithmetic@v1, logic-kernel@v1, algebra-symbolic@v1, ...) have
their own test files but the protocol contract + registry
mutation discipline weren't pinned.

Coverage:

  protocol.py
  - PiStar @runtime_checkable: instances satisfying the duck-type
    pass isinstance check; missing-method instances are rejected
  - registry_key returns "name@version" exactly; distinct versions
    yield distinct keys
  - equivalence_class_id is sha256 over canonicalize() output;
    invariant under pre-canonical form (two raws that canonicalize
    to the same bytes get the same eclass id); distinguishes
    different canonicals
  - assert_round_trip passes on idempotent π*; raises
    AssertionError naming the kernel on non-idempotent;
    propagates PiStarError when canonicalize raises on the test
    input itself

  registry.py
  - register inserts; get retrieves
  - register IS idempotent for the same instance at the same key
    (no error)
  - register REJECTS a different instance at the same key
    (cache_key invariant: name@version content-pinned)
  - same name with different versions coexist
  - get raises KeyError on unknown key
  - list_keys returns sorted keys (deterministic for cache_key
    derivation downstream)
  - domains() groups by domain; per-domain key lists are sorted;
    empty registry → empty dict

isolated_registry fixture monkeypatches REGISTRY to {} for
mutation tests so the global registry stays untouched (matches
the module docstring's no-public-unregister discipline).

Substrate-paper-spec'd primitives (arborist/substrate/* and
arborist/pi_star/protocol.py + registry.py) all directly tested
now.
This commit is contained in:
russell@unturf.com 2026-05-10 12:44:13 -04:00
parent 3ef8975623
commit b17900cb33
No known key found for this signature in database

View file

@ -0,0 +1,280 @@
"""Tests for ``arborist.pi_star.protocol`` + ``arborist.pi_star.registry``
(#000015 Phase 1 substrate).
Both modules previously had zero direct tests despite being the
foundation every π* kernel rides on. Every concrete kernel
(``arithmetic@v1``, ``logic-kernel@v1``, ``algebra-symbolic@v1``,
) implements the :class:`PiStar` Protocol; the registry holds the
``name@version`` instance map that ``cache_key`` derives from.
Test discipline:
- Use unique names per test to avoid polluting the global
REGISTRY (no public unregister; mutation discipline per the
module docstring).
- Test the Protocol contract via tiny stub classes that satisfy
the duck-type without inheriting.
"""
from __future__ import annotations
import hashlib
import pytest
from arborist.pi_star import registry as reg
from arborist.pi_star.protocol import (
PiStar,
PiStarError,
assert_round_trip,
equivalence_class_id,
registry_key,
)
# --- stub π* implementations ----------------------------------------
class _IdentityStub:
"""π* that passes raw bytes through unchanged. Trivially
idempotent. Used as a baseline for the Protocol contract."""
def __init__(self, name: str = "identity-stub", version: str = "v1",
domain: str = "text"):
self.name = name
self.version = version
self.domain = domain
def canonicalize(self, raw: bytes) -> bytes:
return raw
class _StripWhitespaceStub:
"""π* that strips whitespace. Idempotent: stripping twice =
stripping once."""
name = "strip-ws-stub"
version = "v1"
domain = "text"
def canonicalize(self, raw: bytes) -> bytes:
return raw.strip()
class _NonIdempotentStub:
"""π* that doubles its input. NOT idempotent — used only to
test that assert_round_trip catches the violation."""
name = "double-stub"
version = "v1"
domain = "text"
def canonicalize(self, raw: bytes) -> bytes:
return raw + raw
class _DomainGuardStub:
"""π* that raises PiStarError on bytes that don't start with 'X:'.
Used to test that the protocol's contract honors the
PiStarError-on-out-of-domain rule."""
name = "domain-guard-stub"
version = "v1"
domain = "text"
def canonicalize(self, raw: bytes) -> bytes:
if not raw.startswith(b"X:"):
raise PiStarError(f"out-of-domain input: {raw!r}")
return raw[2:]
# --- protocol — runtime checkable -----------------------------------
def test_pistar_runtime_checkable():
"""@runtime_checkable Protocol → instances satisfying the
duck-type pass `isinstance(x, PiStar)` without inheriting."""
assert isinstance(_IdentityStub(), PiStar)
assert isinstance(_StripWhitespaceStub(), PiStar)
# An object with all 3 required attrs + canonicalize method
# passes the runtime check.
def test_pistar_runtime_check_rejects_missing_method():
"""Object missing `canonicalize` → not a PiStar."""
class _Incomplete:
name = "x"
version = "v1"
domain = "text"
# no canonicalize method
# @runtime_checkable Protocol checks attribute presence at
# isinstance-time; an object missing the method is rejected.
assert not isinstance(_Incomplete(), PiStar)
# --- registry_key ---------------------------------------------------
def test_registry_key_format():
"""``registry_key`` returns ``name@version`` exactly."""
s = _IdentityStub(name="my-kernel", version="v3")
assert registry_key(s) == "my-kernel@v3"
def test_registry_key_distinct_versions_distinct_keys():
a = _IdentityStub(name="kern", version="v1")
b = _IdentityStub(name="kern", version="v2")
assert registry_key(a) != registry_key(b)
# --- equivalence_class_id ------------------------------------------
def test_equivalence_class_id_sha256_over_canonical_bytes():
"""``equivalence_class_id`` is sha256 over canonical bytes."""
s = _IdentityStub()
raw = b"hello world"
expected = hashlib.sha256(raw).hexdigest()
assert equivalence_class_id(s, raw) == expected
def test_equivalence_class_id_invariant_under_pre_canonical_form():
"""Two raw inputs that canonicalize to the same bytes → same
equivalence_class_id. Demonstrates the canonicalization
fundamental property."""
s = _StripWhitespaceStub()
a = b" hello "
b = b"hello"
# Both canonicalize to b"hello".
assert equivalence_class_id(s, a) == equivalence_class_id(s, b)
def test_equivalence_class_id_distinguishes_different_canonicals():
s = _IdentityStub()
assert equivalence_class_id(s, b"a") != equivalence_class_id(s, b"b")
# --- assert_round_trip ----------------------------------------------
def test_assert_round_trip_passes_on_idempotent():
"""Idempotent π* survives the round-trip check."""
assert_round_trip(_IdentityStub(), b"any input")
assert_round_trip(_StripWhitespaceStub(), b" spaced ")
def test_assert_round_trip_raises_on_non_idempotent():
"""Non-idempotent π* → AssertionError naming the kernel."""
with pytest.raises(AssertionError, match="double-stub@v1"):
assert_round_trip(_NonIdempotentStub(), b"doubles")
def test_assert_round_trip_propagates_pi_star_error():
"""If `canonicalize` itself raises PiStarError on the input,
that propagates (round-trip can't be checked on out-of-domain
input caller's responsibility to gate)."""
with pytest.raises(PiStarError, match="out-of-domain"):
assert_round_trip(_DomainGuardStub(), b"no prefix")
# --- domain-guard contract ------------------------------------------
def test_canonicalize_raises_pi_star_error_on_undefined_input():
"""Per the protocol docstring: 'canonicalize raises PiStarError
when raw is outside the projection's domain.'"""
s = _DomainGuardStub()
with pytest.raises(PiStarError):
s.canonicalize(b"missing prefix")
def test_canonicalize_succeeds_within_domain():
s = _DomainGuardStub()
out = s.canonicalize(b"X:in-domain")
assert out == b"in-domain"
# --- registry — register / get / list_keys / domains ----------------
@pytest.fixture
def isolated_registry(monkeypatch):
"""Replace REGISTRY with an empty dict for the duration of a
test, restoring on teardown. Avoids polluting the global
registry (which has no public unregister per the module
docstring discipline)."""
monkeypatch.setattr(reg, "REGISTRY", {})
def test_register_inserts_and_get_retrieves(isolated_registry):
s = _IdentityStub(name="reg-test-kernel", version="v1")
reg.register(s)
assert reg.get("reg-test-kernel@v1") is s
def test_register_idempotent_for_same_instance(isolated_registry):
"""Re-registering the SAME instance is a no-op (no error)."""
s = _IdentityStub(name="idem-kernel", version="v1")
reg.register(s)
reg.register(s) # must not raise
assert reg.get("idem-kernel@v1") is s
def test_register_rejects_different_instance_at_same_key(isolated_registry):
"""Two different instances claiming the same name@version →
PiStarError (cache_key invariant: name@version is content-pinned)."""
a = _IdentityStub(name="dup-kernel", version="v1")
b = _IdentityStub(name="dup-kernel", version="v1") # different instance
reg.register(a)
with pytest.raises(PiStarError, match="already bound"):
reg.register(b)
def test_register_allows_different_versions_same_name(isolated_registry):
"""Same name, different version → distinct keys, both register OK.
Lets a v2 kernel coexist with v1 (cache_key invariant unaffected)."""
a = _IdentityStub(name="multi-version-kernel", version="v1")
b = _IdentityStub(name="multi-version-kernel", version="v2")
reg.register(a)
reg.register(b)
assert reg.get("multi-version-kernel@v1") is a
assert reg.get("multi-version-kernel@v2") is b
def test_get_raises_keyerror_on_unknown_key(isolated_registry):
with pytest.raises(KeyError, match="unknown"):
reg.get("never-registered@v1")
def test_list_keys_returns_sorted_keys(isolated_registry):
"""Sorted return order is part of the contract — bench harnesses
iterate the list expecting deterministic order."""
reg.register(_IdentityStub(name="zeta", version="v1"))
reg.register(_IdentityStub(name="alpha", version="v1"))
reg.register(_IdentityStub(name="mu", version="v1"))
keys = reg.list_keys()
assert keys == sorted(keys)
assert keys == ["alpha@v1", "mu@v1", "zeta@v1"]
def test_domains_groups_by_domain(isolated_registry):
reg.register(_IdentityStub(name="text-kern-a", version="v1", domain="text"))
reg.register(_IdentityStub(name="text-kern-b", version="v1", domain="text"))
reg.register(_IdentityStub(name="logic-kern", version="v1", domain="logic"))
domains = reg.domains()
assert set(domains.keys()) == {"text", "logic"}
assert domains["text"] == ["text-kern-a@v1", "text-kern-b@v1"]
assert domains["logic"] == ["logic-kern@v1"]
def test_domains_per_domain_keys_sorted(isolated_registry):
"""Each domain's key list is sorted (deterministic for cache_key
derivation downstream)."""
reg.register(_IdentityStub(name="z-kern", version="v1", domain="text"))
reg.register(_IdentityStub(name="a-kern", version="v1", domain="text"))
reg.register(_IdentityStub(name="m-kern", version="v1", domain="text"))
domains = reg.domains()
assert domains["text"] == sorted(domains["text"])
def test_domains_empty_when_registry_empty(isolated_registry):
assert reg.domains() == {}