| from fastapi.testclient import TestClient |
|
|
| from app import app |
| from telltale.server import api |
|
|
|
|
| client = TestClient(app) |
|
|
|
|
| def setup_function(): |
| api.reset_sessions() |
|
|
|
|
| def test_react_index_is_player_facing_shell(): |
| response = client.get("/") |
|
|
| assert response.status_code == 200 |
| assert "id=\"root\"" in response.text |
| assert "gradio-container" not in response.text |
|
|
|
|
| def test_root_public_assets_are_served_before_spa_fallback(): |
| soundtrack = client.get("/dream.mp3") |
|
|
| assert soundtrack.status_code == 200 |
| assert soundtrack.headers["content-type"].startswith("audio/") |
|
|
|
|
| def test_floors_endpoint_returns_backend_floor_definitions(): |
| response = client.get("/api/floors") |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["total_floors"] == 5 |
| assert len(payload["floors"]) == 5 |
| assert payload["floors"][0]["name"] == "Level 1 (Tutorial)" |
| assert payload["floors"][0]["buy_in"] == 40 |
| assert payload["floors"][4]["is_boss"] is True |
|
|
|
|
| def test_start_endpoint_returns_public_state(): |
| response = client.post("/api/start", json={"seed": 321, "model_mode": "mock"}) |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["run_id"] |
| assert payload["floor"]["floor_number"] == 1 |
| assert payload["legal_actions"] |
|
|
|
|
| def test_start_endpoint_uses_configured_model_mode_when_omitted(): |
| response = client.post("/api/start", json={"seed": 3210}) |
|
|
| assert response.status_code == 200 |
| assert response.json()["model"]["runtime_backend"] == "mock" |
|
|
|
|
| def test_start_endpoint_accepts_voice_flags(): |
| response = client.post( |
| "/api/start", |
| json={"seed": 3211, "model_mode": "mock", "tts_enabled": True, "stt_enabled": True}, |
| ) |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["voice"] == {"tts_enabled": True, "stt_enabled": True} |
|
|
|
|
| def test_invalid_run_id_uses_error_envelope(): |
| response = client.get("/api/state/missing-run") |
|
|
| assert response.status_code == 404 |
| assert response.json()["error"]["code"] == "not_found" |
|
|
|
|
| def test_illegal_action_uses_error_envelope(): |
| started = client.post("/api/start", json={"seed": 322, "model_mode": "mock"}).json() |
| illegal = "check" if "check" not in started["legal_actions"] else "fold" |
|
|
| response = client.post( |
| "/api/action", |
| json={"run_id": started["run_id"], "action": illegal, "amount": 0, "utterance": ""}, |
| ) |
|
|
| assert response.status_code == 400 |
| assert response.json()["error"]["code"] == "illegal_action" |
|
|
|
|
| def test_legal_action_returns_event_batch(): |
| started = client.post("/api/start", json={"seed": 323, "model_mode": "mock"}).json() |
|
|
| response = client.post( |
| "/api/action", |
| json={ |
| "run_id": started["run_id"], |
| "action": started["legal_actions"][0], |
| "amount": 0, |
| "utterance": "The room feels priced in.", |
| }, |
| ) |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["run_id"] == started["run_id"] |
| assert payload["events"] |
| assert payload["public_state"]["run_id"] == started["run_id"] |
|
|
|
|
| def test_continue_endpoint_returns_event_batch(): |
| started = client.post("/api/start", json={"seed": 324, "model_mode": "mock"}).json() |
|
|
| response = client.post("/api/continue", json={"run_id": started["run_id"]}) |
|
|
| assert response.status_code == 200 |
| assert response.json()["public_state"]["run_id"] == started["run_id"] |
|
|
|
|
| def test_reward_endpoint_rejects_when_no_reward_pending(): |
| started = client.post("/api/start", json={"seed": 325, "model_mode": "mock"}).json() |
|
|
| response = client.post("/api/reward", json={"run_id": started["run_id"], "perk_id": "not-a-perk"}) |
|
|
| assert response.status_code == 400 |
| assert response.json()["error"]["code"] == "bad_request" |
|
|
|
|
| def test_trace_export_endpoint_returns_trace_payload(): |
| started = client.post("/api/start", json={"seed": 326, "model_mode": "mock"}).json() |
| client.post( |
| "/api/action", |
| json={"run_id": started["run_id"], "action": started["legal_actions"][0], "amount": 0, "utterance": ""}, |
| ) |
|
|
| response = client.get(f"/api/trace/{started['run_id']}") |
|
|
| assert response.status_code == 200 |
| assert response.json()["run_id"] == started["run_id"] |
|
|
|
|
| def test_transcribe_endpoint_returns_structured_disabled_result(): |
| started = client.post("/api/start", json={"seed": 327, "model_mode": "mock"}).json() |
|
|
| response = client.post( |
| f"/api/transcribe/{started['run_id']}", |
| content=b"audio", |
| headers={"Content-Type": "audio/webm"}, |
| ) |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["text"] == "" |
| assert payload["disabled"] is True |
| assert payload["error"] == "speech input is disabled" |
|
|
|
|
| def test_audio_endpoint_rejects_path_traversal(): |
| response = client.get("/api/audio/%5Csecret.wav") |
|
|
| assert response.status_code == 400 |
| assert response.json()["error"]["code"] == "bad_request" |
|
|