| """Round-trip test: launch FastAPI in TestClient, drive it via OpenSOCClient. |
| |
| The client is HTTP-only and must not import server internals; this test |
| patches `requests` to route to the FastAPI TestClient so we can verify |
| the client without spinning up a real socket. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import os |
| import sys |
| from typing import Any, Dict |
|
|
| import pytest |
|
|
| sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) |
|
|
| from app_runtime import _envs, app |
| from client import OpenSOCClient |
|
|
|
|
| class _TestClientSession: |
| """Adapter that gives `requests.Session` shape to a FastAPI TestClient.""" |
|
|
| def __init__(self): |
| from fastapi.testclient import TestClient |
| self.tc = TestClient(app) |
|
|
| def get(self, url: str, params: Dict[str, Any] | None = None, timeout: float | None = None): |
| path = url.split("//", 1)[-1] |
| path = "/" + path.split("/", 1)[1] if "/" in path else "/" |
| return self.tc.get(path, params=params) |
|
|
| def post(self, url: str, params: Dict[str, Any] | None = None, json: Any = None, timeout: float | None = None): |
| path = url.split("//", 1)[-1] |
| path = "/" + path.split("/", 1)[1] if "/" in path else "/" |
| return self.tc.post(path, params=params, json=json) |
|
|
|
|
| @pytest.fixture() |
| def client(): |
| _envs.clear() |
| return OpenSOCClient(base_url="http://test", session=_TestClientSession()) |
|
|
|
|
| class TestClient: |
| def test_health(self, client): |
| h = client.health() |
| assert h["status"] == "ok" |
|
|
| def test_tasks(self, client): |
| t = client.tasks() |
| assert len(t["tasks"]) == 4 |
|
|
| def test_round_trip(self, client): |
| obs = client.reset(task="stage1_basic", mode="defender_only", seed=3) |
| assert obs["role"] == "defender" |
| first_log_id = obs["log_window"][0]["log_id"] |
| result = client.step( |
| {"submit_triage": { |
| "action": "monitor", |
| "cited_log_id": first_log_id, |
| "rationale": "client test", |
| }}, |
| task="stage1_basic", mode="defender_only", seed=3, |
| ) |
| assert result["done"] is True |
| grade = client.grade(task="stage1_basic", mode="defender_only", seed=3) |
| assert 0.0 <= grade["score"] <= 1.0 |
|
|