Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| 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() | |
| 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 | |