from fastapi.testclient import TestClient from config import Settings from main import create_app from rag.answer import GroundedAnswerService from rag.models import EvidenceDecision, QueryIntent, RetrievalResult, SubjectScope class FixedRouting: def retrieve(self, query, subject_scope, intent): assert query == "Liều thuốc?" assert subject_scope == SubjectScope.HUMAN assert intent == QueryIntent.FACT_LOOKUP return RetrievalResult(EvidenceDecision.ABSTAIN, "drug_not_resolved") class MemoryTraceWriter: def __init__(self): self.rows = [] def save(self, **fields): self.rows.append(fields) return "trace-1" def test_health_and_fail_closed_rag_response_are_traced(): traces = MemoryTraceWriter() app = create_app( settings=Settings(), answer_service=GroundedAnswerService(FixedRouting()), trace_writer=traces, ) client = TestClient(app) assert client.get("/health").json() == {"status": "ok"} response = client.post("/v1/rag/query", json={ "query": "Liều thuốc?", "subject_scope": "human", "intent": "fact_lookup", }) assert response.status_code == 200 assert response.json()["decision"] == "abstain" assert response.json()["trace_id"] == "trace-1" assert traces.rows[0]["reason"] == "drug_not_resolved" def test_query_requires_structured_scope_and_intent(): app = create_app( settings=Settings(), answer_service=GroundedAnswerService(FixedRouting()), trace_writer=MemoryTraceWriter(), ) response = TestClient(app).post("/v1/rag/query", json={"query": "Liều?"}) assert response.status_code == 422