"""Three-way wire-parity check. For each backend (FastAPI, Rust axum): 1. Boot the fixture server on a free port. 2. Poll /api/mizan/session/ until the server responds. 3. Run the Rust `drive_kernel` binary (raw kernel calls) against it. 4. Run the Rust `drive_emitted` binary (typed codegen functions) against it. 5. Tear the server down. Any non-zero driver exit propagates as the script's exit code. Adding the Rust backend here proves that mizan-axum honors the same wire contract as mizan-fastapi — same JSON shapes, same invalidate/merge semantics — beyond the static IR equivalence the codegen-parity test gates. Readiness probe is `/api/mizan/session/` (Mizan-protocol-shaped) rather than `/openapi.json` (FastAPI-feature-shaped) so the harness reads the same surface across backends. """ from __future__ import annotations import os import socket import subprocess import sys import time import urllib.error import urllib.request from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] AFI_DIR = REPO_ROOT / "tests" / "afi" RUST_DIR = REPO_ROOT / "tests" / "rust" RUST_APP_DIR = AFI_DIR / "rust_app" BOOT_TIMEOUT_S = 15.0 POLL_INTERVAL_S = 0.25 def pick_free_port() -> int: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] def wait_for_server(port: int, timeout_s: float, label: str) -> bool: deadline = time.monotonic() + timeout_s url = f"http://127.0.0.1:{port}/api/mizan/session/" while time.monotonic() < deadline: try: with urllib.request.urlopen(url, timeout=1.0) as resp: if resp.status == 200: return True except (urllib.error.URLError, ConnectionError, OSError) as e: sys.stderr.write(f"[wire_parity:{label}] waiting: {type(e).__name__}\n") time.sleep(POLL_INTERVAL_S) return False def run_driver(name: str, base_url: str, label: str) -> int: sys.stdout.write(f"\n=== {label} :: {name} ===\n") sys.stdout.flush() return subprocess.run( ["cargo", "run", "--quiet", "--bin", name, "--", base_url], cwd=RUST_DIR, ).returncode def boot_fastapi(port: int) -> subprocess.Popen: return subprocess.Popen( [ "uv", "run", "uvicorn", "fastapi_app:make_app", "--factory", "--port", str(port), "--log-level", "warning", ], cwd=AFI_DIR, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) def boot_rust(port: int) -> subprocess.Popen: return subprocess.Popen( [ "cargo", "run", "--quiet", "--release", "--manifest-path", str(RUST_APP_DIR / "Cargo.toml"), "--bin", "server", ], env={**os.environ, "PORT": str(port)}, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) def run_against(label: str, server: subprocess.Popen, port: int) -> int: failures = 0 try: if not wait_for_server(port, BOOT_TIMEOUT_S, label): sys.stderr.write(f"[wire_parity:{label}] server boot timed out after {BOOT_TIMEOUT_S}s\n") if server.stderr: tail = server.stderr.read(4096) if tail: sys.stderr.write(tail.decode("utf-8", errors="replace")) return 1 base_url = f"http://127.0.0.1:{port}/api/mizan" for driver in ("drive_kernel", "drive_emitted"): rc = run_driver(driver, base_url, label) if rc != 0: sys.stderr.write(f"[wire_parity:{label}] {driver} exited {rc}\n") failures += 1 finally: server.terminate() try: server.wait(timeout=3) except subprocess.TimeoutExpired: server.kill() server.wait() return failures def main() -> int: total_failures = 0 fastapi_port = pick_free_port() sys.stdout.write(f"[wire_parity] booting fastapi on port {fastapi_port}\n") total_failures += run_against("fastapi", boot_fastapi(fastapi_port), fastapi_port) rust_port = pick_free_port() sys.stdout.write(f"[wire_parity] booting rust on port {rust_port}\n") total_failures += run_against("rust", boot_rust(rust_port), rust_port) return 0 if total_failures == 0 else 1 if __name__ == "__main__": sys.exit(main())