proof-aware-crypto-tooling-.../scripts/mini_pytest.py

217 lines
6.8 KiB
Python
Raw Normal View History

Estate sync: boundary-axiom vocabulary + the four-tier apex reality (R4) The verified corpus completed its phase 2 on 2026-07-06: every ed25519 fork now carries FOUR button-enforced apex tiers up to the full lift (accept <=> decompress(R) = [k](-A)+[s]B as points), the complete scalar layer, and the constructive encoding/decoding chain. pacta was calibrated to the pre-apex corpus and - worse - had no vocabulary for boundary-audited certificates: its axiom audit knew only "clean = exactly the three standard axioms", so the apex tiers would have scored dirty. New vocabulary: - Profile.certificate_axioms: per-certificate ALLOWED axiom sets; expected_axioms_for(cert) resolves each certificate's own boundary. - RepoConfig.apex_boundary: a simple per-fork key (dalek-wrappers / hash3 / anza) expanded by the ed25519 profile into the exact per-tier allowed sets. AUTHORITY NOTE in profiles/ed25519.py: each repo's check.sh Phase 3b is the enforcement point; if the button and this table disagree, the button wins. - run_axiom_audit compares each certificate against ITS allowed set; deviation in EITHER direction (extra axiom or missing boundary axiom) is dirty. New risk reality: - R4 is now reachable: full four-tier apex + constructive chain + scalar arithmetic, all proven with cones pinned to their documented boundaries. R4 always carries explicit residual blockers (SHA-512 oracle, hypothesis-parametric wire parses, translation faithfulness, no side-channel/build assurance - those gate R5). - R3 unchanged (arithmetic pair) and now explains exactly which apex certificates are missing for R4. Attestation trust model hardened: - The provider is trusted for its OBSERVATION, never its VERDICT: axiom_status is re-derived locally from observed_axioms against the agent's own boundary policy. A provider that labels a dirty cone "clean" gains nothing; "proven" with no observed axioms is "unverifiable". - Partial attestations degrade instead of being rejected: uncovered certificates stay unproven and the score caps accordingly (an arithmetic-only attestation still authorizes an R3 library capsule, never a wallet). Also: scripts/mini_pytest.py - a dependency-free test runner (tmp_path, raises, monkeypatch, capsys) for hosts without pytest; examples regenerated FROM the tool (dalek/anza fixtures now R4, 16 certs; new full four-tier attestation example); tests updated + new tests/test_boundaries.py (lying-provider, missing-boundary-axiom, partial-coverage cases). 40/40 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:04:43 +00:00
#!/usr/bin/env python3
"""Dependency-free test runner for hosts without pytest.
Discovers tests/test_*.py, runs every test_* function, and supports the
small pytest surface this suite uses: the tmp_path fixture, pytest.raises,
pytest.skip, and pytest.mark.* as no-ops. Prefer real pytest when it is
installed; this shim exists so `python3 scripts/mini_pytest.py` works on a
bare interpreter (the same portability posture as the rest of pacta).
"""
from __future__ import annotations
import importlib.util
import inspect
import sys
import tempfile
import traceback
import types
from contextlib import contextmanager
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
for entry in (ROOT / "src", ROOT / "provider" / "src", ROOT):
sys.path.insert(0, str(entry))
class _Skip(Exception):
pass
def _install_pytest_stub() -> None:
if "pytest" in sys.modules:
return
stub = types.ModuleType("pytest")
@contextmanager
def raises(exc_type, match=None):
class _Ctx:
value = None
ctx = _Ctx()
try:
yield ctx
except exc_type as exc: # noqa: PERF203
ctx.value = exc
if match is not None:
import re
if not re.search(match, str(exc)):
raise AssertionError(f"exception message {exc!r} does not match {match!r}") from exc
return
raise AssertionError(f"expected {exc_type.__name__} was not raised")
def skip(reason=""):
raise _Skip(reason)
class _Mark:
def __getattr__(self, _name):
def deco(fn=None, *a, **k):
return fn if fn is not None else deco
return deco
stub.raises = raises
stub.skip = skip
stub.mark = _Mark()
stub.approx = lambda v, **k: v
sys.modules["pytest"] = stub
class _MonkeyPatch:
def __init__(self):
self._attr: list[tuple[object, str, object, bool]] = []
self._env: list[tuple[str, str | None]] = []
def setattr(self, target, name, value=None):
import os
if value is None and isinstance(target, str):
# pytest-style dotted target: import the longest importable
# prefix, then walk attributes (handles module-as-attribute
# forms like "pkg.module.shutil.which").
parts = target.split(".")
obj = None
for split in range(len(parts) - 1, 0, -1):
try:
obj = importlib.import_module(".".join(parts[:split]))
except ModuleNotFoundError:
continue
for attr_name in parts[split:-1]:
obj = getattr(obj, attr_name)
break
if obj is None:
raise ModuleNotFoundError(target)
target, name, value = obj, parts[-1], name
had = hasattr(target, name)
self._attr.append((target, name, getattr(target, name, None), had))
setattr(target, name, value)
def setenv(self, name, value):
import os
self._env.append((name, os.environ.get(name)))
os.environ[name] = value
def delenv(self, name, raising=True):
import os
self._env.append((name, os.environ.get(name)))
os.environ.pop(name, None if not raising else os.environ[name] and None)
def undo(self):
import os
for target, name, old, had in reversed(self._attr):
if had:
setattr(target, name, old)
else:
delattr(target, name)
for name, old in reversed(self._env):
if old is None:
os.environ.pop(name, None)
else:
os.environ[name] = old
class _CapSys:
def __init__(self):
import io
self._out, self._err = io.StringIO(), io.StringIO()
self._old = (sys.stdout, sys.stderr)
sys.stdout, sys.stderr = self._out, self._err
def readouterr(self):
out, err = self._out.getvalue(), self._err.getvalue()
self._out.truncate(0), self._out.seek(0)
self._err.truncate(0), self._err.seek(0)
return types.SimpleNamespace(out=out, err=err)
def stop(self):
sys.stdout, sys.stderr = self._old
def _load(path: Path) -> types.ModuleType:
spec = importlib.util.spec_from_file_location(path.stem, path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return module
def main(argv: list[str]) -> int:
_install_pytest_stub()
pattern = argv[1] if len(argv) > 1 else ""
passed = failed = skipped = 0
failures: list[str] = []
for path in sorted((ROOT / "tests").glob("test_*.py")):
if pattern and pattern not in path.name:
continue
try:
module = _load(path)
except Exception:
failed += 1
failures.append(f"{path.name}: import error\n{traceback.format_exc()}")
continue
for name in sorted(dir(module)):
if not name.startswith("test_"):
continue
fn = getattr(module, name)
if not callable(fn):
continue
kwargs = {}
params = inspect.signature(fn).parameters
tmp = None
cleanups = []
if "tmp_path" in params:
tmp = tempfile.TemporaryDirectory(prefix="mini-pytest-")
kwargs["tmp_path"] = Path(tmp.name)
if "monkeypatch" in params:
mp = _MonkeyPatch()
kwargs["monkeypatch"] = mp
cleanups.append(mp.undo)
if "capsys" in params:
cap = _CapSys()
kwargs["capsys"] = cap
cleanups.append(cap.stop)
unknown = [p for p in params if p not in kwargs]
if unknown:
skipped += 1
print(f"s {path.name}::{name} (unsupported fixtures: {unknown})")
continue
try:
fn(**kwargs)
passed += 1
print(f". {path.name}::{name}")
except _Skip as exc:
skipped += 1
print(f"s {path.name}::{name} ({exc})")
except Exception:
failed += 1
failures.append(f"{path.name}::{name}\n{traceback.format_exc()}")
print(f"F {path.name}::{name}")
finally:
for cleanup in cleanups:
cleanup()
if tmp is not None:
tmp.cleanup()
print(f"\n{passed} passed, {failed} failed, {skipped} skipped")
for failure in failures:
print("\n" + "=" * 70)
print(failure)
return 1 if failed else 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))