mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-23 14:15:23 +00:00
Handle matchmaking socket close codes per API contract (#4595)
## What Implements the close-code contract from the matchmaking API handoff (API PR #419) in the matchmaking modal, plus two runnable integration harnesses. Previously `onclose` only logged, so an API deploy while queued left the player on the "searching" spinner forever (the queue is in-memory on the API worker). | Close | Behavior | | --- | --- | | 1008 `Invalid session` | Reconnect and re-send `join` — `getPlayToken()` refreshes an expired token internally, so the rejoin carries a fresh JWT | | 1000 `Replaced by newer connection` | Another tab/window took the queue slot: show a message (new `matchmaking_modal.replaced` string) and stop. No retry | | Any other close before assignment | Server restart/deploy: reconnect and re-send `join`, with exponential backoff (1s doubling to a 15s cap) | Intentional closes (user backs out of the modal, assignment received) don't reconnect. A pending 2s join timer from a previous socket is cleared before each reconnect so it can't fire on the new socket. ## Test harnesses (`tests/matchmaking/`) - **`npm run test:matchmaking`** (contained): drives the real modal in a headless browser against a fake in-process matchmaking server speaking the documented protocol; covers the whole close-code table over real WebSockets. Needs only `npm run dev`. - **`npm run test:matchmaking:e2e`**: real integration against the API worker on `localhost:8787` — two browser players join the real queue, the dev game server receives the checkin assignment, and both clients end up in the same created game. ## Why now This is required by the matchmaking API's client contract independent of the upcoming 2v2 work ("clients must already handle unexpected close → reconnect and rejoin"). The rest of the 2v2 integration is planned for the next version. ## Verification - Contained harness: 8/8 checks pass (join, 1006 reconnect+rejoin, 1008 rejoin with fresh token, assignment, replaced → message + no retry, intentional close → no retry/no message). - E2E harness against a local worker: 3/3 — both players matched into the same game, game created via checkin and joinable by both. - `npm test` (all 2,046 pass), `npx tsc --noEmit`, ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Matchmaking integration harnesses
|
||||
|
||||
Two harnesses for the matchmaking client integration (the `matchmaking-modal`
|
||||
close-code/reconnect contract from the matchmaking API handoff). Neither runs
|
||||
as part of `npm test` — they drive a real headless browser.
|
||||
|
||||
## Contained — `npm run test:matchmaking`
|
||||
|
||||
Integration test against a **fake matchmaking server** (`fakeServer.mjs`, an
|
||||
in-process `ws` server speaking the documented protocol). The browser's
|
||||
`WebSocket` for `/matchmaking/join` is redirected to it, so real close-code
|
||||
semantics apply. Covers:
|
||||
|
||||
| Scenario | Expected client behavior |
|
||||
| ----------------------------------- | ------------------------------- |
|
||||
| join after connect | `{type:"join", jwt}` sent |
|
||||
| abrupt drop (deploy/restart, 1006) | reconnect + rejoin (backoff) |
|
||||
| 1008 `Invalid session` | reconnect + rejoin, fresh token |
|
||||
| `match-assignment` | gameId recorded, socket done |
|
||||
| 1000 `Replaced by newer connection` | message shown, **no** retry |
|
||||
| intentional close (user backs out) | no retry, no message |
|
||||
|
||||
Prerequisite: `npm run dev` (app on :9000). No API worker needed.
|
||||
|
||||
## E2E — `npm run test:matchmaking:e2e`
|
||||
|
||||
Real integration against the **API worker on `localhost:8787`**. Two browser
|
||||
players join the real queue through the real modal; the dev game server's
|
||||
`/matchmaking/checkin` long-poll receives the assignment and creates the
|
||||
game; the test asserts both players get the same `gameId` and dispatch
|
||||
`join-lobby` once the game exists.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- `npm run dev` (app + game server on :9000; the game server polls checkin
|
||||
on :8787 out of the box in dev)
|
||||
- the API worker running locally: `wrangler dev` in the API repo (port 8787)
|
||||
|
||||
On failure the harness dumps both players' browser consoles — close code
|
||||
1008 means the worker rejected the play token; no assignment usually means
|
||||
the worker rejected the game server's `x-api-key` checkin.
|
||||
Reference in New Issue
Block a user