"""Shared helpers for tests that activate as Backend lands implementations. The Backend agent ships Phase-1 stubs that raise :class:`NotImplementedError` (pure functions) or route handlers that raise ``UpstreamUnavailableError`` with a ``"Phase N: ... not implemented yet"`` internal detail. The real test bodies below are written against the SPEC contract now, but must not *fail* the suite while the implementation is a stub (project rule: ``pytest`` MUST exit 0). These helpers convert a "not implemented yet" signal into ``pytest.skip`` so a test self-activates the moment Backend fills in the body, without any edit to the test file. Keep this module import-light. """ from __future__ import annotations from collections.abc import Callable import pytest def call_or_skip[T](fn: Callable[..., T], *args: object, **kwargs: object) -> T: """Call ``fn``; if it is still a Phase-1 stub, skip instead of failing. A stub is detected by ``NotImplementedError`` being raised. Once Backend implements the function the call returns normally and the assertions in the test run for real. """ try: return fn(*args, **kwargs) except NotImplementedError as exc: # pragma: no cover - skip path pytest.skip(f"pending Backend implementation: {fn.__qualname__} ({exc})") def skip_if_stub_route(status_code: int, body: object) -> None: """Skip when an integration response is the Phase-1 'not implemented' stub. The scaffold's route handlers raise ``UpstreamUnavailableError`` (HTTP 502, ``error.code == 'upstream_unavailable'``) with an internal detail of ``'Phase N: ... not implemented yet'``. The internal detail is *not* sent to the client (errors are sanitized), so we recognise the stub by the generic 502 shape produced before any real proxy logic exists. Real implementations return 2xx/4xx for these tests, so this never masks a genuine regression. """ if status_code == 502 and isinstance(body, dict): err = body.get("error") if isinstance(err, dict) and err.get("code") == "upstream_unavailable": pytest.skip("pending Backend implementation: route still returns 502 stub")