multi-agent-system / tests /integration /test_architecture_flow.py
firepenguindisopanda
Refactor test documentation and improve consistency in comments
e382248
Raw
History Blame Contribute Delete
7.87 kB
"""
Integration tests for the architecture session flow.
Tests: create → generate → compare → select → refine.
Uses real NVIDIA LLM API calls.
**Why fixtures rather than test ordering.** These tests previously stashed the
session id on the `pytest` module in `test_create_session` and read it back with
`getattr(pytest, "arch_session_id", None)`, skipping when it was absent. A failed
create therefore turned the remaining six tests into silent skips, and the suite
still reported green. The shared state now lives in module-scoped fixtures: if
create or generate fails, every dependent test ERRORs loudly instead.
Generation costs several minutes of live LLM time, so `generated_options` runs
once per module and is shared by compare / select / refine.
"""
import json
import pytest
from tests.integration.conftest import _require_env, require_ok
def _parse_sse(body: str) -> tuple[list[dict], list[str]]:
"""Return (option payloads, event types) parsed from an SSE response body.
The generate stream emits `{"type": "agent"|"option"|"done"|"error"}` frames;
see `architecture_generation.generate_options_stream`.
"""
options: list[dict] = []
types: list[str] = []
for line in body.splitlines():
if not line.startswith("data: "):
continue
event = json.loads(line[len("data: ") :])
types.append(event.get("type"))
if event.get("type") == "option":
options.append(event["option"])
return options, types
@pytest.fixture(scope="module")
def created_session(live_client, auth_headers) -> dict:
"""Create one architecture session shared by every test in this module."""
response = live_client.post(
"/architecture/sessions",
json={
"project_name": "E-Commerce Platform",
"requirements": (
"A scalable e-commerce platform with product catalog, shopping cart, "
"payment processing, and order management. Must support 10k concurrent users."
),
"constraints": "Budget: $500/month cloud costs. Team: 3 developers. Timeline: 3 months MVP.",
},
headers=auth_headers,
)
require_ok(response, "POST /architecture/sessions")
return response.json()
@pytest.fixture(scope="module")
def generated_options(live_client, auth_headers, created_session) -> list[dict]:
"""Generate architecture options once. Several minutes of live LLM time."""
_require_env("NVIDIA_API_KEY")
session_id = created_session["id"]
response = live_client.post(
f"/architecture/sessions/{session_id}/generate",
json={"num_options": 3},
headers=auth_headers,
)
require_ok(response, f"POST /architecture/sessions/{session_id}/generate")
options, types = _parse_sse(response.text)
assert "error" not in types, f"generate emitted an error event: {response.text[:800]}"
assert options, f"generate produced no option events: {response.text[:800]}"
assert "done" in types, f"generate never emitted a done event: {response.text[-800:]}"
return options
class TestArchitectureSession:
"""Test architecture session lifecycle with live LLM."""
def test_create_session(self, created_session):
"""POST /architecture/sessions creates a new session."""
assert created_session["id"]
assert created_session["project_name"] == "E-Commerce Platform"
assert "10k concurrent users" in created_session["requirements"]
def test_get_session(self, live_client, auth_headers, created_session):
"""GET /architecture/sessions/{id} retrieves session."""
session_id = created_session["id"]
response = live_client.get(
f"/architecture/sessions/{session_id}",
headers=auth_headers,
)
require_ok(response, f"GET /architecture/sessions/{session_id}")
data = response.json()
assert data["id"] == session_id
assert data["requirements"] == created_session["requirements"]
def test_list_sessions(self, live_client, auth_headers, created_session):
"""GET /architecture/sessions lists all sessions."""
response = live_client.get("/architecture/sessions", headers=auth_headers)
require_ok(response, "GET /architecture/sessions")
data = response.json()
assert isinstance(data, list)
assert created_session["id"] in [s["id"] for s in data], (
"the session created by this module is missing from the listing"
)
def test_generate_options(self, generated_options):
"""POST /architecture/sessions/{id}/generate creates options via SSE.
The fixture does the work and asserts the stream shape; this test pins
the contract callers depend on - real options, each with an id and name.
"""
assert len(generated_options) >= 1
for option in generated_options:
assert option.get("id"), f"option is missing an id: {option}"
assert option.get("name"), f"option is missing a name: {option}"
def test_compare_options(self, live_client, auth_headers, created_session, generated_options):
"""POST /architecture/sessions/{id}/compare compares options."""
_require_env("NVIDIA_API_KEY")
session_id = created_session["id"]
response = live_client.post(
f"/architecture/sessions/{session_id}/compare",
headers=auth_headers,
)
require_ok(response, f"POST /architecture/sessions/{session_id}/compare")
data = response.json()
assert "recommendation" in data, f"comparison has no recommendation: {data}"
def test_select_option(self, live_client, auth_headers, created_session, generated_options):
"""POST /architecture/sessions/{id}/select selects an option.
Selects an id that actually came out of generate. The old version sent a
hardcoded "opt_1" and accepted `status_code in (200, 400, 404, 500)` -
an exhaustive set, so it could not fail.
"""
session_id = created_session["id"]
option_id = generated_options[0]["id"]
response = live_client.post(
f"/architecture/sessions/{session_id}/select",
json={"option_id": option_id},
headers=auth_headers,
)
require_ok(response, f"POST /architecture/sessions/{session_id}/select")
data = response.json()
assert data["selected_option"]["id"] == option_id
assert data["session_id"] == session_id
def test_select_unknown_option_is_404(self, live_client, auth_headers, created_session, generated_options):
"""Selecting an option that does not exist is rejected, not accepted."""
session_id = created_session["id"]
response = live_client.post(
f"/architecture/sessions/{session_id}/select",
json={"option_id": "opt_does_not_exist"},
headers=auth_headers,
)
assert response.status_code == 404, response.text
def test_refine_option(self, live_client, auth_headers, created_session, generated_options):
"""POST /architecture/sessions/{id}/refine refines an option."""
_require_env("NVIDIA_API_KEY")
session_id = created_session["id"]
option_id = generated_options[0]["id"]
response = live_client.post(
f"/architecture/sessions/{session_id}/refine",
json={
"target_option_id": option_id,
"feedback": "Make it more cost-effective and reduce operational complexity.",
},
headers=auth_headers,
)
require_ok(response, f"POST /architecture/sessions/{session_id}/refine")
data = response.json()
assert data["refined_option"]["id"] == option_id
assert data["iteration"] >= 1