from pydantic import BaseModel import pytest from fastapi.testclient import TestClient from app.errors import ConfigurationError from app.main import app, get_analysis_service from app.ports import LLm from app.repositories import InMemoryTranscriptAnalysisRepository from app.services import TranscriptAnalysisService class FakeLLM(LLm): def run_completion( self, system_prompt: str, user_prompt: str, dto: type[BaseModel], ) -> BaseModel: return dto(summary="API summary.", action_items=["Confirm owner", "Set deadline"]) async def run_completion_async( self, system_prompt: str, user_prompt: str, dto: type[BaseModel], ) -> BaseModel: return dto(summary="API summary.", action_items=["Confirm owner", "Set deadline"]) @pytest.fixture def client() -> TestClient: service = TranscriptAnalysisService(FakeLLM(), InMemoryTranscriptAnalysisRepository()) app.dependency_overrides[get_analysis_service] = lambda: service with TestClient(app) as test_client: yield test_client app.dependency_overrides.clear() def test_analyze_transcript_success(client: TestClient) -> None: response = client.get("/analyses", params={"transcript": "Discuss rollout plan."}) assert response.status_code == 200 payload = response.json() assert payload["id"] assert payload["summary"] == "API summary." assert payload["action_items"] == ["Confirm owner", "Set deadline"] def test_analyze_transcript_rejects_empty_query(client: TestClient) -> None: response = client.get("/analyses", params={"transcript": " "}) assert response.status_code == 400 assert response.json()["detail"] == "Transcript cannot be empty." def test_get_transcript_analysis_success(client: TestClient) -> None: created = client.get("/analyses", params={"transcript": "Discuss rollout plan."}).json() response = client.get(f"/analyses/{created['id']}") assert response.status_code == 200 assert response.json() == created def test_get_transcript_analysis_returns_404(client: TestClient) -> None: response = client.get("/analyses/missing-id") assert response.status_code == 404 assert response.json()["detail"] == "Transcript analysis 'missing-id' was not found." def test_analyze_batch_success(client: TestClient) -> None: response = client.post( "/analyses/batch", json={"transcripts": ["Discuss roadmap.", "Review hiring plan."]}, ) assert response.status_code == 200 payload = response.json() assert len(payload["items"]) == 2 assert all(item["summary"] == "API summary." for item in payload["items"]) def test_analyze_batch_rejects_empty_list(client: TestClient) -> None: response = client.post("/analyses/batch", json={"transcripts": []}) assert response.status_code == 400 assert response.json()["detail"] == "At least one transcript is required." def test_analyze_batch_rejects_empty_transcript(client: TestClient) -> None: response = client.post("/analyses/batch", json={"transcripts": ["Discuss roadmap.", " "]}) assert response.status_code == 400 assert response.json()["detail"] == "Transcript cannot be empty." def test_configuration_error_returns_503() -> None: def raise_configuration_error() -> TranscriptAnalysisService: raise ConfigurationError( "OPENAI_API_KEY is not configured. Set it in .env or as an environment variable." ) app.dependency_overrides[get_analysis_service] = raise_configuration_error try: with TestClient(app) as test_client: response = test_client.get("/analyses", params={"transcript": "Discuss rollout plan."}) finally: app.dependency_overrides.clear() assert response.status_code == 503 assert response.json()["detail"] == ( "OPENAI_API_KEY is not configured. Set it in .env or as an environment variable." ) def test_gradio_ui_is_mounted(client: TestClient) -> None: response = client.get("/", follow_redirects=True) assert response.status_code == 200 assert "text/html" in response.headers["content-type"]