"""API and heuristic tests for trending discovery sort. docs/planning/discovery.md (task 33706f401ad9). """ from datetime import datetime, timedelta, timezone import pytest from fastapi.testclient import TestClient from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.pool import NullPool from app.config import settings from app.main import app from app.models.world_visit import WorldVisit def _register(client: TestClient, username: str) -> tuple[int, dict[str, str]]: resp = client.post( "/api/auth/register", json={ "username": username, "email": f"{username}@example.com", "password": "secret123", "date_of_birth": "1990-01-01", }, ) assert resp.status_code == 201, resp.text token = resp.json()["access_token"] headers = {"Authorization": f"Bearer {token}"} me = client.get("/api/users/me", headers=headers) assert me.status_code == 200, me.text return me.json()["id"], headers def _session_factory(): engine = create_async_engine(settings.DATABASE_URL, poolclass=NullPool) return engine, async_sessionmaker(engine, expire_on_commit=False) @pytest.mark.asyncio async def test_trending_sort_heuristic_and_zero_visits(): client = TestClient(app) uid, headers = _register(client, "creator_trending_1") # Create 3 worlds wA = client.post( "/api/worlds/", headers=headers, json={"name": "Trending Hot World", "visibility": "public", "category": "Action"}, ).json() wB = client.post( "/api/worlds/", headers=headers, json={"name": "Older Mild World", "visibility": "public", "category": "Action"}, ).json() wC = client.post( "/api/worlds/", headers=headers, json={"name": "Zero Visit World", "visibility": "public", "category": "Action"}, ).json() engine, sf = _session_factory() now = datetime.now(timezone.utc) try: async with sf() as session: # World A: 3 visits within last 2 hours (weight 3 each = score 9) for i in range(3): session.add( WorldVisit( world_id=wA["id"], anonymous_token=f"hot_visitor_{i}", visited_at=now - timedelta(hours=1, minutes=i), ) ) # World B: 1 visit 5 days ago (weight 1 = score 1) session.add( WorldVisit( world_id=wB["id"], anonymous_token="old_visitor", visited_at=now - timedelta(days=5), ) ) # World C: zero visits await session.commit() finally: await engine.dispose() # Query with sort=trending resp = client.get("/api/discovery/worlds?sort=trending&category=Action") assert resp.status_code == 200, resp.text items = resp.json()["items"] # Verify order: wA should be first, wB second, wC third ids = [item["id"] for item in items] assert wA["id"] in ids assert wB["id"] in ids assert wC["id"] in ids idxA = ids.index(wA["id"]) idxB = ids.index(wB["id"]) idxC = ids.index(wC["id"]) assert idxA < idxB < idxC, f"Expected order A ({idxA}) < B ({idxB}) < C ({idxC})"