- 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.
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
"""Browser <-> backend pair: real Chrome (CDP) against the real backend HTTP server.
|
|
|
|
Requires Node and a Chrome instance exposing the DevTools protocol on
|
|
127.0.0.1:9444 (see frontend/README.md). Skips otherwise.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from tests.integration.conftest import REPO_ROOT
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
CHROME_PORT = int(os.getenv("CW_CHROME_CDP_PORT", "9444"))
|
|
|
|
|
|
def chrome_available() -> bool:
|
|
try:
|
|
with urllib.request.urlopen(f"http://127.0.0.1:{CHROME_PORT}/json/version", timeout=2) as r:
|
|
return r.status == 200
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def free_port() -> int:
|
|
with socket.socket() as s:
|
|
s.bind(("127.0.0.1", 0))
|
|
return s.getsockname()[1]
|
|
|
|
|
|
@pytest.mark.skipif(shutil.which("node") is None or not chrome_available(), reason="node and Chrome CDP on 9444 required")
|
|
def test_browser_against_real_backend(tmp_path: Path):
|
|
port = free_port()
|
|
control = tmp_path / "scenario"
|
|
control.write_text("standard\n")
|
|
env = {
|
|
"PATH": os.environ["PATH"],
|
|
"CW_PEER_CONTROL_FILE": str(control),
|
|
"CW_ARTIFACT_DIR": str(tmp_path / "artifacts"),
|
|
"CW_DEV_PORT": str(port),
|
|
"PYTHONPATH": str(REPO_ROOT),
|
|
}
|
|
server = subprocess.Popen([sys.executable, str(REPO_ROOT / "tests/integration/dev_server.py")], env=env, cwd=REPO_ROOT)
|
|
try:
|
|
deadline = time.time() + 30
|
|
while time.time() < deadline:
|
|
try:
|
|
with urllib.request.urlopen(f"http://127.0.0.1:{port}/", timeout=1) as r:
|
|
if r.status == 200:
|
|
break
|
|
except Exception:
|
|
time.sleep(0.2)
|
|
else:
|
|
raise AssertionError("dev server did not start")
|
|
res = subprocess.run(
|
|
["node", str(REPO_ROOT / "tests/integration/browser_backend.mjs"), "--app", f"http://127.0.0.1:{port}", "--control", str(control), "--chrome", str(CHROME_PORT)],
|
|
capture_output=True, text=True, timeout=300, cwd=REPO_ROOT,
|
|
)
|
|
print(res.stdout)
|
|
print(res.stderr, file=sys.stderr)
|
|
assert res.returncode == 0, res.stdout + res.stderr
|
|
finally:
|
|
server.terminate()
|
|
try:
|
|
server.wait(timeout=10)
|
|
except subprocess.TimeoutExpired:
|
|
server.kill()
|