from __future__ import annotations import asyncio from typing import List import httpx from fastapi import FastAPI from fastapi.testclient import TestClient from services.api import app as api_app from services.analytics import app as analytics_app from services.game import app as game_app def _install_asgi_clients() -> List[httpx.AsyncClient]: created: List[httpx.AsyncClient] = [] def make_client(app: FastAPI, base_url: str) -> httpx.AsyncClient: client = httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url=base_url) created.append(client) return client api_app.clients["game"] = make_client(game_app.app, "http://game") api_app.clients["analytics"] = make_client(analytics_app.app, "http://analytics") dummy_ai = FastAPI() @dummy_ai.get("/models") def _models(): return {"models": []} @dummy_ai.post("/act") def _act(): return {"action": {"type": "end_turn", "payload": {}}, "debug": {}} api_app.clients["ai"] = make_client(dummy_ai, "http://ai") return created def _close_clients(clients: List[httpx.AsyncClient]) -> None: for client in clients: try: loop = asyncio.get_event_loop() except RuntimeError: asyncio.run(client.aclose()) else: loop.run_until_complete(client.aclose()) def test_full_user_flow() -> None: with TestClient(api_app.app) as client: created = _install_asgi_clients() try: # Register + login response = client.post("/api/auth/register", json={"username": "user_flow", "password": "secret"}) assert response.status_code == 200 token = response.json()["token"] headers = {"Authorization": f"Bearer {token}"} # Create lobby and seat host response = client.post("/api/games", json={"name": "Flow", "max_players": 3}, headers=headers) assert response.status_code == 200 game_id = response.json()["id"] response = client.post(f"/api/games/{game_id}/join", headers=headers) assert response.status_code == 200 payload = response.json() assert payload["players"][0]["name"] == "user_flow" # Seat AI opponent and start the game for _ in range(2): response = client.post( f"/api/games/{game_id}/add-ai", json={"ai_type": "random"}, headers=headers, ) assert response.status_code == 200 response = client.post(f"/api/games/{game_id}/start", headers=headers) assert response.status_code == 200 state = response.json() assert state["status"] == "running" assert state["game"]["phase"] == "setup_round_one" assert len(state["legal_actions"]) > 0 finally: _close_clients(created)