import json from pathlib import Path from fastapi.testclient import TestClient import app.main as main import app.npc as npc client = TestClient(main.app) VALID = json.loads((Path(__file__).parent / "fixtures" / "canon_log_valid.json").read_text()) def _body(**over): b = {"canon_log": VALID, "npc_id": "fenn", "disposition": 0, "available_moves": ["refuse"], "utterance": "hello"} b.update(over) return b def test_valid_request_returns_prose(monkeypatch): monkeypatch.setattr(main, "npc_run", lambda **k: "Aye, well met.") r = client.post("/npc/speak", json=_body()) assert r.status_code == 200 assert r.json() == {"prose": "Aye, well met."} def test_unknown_npc_maps_to_422(monkeypatch): def boom(**k): raise npc.UnknownNpc("nobody") monkeypatch.setattr(main, "npc_run", boom) r = client.post("/npc/speak", json=_body(npc_id="nobody")) assert r.status_code == 422 assert "canon_log_errors" in r.json()["detail"] def test_model_error_maps_to_502(monkeypatch): from app.ollama_client import ModelError def boom(**k): raise ModelError("down") monkeypatch.setattr(main, "npc_run", boom) r = client.post("/npc/speak", json=_body()) assert r.status_code == 502 assert "model_error" in r.json()["detail"] def test_invalid_log_still_422(): bad = json.loads(json.dumps(VALID)) bad["player"]["luck"] = 5 # §7 leak — schema rejects r = client.post("/npc/speak", json=_body(canon_log=bad)) assert r.status_code == 422 assert "canon_log_errors" in r.json()["detail"] def test_missing_field_is_422(): b = _body() del b["utterance"] r = client.post("/npc/speak", json=b) assert r.status_code == 422