- 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.
59 lines
2.3 KiB
Python
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"
|