"""Replay each contract example in ../contract against a fresh Manager. Each example lists calls in order, keyed by method name. A call has "args" (omitted for methods that take none) and either "result" (the expected return value as JSON; floats are compared to 6 significant figures) or "error" (a UserError of "kind": "user", whose text contains "message" if given). This mirrors what @elvis/pyodide's worker sends to the UI. """ import json from pathlib import Path import pytest from manager import Manager CONTRACT_DIR = Path(__file__).parent.parent / "contract" EXAMPLES = sorted(CONTRACT_DIR.glob("*.json")) def same(actual, expected) -> bool: """Equal JSON values, with floats compared approximately.""" if isinstance(expected, float) or isinstance(actual, float): return isinstance(actual, (int, float)) and actual == pytest.approx(expected, rel=1e-6, abs=1e-12) if isinstance(expected, dict): return isinstance(actual, dict) and actual.keys() == expected.keys() and all(same(actual[k], expected[k]) for k in expected) if isinstance(expected, list): return isinstance(actual, list) and len(actual) == len(expected) and all(same(a, e) for a, e in zip(actual, expected)) return actual == expected @pytest.mark.parametrize("path", EXAMPLES, ids=[p.stem for p in EXAMPLES]) def test_contract_example(path: Path) -> None: example = json.loads(path.read_text()) manager = Manager() for method, call in example["calls"].items(): fn = getattr(manager, method) try: data = fn(call["args"]) if "args" in call else fn() except Exception as e: # The worker treats only UserError as a user error; anything else is a bug if type(e).__name__ != "UserError": raise assert "error" in call, f"{method} raised UserError: {e}" assert call["error"]["kind"] == "user" assert call["error"].get("message", "") in str(e) continue assert "error" not in call, f"{method} returned {data!r} instead of raising UserError" # Results must be plain JSON values (no NumPy types) to reach the UI unchanged actual = json.loads(json.dumps(data)) assert same(actual, call["result"]), f"{method} returned {actual!r}"