Files
scrabble-game/backend/internal/engine/abort_test.go
T
Ilia Denisov 222eaf730f
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 46s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m13s
feat(game): void unreplayable games as a draw instead of erroring
A committed move that becomes illegal under a tightened rule (the single-word
connectivity fix) makes engine replay fail, which left such games unopenable —
an empty screen and an 'illegal play' error. Now the first open closes the game
gracefully as a draw (engine.EndAborted -> end_reason 'aborted', no winner),
preserves the journal, and surfaces an impersonal organizer note at the end of
the move history and in the GCG export.

- engine: EndAborted + Abort() (draw, no rack adjustment; winner -1).
- service: replay aborts on ErrIllegalPlay; liveGame persists the void once
  (lazy, on open); GameState re-reads for the settled view.
- store: VoidGame finishes the game and stamps a draw without a journal row.
- migration 00002: allow end_reason 'aborted'.
- ui: organizer note under the history grid; i18n en/ru.
- docs: ARCHITECTURE 6/9.1, FUNCTIONAL(+ru), PRERELEASE MW3.
2026-06-14 16:57:27 +02:00

32 lines
1.0 KiB
Go

package engine
import "testing"
// TestAbortFinishesAsDrawWithoutAdjustment covers Abort, the graceful close used when a
// committed game can no longer be reconstructed from its journal. The game ends as a draw
// (no winner) regardless of the running scores, and the scores are left untouched (no
// end-game rack adjustment).
func TestAbortFinishesAsDrawWithoutAdjustment(t *testing.T) {
g, err := New(testReg, Options{Variant: VariantEnglish, Version: testVersion, Players: 2, Seed: 1})
if err != nil {
t.Fatalf("new game: %v", err)
}
g.scores[0], g.scores[1] = 10, 5 // a clear leader, so a draw cannot come from equal scores
g.Abort()
if !g.Over() {
t.Error("aborted game should be over")
}
if g.Reason() != EndAborted {
t.Errorf("reason = %v, want EndAborted", g.Reason())
}
res := g.Result()
if res.Winner != -1 {
t.Errorf("winner = %d, want -1 (draw)", res.Winner)
}
if res.Scores[0] != 10 || res.Scores[1] != 5 {
t.Errorf("scores = %v, want [10 5] (no rack adjustment on abort)", res.Scores)
}
}