"""Spawn-safe HTTP/SSE fixture, controlled only through a test-owned directory.""" import asyncio from contextlib import asynccontextmanager import json import os from pathlib import Path import time from fastapi import FastAPI from starlette.responses import StreamingResponse @asynccontextmanager async def lifespan(app: FastAPI): directory = Path(os.environ["SCADA_HANDOFF_FIXTURE_DIR"]) pid = os.getpid() app.state.directory = directory app.state.version = (directory / "version").read_text().strip() mode = (directory / "startup-mode").read_text().strip() (directory / f"started-{pid}").write_text(app.state.version) if mode == "fail": raise RuntimeError("test candidate startup failed") if mode == "delay": await asyncio.sleep(0.3) while mode == "wait" and not (directory / "allow-startup").exists(): await asyncio.sleep(0.02) try: yield finally: (directory / f"stopped-{pid}").write_text(app.state.version) app = FastAPI(lifespan=lifespan) async def readiness_check() -> bool: directory = Path(os.environ["SCADA_HANDOFF_FIXTURE_DIR"]) ready = (directory / "readiness").read_text().strip() == "true" (directory / f"checked-{os.getpid()}").write_text(str(ready).lower()) await asyncio.sleep(.05) return ready @app.get("/health") async def health(): return {"pid": os.getpid(), "version": app.state.version} @app.get("/events") async def events(): async def stream(): directory = app.state.directory pid = os.getpid() while not (directory / f"release-{pid}").exists() and not (directory / "release-all").exists(): payload = {"pid": pid, "version": app.state.version, "time_ns": time.time_ns()} yield f"data: {json.dumps(payload)}\n\n" await asyncio.sleep(0.025) yield f"event: complete\ndata: {json.dumps({'pid': pid})}\n\n" return StreamingResponse(stream(), media_type="text/event-stream")