confluence_web/tests/integration/test_error_mapping.py
Artur Mukhamadiev d77b52ae86 integration: contract checks, error mapping, deployment entry points
- tests/integration: frontend served by backend, runtime error mapping,
  backend + real pi image with scripted model/Confluence (shared example,
  variants, failure paths, cancellation/busy gate, isolation canaries,
  HTTP download ownership, 16 MiB prompt round trip), real OpenAI-compatible
  adapter + real image over a scripted transport, real Chrome against the
  real backend with a scripted runtime peer, backend crash/restart
  reconciliation, and an opt-in live model check (marker: live).
- backend: map runtime terminal codes (model_output_limit,
  model_context_exceeded, query_timeout, connectivity_failed) to the
  contract's HTTP statuses; make the artifact 404 body identical for
  no-session, wrong-session, unknown and expired IDs.
- Makefile, scripts/run-backend.sh, deploy/confluence-web.env.example,
  root README for the integrated application; integration pytest marker.
2026-09-14 22:20:34 +03:00

59 lines
2.3 KiB
Python

"""Runtime terminal error codes map to the contract's HTTP statuses (no Docker)."""
from __future__ import annotations
from pathlib import Path
import httpx
import pytest
from httpx import ASGITransport
from backend.app import create_app
from backend.artifacts import ArtifactStore
from backend.containers import FakeContainerManager
from backend.dev.fake_peer import ScriptedContainerPeer
from backend.model import FakeModelAdapter
from backend.settings import Settings
from tests.backend.conftest import make_test_confluence_client_factory
pytestmark = pytest.mark.integration
ORIGIN = "http://testserver"
def error_peer(code: str):
async def steps(reader, writer, peer):
start = await peer._read_line(reader)
assert start["type"] == "start"
await peer._write_msg(writer, {"v": 1, "type": "error", "id": "a_err", "payload": {"code": code, "message": "RUNTIME-DETAIL-MUST-NOT-LEAK"}})
return lambda: ScriptedContainerPeer(custom_steps=steps)
@pytest.mark.parametrize("code,status,expected", [
("model_output_limit", 502, "model_output_limit"),
("model_context_exceeded", 400, "model_context_exceeded"),
("query_timeout", 504, "query_timeout"),
("connectivity_failed", 502, "connectivity_failed"),
("execution_failed", 500, "execution_failed"),
("invalid_input", 500, "execution_failed"),
("something_unknown", 500, "execution_failed"),
])
@pytest.mark.asyncio
async def test_runtime_error_code_mapping(tmp_path: Path, code, status, expected):
settings = Settings(query_timeout_seconds=20.0, artifact_storage_dir=tmp_path / "a")
app = create_app(
settings=settings,
container_manager=FakeContainerManager(error_peer(code)),
artifact_store=ArtifactStore(settings.artifact_storage_dir),
model_adapter=FakeModelAdapter(),
confluence_client_factory=make_test_confluence_client_factory(),
)
async with httpx.AsyncClient(transport=ASGITransport(app=app), base_url=ORIGIN) as c:
r = await c.post("/api/v1/query", json={"prompt": "x", "credentials": {"url": "https://approved.example.com", "pat": "valid-pat"}}, headers={"Origin": ORIGIN})
assert r.status_code == status
body = r.json()
assert body["error"]["code"] == expected
assert "RUNTIME-DETAIL" not in r.text
assert r.headers["cache-control"] == "no-store"