Files
stack/tests/llm/test_api.py
kert edb72e9aba feat(llm): SSO-guarded RAG chat UI at llm.fhirworx.io (P34)
New llm FastAPI service (src/llm/api.py + rag.py + web/chat.html): grounded
streaming chat over indexed comments with cited sources. Own image, compose
service, Traefik reef entry with git-sso, llm subdomain registered. Dashboard
tile + README row. stack llm serve CLI.
2026-07-17 16:52:28 -04:00

66 lines
1.9 KiB
Python

"""llm.api — FastAPI chat app."""
from unittest.mock import patch
from fastapi.testclient import TestClient
from llm.api import app
client = TestClient(app)
class TestHealth:
def test_ok(self):
r = client.get("/health")
assert r.status_code == 200
assert r.json() == {"status": "ok"}
class TestIndex:
def test_serves_chat_page(self):
r = client.get("/")
assert r.status_code == 200
assert "text/html" in r.headers["content-type"]
assert "Comment Chat" in r.text
assert 'id="form"' in r.text
class TestWhoami:
def test_reads_forwarded_header(self):
r = client.get("/whoami", headers={"X-Auth-Request-User": "kert"})
assert r.json() == {"user": "kert"}
def test_empty_when_absent(self):
r = client.get("/whoami")
assert r.json() == {"user": ""}
class TestChat:
def test_empty_question_400(self):
r = client.post("/chat", json={"question": " "})
assert r.status_code == 400
@patch("llm.rag.stream_answer")
def test_streams_sse_events(self, mock_stream):
mock_stream.return_value = iter(
[
{"type": "token", "text": "Hi"},
{"type": "sources", "sources": []},
{"type": "done"},
]
)
r = client.post("/chat", json={"question": "hello"})
assert r.status_code == 200
assert "text/event-stream" in r.headers["content-type"]
body = r.text
assert 'data: {"type": "token", "text": "Hi"}' in body
assert '"type": "done"' in body
@patch("llm.rag.stream_answer")
def test_error_becomes_event_not_500(self, mock_stream):
mock_stream.side_effect = RuntimeError("ollama down")
r = client.post("/chat", json={"question": "hello"})
assert r.status_code == 200
assert '"type": "error"' in r.text
assert "ollama down" in r.text