from collections.abc import Sequence from zbt.commands.serve import run_backend_supervisor class FakeProcess: def __init__(self, return_code: int | None) -> None: self._return_code = return_code self.terminated = False self.killed = False def poll(self) -> int | None: return self._return_code def terminate(self) -> None: self.terminated = True self._return_code = 0 def wait(self, timeout: float | None = None) -> int: del timeout return 0 if self._return_code is None else self._return_code def kill(self) -> None: self.killed = True self._return_code = -9 def test_unified_backend_starts_api_and_worker_and_stops_peer_on_exit() -> None: api = FakeProcess(return_code=0) worker = FakeProcess(return_code=None) launched_roles: list[str] = [] def launch(role: str, command: Sequence[str]) -> FakeProcess: assert command launched_roles.append(role) return api if role == "api" else worker result = run_backend_supervisor( launch=launch, api_command=("python", "-m", "uvicorn", "zbt.main:app"), worker_command=("python", "-m", "zbt.commands.run_refund_worker"), output=lambda _: None, poll_interval_seconds=0, ) assert result == 0 assert launched_roles == ["api", "refund-worker"] assert worker.terminated is True assert worker.killed is False def test_unified_backend_fails_when_refund_worker_exits_unexpectedly() -> None: api = FakeProcess(return_code=None) worker = FakeProcess(return_code=0) def launch(role: str, command: Sequence[str]) -> FakeProcess: del command return api if role == "api" else worker result = run_backend_supervisor( launch=launch, api_command=("api",), worker_command=("worker",), output=lambda _: None, poll_interval_seconds=0, ) assert result == 1 assert api.terminated is True