911 / tests /test_openenv_integration.py
garvitsachdeva's picture
Submission polish: compliance hardening, baseline matrix, dashboard UX, tests, and docs
775befb
"""Tests for OpenEnv server/client integration (dispatch domain)."""
from __future__ import annotations
import asyncio
import pytest
from fastapi.testclient import TestClient
import src.server.app as server_app
from src.models import Action, DispatchAction
from src.openenv_environment import OpenEnvEnvironment
@pytest.fixture(autouse=True)
def reset_env() -> None:
server_app._env = None
yield
server_app._env = None
class TestOpenEnvEnvironment:
def test_reset_and_state(self) -> None:
env = OpenEnvEnvironment(task_id="single_incident", seed=42)
obs = asyncio.run(env.reset())
assert obs.result == "dispatch center online"
assert obs.protocol_ok is True
st = env.state()
assert st.task_id == "single_incident"
assert st.step_count == 0
def test_step_returns_tuple(self) -> None:
env = OpenEnvEnvironment(task_id="single_incident", seed=42)
asyncio.run(env.reset())
action = Action(
action_type=DispatchAction.DISPATCH,
unit_id="MED-1",
incident_id="INC-001",
)
obs, reward, done = asyncio.run(env.step(action))
assert isinstance(obs.result, str)
assert isinstance(reward, float)
assert isinstance(done, bool)
env.close()
class TestResetEndpoint:
def test_reset_returns_observation(self) -> None:
c = TestClient(server_app.app)
response = c.post("/reset", json={"task_id": "single_incident", "seed": 42})
assert response.status_code == 200
data = response.json()
assert data["result"] == "dispatch center online"
assert data["protocol_ok"] is True
def test_reset_with_empty_body_returns_200(self) -> None:
"""Verify prevalidation.sh compatible: POST /reset with {} returns 200."""
c = TestClient(server_app.app)
response = c.post("/reset", json={})
assert response.status_code == 200
data = response.json()
assert data["result"] == "dispatch center online"
class TestStepEndpoint:
def test_step_requires_reset_first(self) -> None:
c = TestClient(server_app.app)
response = c.post(
"/step",
json={
"action": {
"action_type": "DISPATCH",
"unit_id": "MED-1",
"incident_id": "INC-001",
}
},
)
assert response.status_code == 500
assert "not initialized" in response.json()["detail"].lower()
def test_step_invalid_action_rejected(self) -> None:
c = TestClient(server_app.app)
c.post("/reset", json={"task_id": "single_incident", "seed": 42})
response = c.post("/step", json={"action": {"invalid": "field"}})
assert response.status_code == 500
assert "invalid action" in response.json()["detail"].lower()
def test_step_ok(self) -> None:
c = TestClient(server_app.app)
c.post("/reset", json={"task_id": "single_incident", "seed": 42})
response = c.post(
"/step",
json={
"action": {
"action_type": "DISPATCH",
"unit_id": "MED-1",
"incident_id": "INC-001",
}
},
)
assert response.status_code == 200
data = response.json()
assert set(data.keys()) == {"observation", "reward", "done"}
class TestStateEndpoint:
def test_state_requires_reset_first(self) -> None:
c = TestClient(server_app.app)
response = c.get("/state")
assert response.status_code == 500
def test_state_returns_current_state(self) -> None:
c = TestClient(server_app.app)
c.post("/reset", json={"task_id": "single_incident", "seed": 42})
response = c.get("/state")
assert response.status_code == 200
data = response.json()
assert data["task_id"] == "single_incident"
assert data["step_count"] == 0
class TestHealthEndpoint:
def test_health_ok(self) -> None:
c = TestClient(server_app.app)
response = c.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "healthy"}
class TestTasksEndpoint:
def test_tasks_endpoint_returns_four_tasks(self) -> None:
c = TestClient(server_app.app)
response = c.get("/tasks")
assert response.status_code == 200
tasks = response.json()
assert len(tasks) == 4
task_ids = {t["task_id"] for t in tasks}
assert task_ids == {
"single_incident",
"multi_incident",
"mass_casualty",
"shift_surge",
}
class TestDashboardEndpoint:
def test_dashboard_state_before_reset_returns_valid_shape(self) -> None:
c = TestClient(server_app.app)
response = c.get("/dashboard/state")
assert response.status_code == 200
data = response.json()
assert data["task_id"] == "none"
assert data["step_count"] == 0
assert isinstance(data["units"], dict)
assert isinstance(data["incidents"], dict)
assert isinstance(data["legal_actions"], list)
assert isinstance(data["issues"], list)
assert data["observation"] is None
def test_dashboard_state_after_reset_exposes_legal_actions(self) -> None:
c = TestClient(server_app.app)
reset_response = c.post("/reset", json={"task_id": "single_incident", "seed": 42})
assert reset_response.status_code == 200
response = c.get("/dashboard/state")
assert response.status_code == 200
data = response.json()
assert data["task_id"] == "single_incident"
assert isinstance(data["legal_actions"], list)
assert data["observation"] is not None