import pytest from httpx import AsyncClient, ASGITransport @pytest.fixture def app(): from src.backend.main import app return app @pytest.fixture async def client(app): transport = ASGITransport(app=app) async with AsyncClient( transport=transport, base_url="http://test", headers={"X-Api-Key": "test-key-12345"}, ) as ac: yield ac @pytest.fixture async def client_no_auth(app): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as ac: yield ac @pytest.mark.asyncio async def test_request_without_api_key_returns_403(client_no_auth): resp = await client_no_auth.get("/api/health") assert resp.status_code == 403 @pytest.mark.asyncio async def test_request_with_wrong_api_key_returns_403(app): transport = ASGITransport(app=app) async with AsyncClient( transport=transport, base_url="http://test", headers={"X-Api-Key": "wrong-key"}, ) as ac: resp = await ac.get("/api/health") assert resp.status_code == 403 @pytest.mark.asyncio async def test_create_topic(client): resp = await client.post( "/api/topics", json={ "name": "coding", "system_prompt": "You are an expert coder.", "summary": "Coding topic", "model": "claude-sonnet-4-6", }, ) assert resp.status_code == 201 data = resp.json() assert data["success"] is True assert data["data"]["name"] == "coding" assert data["data"]["model"] == "claude-sonnet-4-6" assert "id" in data["data"] @pytest.mark.asyncio async def test_create_topic_minimal(client): resp = await client.post( "/api/topics", json={"name": "analysis", "system_prompt": "You analyze data."}, ) assert resp.status_code == 201 data = resp.json() assert data["data"]["summary"] == "" assert data["data"]["model"] is None @pytest.mark.asyncio async def test_create_topic_duplicate_name_fails(client): await client.post( "/api/topics", json={"name": "coding", "system_prompt": "Prompt 1"}, ) resp = await client.post( "/api/topics", json={"name": "coding", "system_prompt": "Prompt 2"}, ) assert resp.status_code == 409 @pytest.mark.asyncio async def test_list_topics(client): await client.post( "/api/topics", json={"name": "a", "system_prompt": "A"} ) await client.post( "/api/topics", json={"name": "b", "system_prompt": "B"} ) resp = await client.get("/api/topics") assert resp.status_code == 200 data = resp.json() assert len(data["data"]) == 2 @pytest.mark.asyncio async def test_get_topic(client): create_resp = await client.post( "/api/topics", json={"name": "coding", "system_prompt": "Code"} ) topic_id = create_resp.json()["data"]["id"] resp = await client.get(f"/api/topics/{topic_id}") assert resp.status_code == 200 assert resp.json()["data"]["name"] == "coding" @pytest.mark.asyncio async def test_get_topic_not_found(client): resp = await client.get("/api/topics/nonexistent-id") assert resp.status_code == 404 @pytest.mark.asyncio async def test_update_topic(client): create_resp = await client.post( "/api/topics", json={"name": "coding", "system_prompt": "Old"} ) topic_id = create_resp.json()["data"]["id"] resp = await client.put( f"/api/topics/{topic_id}", json={"system_prompt": "New prompt"}, ) assert resp.status_code == 200 assert resp.json()["data"]["system_prompt"] == "New prompt" assert resp.json()["data"]["name"] == "coding" @pytest.mark.asyncio async def test_delete_topic(client): create_resp = await client.post( "/api/topics", json={"name": "coding", "system_prompt": "Code"} ) topic_id = create_resp.json()["data"]["id"] resp = await client.delete(f"/api/topics/{topic_id}") assert resp.status_code == 200 resp = await client.get(f"/api/topics/{topic_id}") assert resp.status_code == 404 @pytest.mark.asyncio async def test_summarize_topic(client): create_resp = await client.post( "/api/topics", json={"name": "summarize-test", "system_prompt": "You help."}, ) topic_id = create_resp.json()["data"]["id"] from src.backend.db.database import get_db, create_request, complete_request db = await get_db() try: req1 = await create_request(db, "Question 1", topic_id) await complete_request(db, req1["id"], "Answer 1") req2 = await create_request(db, "Question 2", topic_id) await complete_request(db, req2["id"], "Answer 2") finally: await db.close() from unittest.mock import patch, AsyncMock, MagicMock mock_worker = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = { "success": True, "summary": "Comprehensive summary of all exchanges.", } mock_client = AsyncMock() mock_client.post.return_value = mock_response mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) with patch("src.backend.services.worker_client.httpx.AsyncClient", return_value=mock_client): resp = await client.post(f"/api/topics/{topic_id}/summarize") assert resp.status_code == 200 data = resp.json() assert data["success"] is True assert "summary" in data["data"] @pytest.mark.asyncio async def test_summarize_topic_no_requests(client): create_resp = await client.post( "/api/topics", json={"name": "empty-topic", "system_prompt": "Empty."}, ) topic_id = create_resp.json()["data"]["id"] resp = await client.post(f"/api/topics/{topic_id}/summarize") assert resp.status_code == 400