core/tests/test_demo_composition.py
Shay 7f6c497a21
All checks were successful
smoke / smoke (-m "not quarantine") (pull_request) Successful in 14m5s
lane-shas / verify pinned lane SHAs (pull_request) Successful in 25m23s
fix(ci): path-stable env deltas so lane SHA pins stop thrashing
Root cause of demo_composition pin thrash was not cancelled CI runs:
verify_no_global_state_mutation dumped full env_subset before/after
tuples into the lane report, embedding CORE_ENGINE_STATE_DIR temp
paths (and any ambient CORE_* host values). Each hermetic run then
produced a different report SHA.

Fix for good:
- Report key-level env deltas only (+/- / changed), with path-like
  CORE_* values redacted to <path> in the message text.
- Detection still uses raw snapshots (mutations are not ignored).
- Hermetic CORE_ENGINE_STATE_DIR for demo_composition and all
  verify_lane_shas invocations.
- Dual-run-stable re-pin: demo_composition e2ba2314…,
  public_demo 7d8ba0db… (matches CI-observed soft-budget SHA).
- Drop dead CORE_SHOWCASE_SKIP_BUDGET workflow env (soft is default).
- Tests pin the delta format and hermetic-path stability.

Lane: python scripts/verify_lane_shas.py → 9/9 match.
2026-07-14 17:41:04 -07:00

299 lines
11 KiB
Python

"""ADR-0098 Demo Composition Contract — unit tests."""
from __future__ import annotations
from pathlib import Path
from typing import Any
import pytest
from core.demos import (
CLAIM_CONTRACT_VERSION,
Claim,
DemoCommand,
DemoContractError,
DemoResult,
canonical_json,
verify_no_global_state_mutation,
)
from core.demos.audit_tour_adapter import AuditTourDemo
from core.demos.contract import capture_state
from core.demos.tour_adapters import (
AnchorLensTourDemo,
OrthogonalityTourDemo,
RegisterTourDemo,
)
# ---------------------------------------------------------------------------
# Schema validation
# ---------------------------------------------------------------------------
class TestClaim:
def test_well_formed_claim(self) -> None:
c = Claim(
claim_id="c1",
statement="x is y",
supported=True,
evidence_locator="report.json",
)
assert c.as_dict() == {
"claim_id": "c1",
"statement": "x is y",
"supported": True,
"evidence_locator": "report.json",
}
@pytest.mark.parametrize(
"field, value",
[
("claim_id", ""),
("statement", " "),
("evidence_locator", ""),
],
)
def test_empty_required_field_rejected(self, field: str, value: str) -> None:
kwargs = dict(
claim_id="c1",
statement="x is y",
supported=True,
evidence_locator="locator",
)
kwargs[field] = value
with pytest.raises(DemoContractError):
Claim(**kwargs)
def test_frozen(self) -> None:
c = Claim(
claim_id="c1",
statement="x",
supported=True,
evidence_locator="y",
)
with pytest.raises((AttributeError, TypeError)):
c.supported = False # type: ignore[misc]
class TestDemoResult:
def _make_claims(self) -> tuple[Claim, ...]:
return (
Claim(claim_id="c1", statement="s1", supported=True, evidence_locator="e1"),
Claim(claim_id="c2", statement="s2", supported=True, evidence_locator="e2"),
)
def test_well_formed_result(self, tmp_path: Path) -> None:
claims = self._make_claims()
result = DemoResult(
demo_id="d1",
claim_contract_version=CLAIM_CONTRACT_VERSION,
claims=claims,
evidence={"c1": "x", "c2": "y"},
all_claims_supported=True,
json_path=tmp_path / "d1.json",
)
assert result.all_claims_supported is True
assert result.demo_id == "d1"
def test_duplicate_claim_id_rejected(self, tmp_path: Path) -> None:
dup = (
Claim(claim_id="c1", statement="s", supported=True, evidence_locator="e"),
Claim(claim_id="c1", statement="s", supported=True, evidence_locator="e"),
)
with pytest.raises(DemoContractError, match="duplicate claim_id"):
DemoResult(
demo_id="d1",
claim_contract_version=CLAIM_CONTRACT_VERSION,
claims=dup,
evidence={"c1": "x"},
all_claims_supported=True,
json_path=tmp_path / "d1.json",
)
def test_wrong_contract_version_rejected(self, tmp_path: Path) -> None:
with pytest.raises(DemoContractError, match="claim_contract_version"):
DemoResult(
demo_id="d1",
claim_contract_version=2,
claims=(),
evidence={},
all_claims_supported=True,
json_path=tmp_path / "d1.json",
)
def test_missing_evidence_rejected(self, tmp_path: Path) -> None:
with pytest.raises(DemoContractError, match="evidence missing"):
DemoResult(
demo_id="d1",
claim_contract_version=CLAIM_CONTRACT_VERSION,
claims=self._make_claims(),
evidence={"c1": "x"}, # missing c2
all_claims_supported=True,
json_path=tmp_path / "d1.json",
)
# ---------------------------------------------------------------------------
# Canonical JSON serializer
# ---------------------------------------------------------------------------
class TestCanonicalJson:
def test_deterministic_across_calls(self) -> None:
payload = {"b": 1, "a": [1, 2, 3], "c": {"y": 0, "x": 1}}
assert canonical_json(payload) == canonical_json(payload)
def test_keys_sorted(self) -> None:
out = canonical_json({"z": 1, "a": 2}).decode()
assert out.index('"a"') < out.index('"z"')
def test_trailing_newline(self) -> None:
assert canonical_json({"x": 1}).endswith(b"\n")
# ---------------------------------------------------------------------------
# Global-state-mutation detector
# ---------------------------------------------------------------------------
class TestGlobalStateDetector:
def test_identical_snapshots_pass(self) -> None:
s = capture_state()
passed, divergences = verify_no_global_state_mutation(before=s, after=s)
assert passed is True
assert divergences == ()
def test_env_var_addition_flagged(self) -> None:
import os
os.environ.pop("CORE_DETECTOR_TEST_FLAG", None)
before = capture_state()
os.environ["CORE_DETECTOR_TEST_FLAG"] = "1"
after = capture_state()
os.environ.pop("CORE_DETECTOR_TEST_FLAG", None)
passed, divergences = verify_no_global_state_mutation(before=before, after=after)
assert passed is False
assert any("env_subset" in d for d in divergences)
# Key-level delta only — never a full env dump (host paths break pins).
assert any(
d == "env_subset: +CORE_DETECTOR_TEST_FLAG=1" for d in divergences
)
assert not any("before=" in d for d in divergences)
def test_env_delta_stable_across_hermetic_engine_state_paths(self) -> None:
"""Lane pins must not depend on CORE_ENGINE_STATE_DIR temp paths."""
before_a = {
"env_subset": (("CORE_ENGINE_STATE_DIR", "/tmp/run_a_path"),),
}
after_a = {
"env_subset": (
("CORE_ENGINE_STATE_DIR", "/tmp/run_a_path"),
("CORE_STATEFUL_FIXTURE_FLAG", "1"),
),
}
before_b = {
"env_subset": (("CORE_ENGINE_STATE_DIR", "/var/folders/xyz/run_b"),),
}
after_b = {
"env_subset": (
("CORE_ENGINE_STATE_DIR", "/var/folders/xyz/run_b"),
("CORE_STATEFUL_FIXTURE_FLAG", "1"),
),
}
passed_a, div_a = verify_no_global_state_mutation(before=before_a, after=after_a)
passed_b, div_b = verify_no_global_state_mutation(before=before_b, after=after_b)
assert passed_a is False and passed_b is False
assert div_a == div_b
assert div_a == ("env_subset: +CORE_STATEFUL_FIXTURE_FLAG=1",)
def test_path_like_env_change_detected_without_absolute_path(self) -> None:
before = {"env_subset": (("CORE_ENGINE_STATE_DIR", "/tmp/old"),)}
after = {"env_subset": (("CORE_ENGINE_STATE_DIR", "/tmp/new"),)}
passed, divergences = verify_no_global_state_mutation(before=before, after=after)
assert passed is False
assert divergences == (
"env_subset: CORE_ENGINE_STATE_DIR '<path>' -> '<path>'",
)
assert "/tmp/" not in "".join(divergences)
def test_lazy_import_not_flagged(self) -> None:
"""None → module id transition is benign (lazy import)."""
before = {"chat.runtime": None, "env_subset": ()}
after = {"chat.runtime": 12345, "env_subset": ()}
passed, divergences = verify_no_global_state_mutation(before=before, after=after)
assert passed is True
assert divergences == ()
def test_id_to_id_rebind_flagged(self) -> None:
"""id → different id is a real rebinding."""
before = {"chat.runtime": 100, "env_subset": ()}
after = {"chat.runtime": 200, "env_subset": ()}
passed, divergences = verify_no_global_state_mutation(before=before, after=after)
assert passed is False
assert any("chat.runtime" in d for d in divergences)
# ---------------------------------------------------------------------------
# Protocol conformance
# ---------------------------------------------------------------------------
class TestProtocolConformance:
@pytest.mark.parametrize(
"adapter_factory",
[
AuditTourDemo,
RegisterTourDemo,
AnchorLensTourDemo,
OrthogonalityTourDemo,
],
)
def test_adapter_is_demo_command(self, adapter_factory: Any) -> None:
adapter = adapter_factory()
assert isinstance(adapter, DemoCommand)
assert adapter.claim_contract_version == CLAIM_CONTRACT_VERSION
assert adapter.demo_id.strip()
@pytest.mark.parametrize(
"adapter_factory",
[AuditTourDemo, RegisterTourDemo, OrthogonalityTourDemo],
)
def test_seed_rejected_when_unsupported(
self, adapter_factory: Any, tmp_path: Path
) -> None:
adapter = adapter_factory()
with pytest.raises(DemoContractError, match="does not accept a seed"):
adapter.run(output_dir=tmp_path, seed=42)
# ---------------------------------------------------------------------------
# Adapter runtime behavior (fast adapters only — audit-tour)
# ---------------------------------------------------------------------------
class TestAuditTourAdapterIntegration:
"""One end-to-end run + byte-equality check on the fastest adapter."""
def test_run_produces_supported_claims(self, tmp_path: Path) -> None:
adapter = AuditTourDemo()
result = adapter.run(output_dir=tmp_path)
assert result.demo_id == "audit-tour"
assert len(result.claims) == 5
assert result.all_claims_supported is True
assert result.json_path.exists()
assert result.json_path.parent == tmp_path
def test_byte_equal_across_two_runs(self, tmp_path: Path) -> None:
adapter = AuditTourDemo()
a = adapter.run(output_dir=tmp_path / "a")
b = adapter.run(output_dir=tmp_path / "b")
assert a.json_path.read_bytes() == b.json_path.read_bytes()
def test_no_global_state_mutation(self, tmp_path: Path) -> None:
before = capture_state()
AuditTourDemo().run(output_dir=tmp_path)
after = capture_state()
passed, divergences = verify_no_global_state_mutation(
before=before, after=after
)
assert passed is True, f"unexpected divergences: {divergences}"