Offer a Continue link for ongoing games in the lobby
A player who accidentally closes the game page can now get back to it from the lobby. Backend: - GameStore.list_ongoing() returns live unfinished sessions (newest first), skipping expired keys and finished matches; - GET /api/me/games lists the games the caller is seated in, with the game name/description, players, open seats and your seat (no join code); - store, platform route and app integration tests. Frontend: - MyGame/MyGamesPage model plus api::my_games(); - the lobby shows a "Your games" card with phase, seats, players and a Continue button linking to /game/<id>; hidden when empty or logged out.
This commit is contained in:
9 files changed
+446
-4
No files matched your search
@@ -188,6 +188,63 @@ class GamesRouteTest(unittest.TestCase):
|
||||
response = await client.get("/api/games/does-not-exist")
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
@async_test
|
||||
async def test_my_games_lists_only_my_ongoing_games(self) -> None:
|
||||
app, platform, _ = make_platform()
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://127.0.0.1") as client:
|
||||
anonymous = await client.get("/api/me/games")
|
||||
self.assertEqual(401, anonymous.status_code)
|
||||
|
||||
with oidc_user(platform.oidc, "alice"):
|
||||
mine = await client.post(
|
||||
"/api/games", json={"options": {"target": 5}}
|
||||
)
|
||||
with oidc_user(platform.oidc, "bob"):
|
||||
theirs = await client.post("/api/games", json={})
|
||||
self.assertEqual(201, mine.status_code)
|
||||
self.assertEqual(201, theirs.status_code)
|
||||
|
||||
with oidc_user(platform.oidc, "alice"):
|
||||
listed = await client.get("/api/me/games")
|
||||
self.assertEqual(200, listed.status_code)
|
||||
results = listed.json()["results"]
|
||||
self.assertEqual([mine.json()["id"]], [g["id"] for g in results])
|
||||
entry = results[0]
|
||||
self.assertEqual("dummy", entry["game_type"])
|
||||
self.assertEqual("Dummy game", entry["game_name"])
|
||||
self.assertTrue(entry["game_description"])
|
||||
self.assertEqual("lobby", entry["phase"])
|
||||
self.assertEqual(5, entry["target"])
|
||||
self.assertEqual(1, entry["seats_open"])
|
||||
self.assertEqual(0, entry["your_seat"])
|
||||
self.assertNotIn("join_code", entry)
|
||||
self.assertEqual("alice", entry["players"][0]["sub"])
|
||||
|
||||
# Bob's own game is the only one he sees; Alice's is not his.
|
||||
with oidc_user(platform.oidc, "bob"):
|
||||
bobs = await client.get("/api/me/games")
|
||||
self.assertEqual(
|
||||
[theirs.json()["id"]],
|
||||
[g["id"] for g in bobs.json()["results"]],
|
||||
)
|
||||
|
||||
# Once Bob joins Alice's game it appears for both, now in the
|
||||
# playing phase.
|
||||
with oidc_user(platform.oidc, "bob"):
|
||||
await client.post(
|
||||
"/api/games/join", json={"code": mine.json()["join_code"]}
|
||||
)
|
||||
with oidc_user(platform.oidc, "bob"):
|
||||
bobs = await client.get("/api/me/games")
|
||||
by_id = {g["id"]: g for g in bobs.json()["results"]}
|
||||
self.assertEqual(
|
||||
{mine.json()["id"], theirs.json()["id"]}, set(by_id)
|
||||
)
|
||||
self.assertEqual("playing", by_id[mine.json()["id"]]["phase"])
|
||||
self.assertEqual(1, by_id[mine.json()["id"]]["your_seat"])
|
||||
self.assertEqual(0, by_id[mine.json()["id"]]["seats_open"])
|
||||
|
||||
|
||||
class GameTypesRouteTest(unittest.TestCase):
|
||||
@async_test
|
||||
|
||||
Reference in new issue
Block a user