Files
Ilia Denisov ec435c0e7f
Tests · Go / test (push) Successful in 8s
Tests · Integration / integration (push) Successful in 11s
Tests · Go / test (pull_request) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 11s
Stage 14: solver & dictionary split — consume published module + DAWG artifact (TODO-1/TODO-2)
- backend/go.mod pins gitea.iliadenisov.ru/developer/scrabble-solver v1.0.0; the engine's
  imports use the published module path; go.work drops the solver replace (GOPRIVATE fetches
  it directly from Gitea). The solver's wordlist/dictdawg are now public packages.
- CI (go-unit, integration): drop the solver sibling-clone, set GOPRIVATE, and download the
  dictionary DAWG release artifact (scrabble-dawg-<DICT_VERSION>.tar.gz from the new
  scrabble-dictionary repo) for BACKEND_DICT_DIR.
- Docs: ARCHITECTURE §5/§11/§13/§14 + backend/README updated to the published-module +
  release-artifact model. PLAN.md re-scoped Stage 14 to the split and added Stages 15 (deploy
  infra & test contour), 16 (prod contour), 17 (dual Telegram bots); TODO-1/TODO-2 marked done.
2026-06-04 20:00:36 +02:00

101 lines
3.2 KiB
Go

package engine
import (
"errors"
"testing"
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
)
// TestRegistryOpensEveryVariant checks that Open loads all three variants at the
// requested version and reports them through Latest and Versions.
func TestRegistryOpensEveryVariant(t *testing.T) {
for _, v := range Variants() {
version, solver, err := testReg.Latest(v)
if err != nil {
t.Fatalf("latest %s: %v", v, err)
}
if version != testVersion {
t.Errorf("latest %s version = %q, want %q", v, version, testVersion)
}
if solver == nil {
t.Errorf("latest %s solver is nil", v)
}
if got := testReg.Versions(v); len(got) != 1 || got[0] != testVersion {
t.Errorf("versions %s = %v, want [%q]", v, got, testVersion)
}
}
}
// TestRegistryValidatesKnownWords is the per-variant smoke test: a known word
// laid over the centre validates against the loaded dictionary, including the
// Эрудит variant.
func TestRegistryValidatesKnownWords(t *testing.T) {
cases := []struct {
variant Variant
word string
}{
{VariantEnglish, "cat"},
{VariantRussianScrabble, "кот"},
{VariantErudit, "кот"},
}
for _, tc := range cases {
t.Run(tc.variant.String(), func(t *testing.T) {
solver, err := testReg.Solver(tc.variant, testVersion)
if err != nil {
t.Fatalf("solver: %v", err)
}
rs := solver.Rules()
row, col := centre(rs)
ps := placementsForWord(t, rs, row, col, scrabble.Horizontal, tc.word)
if _, err := solver.ValidatePlay(board.New(rs.Rows, rs.Cols), scrabble.Horizontal, ps); err != nil {
t.Fatalf("validate %q against %s: %v", tc.word, tc.variant, err)
}
})
}
}
// TestRegistryUnknownLookups covers the not-found error taxonomy.
func TestRegistryUnknownLookups(t *testing.T) {
reg, err := Open(testDictDir(), testVersion, VariantEnglish)
if err != nil {
t.Fatalf("open english-only registry: %v", err)
}
defer reg.Close()
if _, err := reg.Solver(VariantEnglish, "absent"); !errors.Is(err, ErrUnknownVersion) {
t.Errorf("solver with bad version: got %v, want ErrUnknownVersion", err)
}
if _, err := reg.Solver(VariantErudit, testVersion); !errors.Is(err, ErrUnknownVariant) {
t.Errorf("solver for unloaded variant: got %v, want ErrUnknownVariant", err)
}
if _, _, err := reg.Latest(VariantErudit); !errors.Is(err, ErrUnknownVariant) {
t.Errorf("latest for unloaded variant: got %v, want ErrUnknownVariant", err)
}
if got := reg.Versions(VariantErudit); got != nil {
t.Errorf("versions for unloaded variant = %v, want nil", got)
}
}
// TestRegistryCloseIdempotent verifies Close may be called more than once.
func TestRegistryCloseIdempotent(t *testing.T) {
reg, err := Open(testDictDir(), testVersion, VariantEnglish)
if err != nil {
t.Fatalf("open: %v", err)
}
if err := reg.Close(); err != nil {
t.Fatalf("first close: %v", err)
}
if err := reg.Close(); err != nil {
t.Fatalf("second close: %v", err)
}
}
// TestRegistryOpenMissingDir fails when a dictionary file is absent.
func TestRegistryOpenMissingDir(t *testing.T) {
if _, err := Open(t.TempDir(), testVersion, VariantEnglish); err == nil {
t.Fatal("expected an error opening a registry over an empty directory")
}
}