# Scrabble Game — Testing How the project is tested and the gate every stage must pass. Read before adding tests or touching CI. ## Layers - **Go unit tests** — table-driven where it helps; `testing` + standard library. Every functional change ships with regression coverage. Run: `go test -count=1 ./backend/...` (the module list grows with the workspace). - **Integration** *(Stage 1+)* — Postgres-backed tests behind the `integration` build tag spin a throwaway `postgres:17-alpine` via `testcontainers-go`. They live in `backend/internal/inttest` and run with `go test -tags=integration -count=1 -p=1 ./backend/...` (needs Docker), guarded by a separate CI workflow (`integration.yaml`; Ryuk disabled, serial). Slow. - **UI** *(introduced with the UI in Stage 7)* — Vitest (unit) + Playwright (e2e), mirroring the chosen plain-Svelte + Vite toolchain. - **Engine** *(Stage 2+)* — correctness of scoring and move generation is owned by `scrabble-solver`'s own GCG-backed tests. `backend/internal/engine` adds, on top of the embedded solver: per-variant smoke tests (load all three committed DAWGs and validate a known word, including Эрудит), bag draw/return determinism and exchange accounting, the `Game` end-conditions (empty bag with an empty rack, and six scoreless turns) with end-game rack scoring, and **dictionary-independent history replay** (`ReplayBoard` reproduces a full greedy game's final board from decoded records alone). The engine tests read the DAWGs from `BACKEND_DICT_DIR` (or the sibling `scrabble-solver` checkout) and fail loudly when it is absent. The 24-hour timeout / auto-resign and robot balance/margin regression tests arrive with those stages. ## Principles - A green run must not depend on cached state: use `-count=1` in CI. - Tests that need infrastructure fail loudly (`t.Fatal`) when it is unavailable rather than silently skipping coverage. - No network or real platform calls in unit tests; validate platform credentials behind an interface seam and test with fixtures. ## Per-stage CI gate Every completed stage is exercised on `gitea.iliadenisov.ru` before it is marked done in [`../PLAN.md`](../PLAN.md): 1. Commit the stage on its `feature/*` branch. 2. Push to `origin`. 3. Watch the run to completion — never hand-roll a poll loop: `python3 ~/.claude/bin/gitea-ci-watch.py` (launch in the background). 4. Only after every workflow that fired is green may the stage be marked done.