Manages, runs, extends, and troubleshoots the Power Chess backend test suite (Go unit tests and WebSocket integration tests). Use when adding tests, fixing failing tests, checking coverage, or asked about the testing strategy, test structure, or test commands.
# All Go tests (unit + integration)
go test ./...
# Coverage profile
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
Before any commit: run go test ./... first. Only commit when all tests pass.
| Package | File | What it covers |
|---|---|---|
internal/chess | engine_coverage_test.go | IsStalemate, ApplyPseudoLegalMove, IsSquareAttacked edge cases |
internal/gameplay | turn_coverage_test.go | GrantCaptureBonusMana, ConsumeCardFromHand, EndTurn, StartTurn, SelectPlayerSkill, , |
tickCooldownsEnterMulliganPhaseWithoutShuffleinternal/match | engine_coverage_test.go | EndTurn, ActivatePlayerSkill, PendingEffects, ReactionWindowSnapshot, EffectResolver implementations |
internal/server | ws_integration_test.go | Original WebSocket integration tests |
internal/server | ws_handlers_test.go | New handler tests: confirm_mulligan, submit_move, ignite_card, draw_card, leave_match, debug_match_fixture |
The key helpers in ws_handlers_test.go:
// Start a test server and dial two players
srv := newTestServer(t)
cA, _ := dialAndHello(t, srv) // clears read deadline after hello
cB, _ := dialAndHello(t, srv)
// Join both players to the same room
joinRoom(t, cA, "room-1", "")
joinRoom(t, cB, "room-1", "")
// Progress through mulligan
confirmMulliganBoth(t, cA, cB)
// Drain messages until a specific type (up to maxMsg messages)
env, found := drainUntilType(t, cA, MessageStateSnapshot, 20)
gorilla/websocket v1.5.3 permanently marks a connection as broken once a read deadline fires — even on a clean timeout with no partial frame. Rules:
dialAndHello clears the read deadline after the hello (c.SetReadDeadline(time.Time{}))drainUntilType(t, conn, targetType, 20) with a large maxMsg to skip buffered snapshotsdrainRemainingSnapshots (removed) or any helper that sets short deadlines then moves onWhen using debug_match_fixture in tests, card IDs must be from DefaultDeckPresetCardIDs():
"energy-gain", "knight-touch", "bishop-touch", "rook-touch", "backstab", "extinguish", "clairvoyance", "save-it-for-later", "retaliate", "counterattack", "sacrifice-of-the-masses", "mana-burn""double-turn", "stop-right-there", etc.The server must be started with ADMIN_DEBUG_MATCH=1 to accept debug_match_fixture messages.
The join_match idempotency key includes the client's connID (random per connection):
roomID|join_match|connID|envID
All other handlers use c.requestKey(env) which includes c.playerID. This prevents clients that share the same envelope counter from colliding.
Run coverage after adding new tests:
go test ./... -coverprofile=coverage.out && go tool cover -func=coverage.out | tail -5
Focus new tests on handlers and resolvers in:
internal/server/ws.go — handler functionsinternal/match/resolvers.go — Apply and RequiresTarget implementationsinternal/gameplay/state.go — turn/mana helpers| Symptom | Likely cause | Fix |
|---|---|---|
i/o timeout in WS integration test | Read deadline not cleared after dialAndHello | c.SetReadDeadline(time.Time{}) after hello |
duplicate_request ack for second player join | Old code missing connID in dedup key | Fixed in handleJoinMatch (connID scoping) |
#gameShell stays hidden in E2E | Server returned duplicate for join_match | See dedup fix above |
deck_lookup_failed in debug fixture | Card ID not in default preset | Use only cards from DefaultDeckPresetCardIDs() |