feat: implement news aggregator API with conventional commits
- Add FastAPI application with complete router structure - Implement search, articles, ask, feedback, and health endpoints - Add comprehensive Pydantic schemas for API requests/responses - Include stub service implementations for all business logic - Add full test suite with pytest-asyncio integration - Configure conventional commits enforcement via git hooks - Add project documentation and contribution guidelines - Support both OpenAI and Gemini LLM integration options
This commit is contained in:
64
apps/api/tests/test_main.py
Normal file
64
apps/api/tests/test_main.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import pytest
|
||||
import httpx
|
||||
|
||||
from news_api.main import create_app
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def client():
|
||||
app = create_app()
|
||||
async with httpx.AsyncClient(app=app, base_url="http://testserver") as async_client:
|
||||
yield async_client
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_healthz(client: httpx.AsyncClient) -> None:
|
||||
response = await client.get("/healthz")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "ok"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_defaults(client: httpx.AsyncClient) -> None:
|
||||
response = await client.get("/v1/search", params={"q": "energy"})
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["mode"] == "hybrid"
|
||||
assert payload["page"] == 1
|
||||
assert payload["results"], "Expected at least one search result"
|
||||
first = payload["results"][0]
|
||||
assert first["title"].startswith("Stubbed headline")
|
||||
assert first["badges"][0]["name"] == "Reuters"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_article_by_id(client: httpx.AsyncClient) -> None:
|
||||
article_id = "stub-article"
|
||||
response = await client.get(f"/v1/articles/{article_id}")
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["id"] == article_id
|
||||
assert payload["canonical_url"].startswith("https://www.reuters.com")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ask_endpoint_returns_citations(client: httpx.AsyncClient) -> None:
|
||||
response = await client.post("/v1/ask", json={"query": "What is the latest in energy?"})
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["answer"], "Expected sentences in answer"
|
||||
assert payload["citations"], "Expected citations"
|
||||
assert all(sentence["citations"] for sentence in payload["answer"])
|
||||
assert payload["conversation_id"], "Expected conversation id"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_feedback_endpoint_acknowledges(client: httpx.AsyncClient) -> None:
|
||||
response = await client.post(
|
||||
"/v1/feedback",
|
||||
json={"query": "test", "answer_id": "stub", "verdict": "up", "comment": "nice"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["status"] == "queued"
|
||||
assert "received_at" in payload
|
||||
Reference in New Issue
Block a user