feat: add decision API routes with tests
PUT /api/decisions/{listing_id} to set decision,
GET /api/decisions to list all user decisions,
DELETE /api/decisions/{listing_id} to remove a decision.
All 6 API route tests pass.
This commit is contained in:
parent
d350b806ba
commit
341de89004
3 changed files with 241 additions and 0 deletions
128
tests/unit/test_decision_routes.py
Normal file
128
tests/unit/test_decision_routes.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
"""Unit tests for decision API routes."""
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from sqlalchemy import Engine
|
||||
from sqlmodel import SQLModel, Session, create_engine
|
||||
|
||||
from models.user import User
|
||||
from models.decision import ListingDecision # noqa: F401 - needed for table creation
|
||||
from api.auth import get_current_user, User as AuthUser
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def decision_engine() -> Engine:
|
||||
engine = create_engine(
|
||||
"sqlite:///:memory:",
|
||||
echo=False,
|
||||
connect_args={"check_same_thread": False},
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
with Session(engine) as session:
|
||||
session.add(User(id=1, email="test@example.com"))
|
||||
session.commit()
|
||||
yield engine # type: ignore[misc]
|
||||
SQLModel.metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client(decision_engine: Engine) -> AsyncClient:
|
||||
import database
|
||||
import api.app as api_app
|
||||
import api.decision_routes as decision_routes_mod
|
||||
import api.poi_routes as poi_routes_mod
|
||||
|
||||
app = api_app.app
|
||||
mock_user = AuthUser(
|
||||
sub="test-user-id", email="test@example.com", name="Test User"
|
||||
)
|
||||
app.dependency_overrides[get_current_user] = lambda: mock_user
|
||||
|
||||
original_db = database.engine
|
||||
original_app = api_app.engine
|
||||
original_decision = decision_routes_mod.engine
|
||||
original_poi = poi_routes_mod.engine
|
||||
database.engine = decision_engine
|
||||
api_app.engine = decision_engine
|
||||
decision_routes_mod.engine = decision_engine
|
||||
poi_routes_mod.engine = decision_engine
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c # type: ignore[misc]
|
||||
|
||||
database.engine = original_db
|
||||
api_app.engine = original_app
|
||||
decision_routes_mod.engine = original_decision
|
||||
poi_routes_mod.engine = original_poi
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
class TestDecisionRoutes:
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_decision(self, client: AsyncClient) -> None:
|
||||
resp = await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "liked", "listing_type": "RENT"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["decision"] == "liked"
|
||||
assert data["listing_id"] == 100
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_decisions(self, client: AsyncClient) -> None:
|
||||
await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "liked", "listing_type": "RENT"},
|
||||
)
|
||||
resp = await client.get("/api/decisions")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert len(data) == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_decision(self, client: AsyncClient) -> None:
|
||||
await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "liked", "listing_type": "RENT"},
|
||||
)
|
||||
resp = await client.delete(
|
||||
"/api/decisions/100", params={"listing_type": "RENT"}
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["success"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_nonexistent_returns_404(
|
||||
self, client: AsyncClient
|
||||
) -> None:
|
||||
resp = await client.delete(
|
||||
"/api/decisions/999", params={"listing_type": "RENT"}
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_decision_returns_400(
|
||||
self, client: AsyncClient
|
||||
) -> None:
|
||||
resp = await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "maybe", "listing_type": "RENT"},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_decision(self, client: AsyncClient) -> None:
|
||||
await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "liked", "listing_type": "RENT"},
|
||||
)
|
||||
resp = await client.put(
|
||||
"/api/decisions/100",
|
||||
json={"decision": "disliked", "listing_type": "RENT"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["decision"] == "disliked"
|
||||
# Still only one decision
|
||||
resp2 = await client.get("/api/decisions")
|
||||
assert len(resp2.json()) == 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue