| """Tests for the optional LLM answer layer. | |
| These run without an API key, so they exercise the extractive fallback — the | |
| same path CI takes. The live LLM path is covered by manual verification. | |
| """ | |
| import os | |
| from fastapi.testclient import TestClient | |
| from app import generate | |
| from app.main import app | |
| client = TestClient(app) | |
| def test_llm_unavailable_without_key(monkeypatch): | |
| monkeypatch.delenv("LLM_API_KEY", raising=False) | |
| assert generate.llm_available() is False | |
| assert generate.generate_answer("anything", ["a passage"]) is None | |
| def test_ask_falls_back_to_extractive_without_key(monkeypatch): | |
| monkeypatch.delenv("LLM_API_KEY", raising=False) | |
| text = "The Eiffel Tower is located in Paris, France." | |
| doc_id = client.post("/documents", data={"text": text}).json()["document_id"] | |
| response = client.post( | |
| "/ask", json={"document_id": doc_id, "question": "Where is the Eiffel Tower?"} | |
| ) | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["generated"] is False | |
| assert "Paris" in body["answer"] | |