Skip to content

feat(games): open and join games - #200

Open
Utzig26 wants to merge 1 commit into
feat/chess-engine-adapterfrom
feat/game-lifecycle
Open

feat(games): open and join games#200
Utzig26 wants to merge 1 commit into
feat/chess-engine-adapterfrom
feat/game-lifecycle

Conversation

@Utzig26

@Utzig26 Utzig26 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Implements RFC-002. Brings back the module that carried the reason for the API to exist.

Stacked on #199, which is stacked on #198. Retarget as those merge.

What lands

Route Effect
🔒 POST /games Opens a game in WP, creator takes one colour
🔒 POST /games/:id/join Takes the open colour, game moves to W

The state machine (WP → W → A → F), the game schema, the readable id, and the serializer that will be shared with RFC-006 and RFC-007.

Playing yourself is supported, not tolerated

The creator may join their own game, hold both colours, and later move each side in turn. In the old code this worked by omission — nobody checked who was joining, and wichPlayer() already returned 'wb' for the case without anything documenting it. Here it is in the Swagger description and covered by a test.

The unique index decides the id

The old loop queried the collection until it found a free resourceId. This generates one, tries to insert, and lets the unique index reject it, retrying up to five times before giving up with a 500.

Same lesson as the duplicate username in #198: the index decides, and it decides atomically.

The player id never leaves the service

Responses carry only the username, which is what the old code achieved with a @Transform on serialisation. Two end-to-end tests assert that no twenty-four character hex string appears in any response body.

Other corrections carried over from the legacy notes

The initial FEN is complete (... w KQkq - 0 1). The old schema defaulted to the piece placement alone, so a client trusting it before the first move parsed an invalid string.

Spelling in the public contract is fixed rather than inherited: additionalInfo, and "Waiting for the players to connect".

Two defects the tests caught

Both are recorded in the RFC, because they will bite again in RFC-003 and RFC-005.

@ValidateNested() does not require presence. A body with no timeControl passed validation and crashed the service on destructuring. It needs @IsObject() alongside it. Caught by rejects a missing time control.

A mongoose field cannot be required and default to '' at the same time, since mongoose reads the empty string as missing. A new game failed its own schema validation on pgn.

The engine boundary caught the test suite

The no-restricted-imports rule added in #199 fired on test/games.e2e-spec.ts, which had imported chess.js to assert the FEN parses. The import was dropped rather than the rule relaxed — the exact string assertion already proves the FEN is complete, and chess-engine.service.spec already proves it parses.

Verification

181 tests green, clean strict typecheck and lint.

  • 99 unit, 17 new: colour chosen and drawn both ways, id collisions retried and eventually given up on, an error that is not a collision not swallowed, and every state that refuses a join
  • 82 end-to-end, 24 new: the envelope, an eight case time control matrix, unknown fields, self-play, the second join conflict, and the id leak

Implements RFC-002. Brings back the module that carried the reason for the
API to exist, with the state machine, the readable id and the two routes
that put players on a board.

POST /games opens a game in WP with the creator on the colour they asked
for, or on one drawn at random. POST /games/:id/join takes the open colour
and moves the game to W. Playing yourself is supported rather than tolerated:
the creator may join their own game, hold both colours, and later move each
side in turn. It is documented in Swagger and covered by a test.

The readable id keeps the shape the old code used, but inverts the control.
The old loop queried the collection until it found a free id; this generates
one, tries to insert, and lets the unique index reject it, retrying up to
five times. Same lesson as the duplicate username: the index decides, and it
decides atomically.

The player id never leaves the service. Responses carry only the username,
which is what the old code achieved with a Transform on serialisation, and
two end-to-end tests assert no twenty-four character hex string appears in
any response body.

The initial FEN is complete now. The old schema defaulted to the piece
placement alone, so a client trusting it before the first move parsed an
invalid string.

Spelling in the public contract is fixed rather than inherited:
additionalInfo, and "Waiting for the players to connect".

Two defects the tests caught, both recorded in the RFC because they will
bite again:

ValidateNested does not require presence. A body with no timeControl passed
validation and crashed the service on destructuring. It needs IsObject
alongside it.

A mongoose field cannot be required and default to an empty string at the
same time, since mongoose reads the empty string as missing. A new game
failed its own schema validation on pgn.

17 unit tests over colour selection, id collisions and every state that
refuses a join, and 24 end-to-end covering the envelope, the time control
matrix, self-play and the id leak.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant