Add regional variant tagging (us/gb) for English words

- SCOWL loader now loads american-words and british-words alongside
  english-words, tagging each with variant "us", "gb", or NULL (common)
- Words appearing in both American and British lists get NULL variant
- Add variant column to words table with schema migration
- API: /define returns variant field for English, /random and /words
  accept ?variant=us|gb filter
- Add total word count to server status line
- Fix rows.Close leak in PopulateCMUTails, add rows.Err() check
- Add tests for PopulateCMUTails, cmuTail, TotalWordCount
- Expand test seed data and assertions to cover pt-PT definitions,
  synonyms, and validity checks
- Update README with variant docs, /difficulty, /words, /rhyme
  endpoints, and current database statistics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-04-04 15:21:27 -07:00
parent 28ec11ba4e
commit 704c06f41d
8 changed files with 488 additions and 71 deletions

View File

@@ -49,6 +49,7 @@ func setupTestServer(t *testing.T) (*dictionary.Dictionary, *http.ServeMux) {
mux.HandleFunc("GET /define", handleDefine(dict))
mux.HandleFunc("GET /synonyms", handleSynonyms(dict))
mux.HandleFunc("GET /translate", handleTranslate(dict))
mux.HandleFunc("GET /words", handleWords(dict))
mux.HandleFunc("GET /health", handleHealth(dict, ":memory:"))
t.Cleanup(func() { db.Close() })
@@ -201,6 +202,55 @@ func TestTranslateEndpoint(t *testing.T) {
}
}
func TestWordsEndpoint(t *testing.T) {
_, mux := setupTestServer(t)
srv := httptest.NewServer(mux)
defer srv.Close()
// All English words
resp, _ := http.Get(srv.URL + "/words?lang=en")
if resp.StatusCode != 200 {
t.Fatalf("status = %d, want 200", resp.StatusCode)
}
var result struct {
Lang string `json:"lang"`
Count int `json:"count"`
Words []string `json:"words"`
}
json.NewDecoder(resp.Body).Decode(&result)
resp.Body.Close()
if result.Lang != "en" {
t.Errorf("lang = %q, want en", result.Lang)
}
if result.Count != len(result.Words) {
t.Errorf("count %d != len(words) %d", result.Count, len(result.Words))
}
if result.Count != 2 { // happy, cat
t.Errorf("count = %d, want 2", result.Count)
}
// With length filter — no match
resp, _ = http.Get(srv.URL + "/words?lang=en&min=100")
if resp.StatusCode != 404 {
t.Errorf("no match: status = %d, want 404", resp.StatusCode)
}
resp.Body.Close()
// Missing lang
resp, _ = http.Get(srv.URL + "/words")
if resp.StatusCode != 400 {
t.Errorf("missing lang: status = %d, want 400", resp.StatusCode)
}
resp.Body.Close()
// Bad lang
resp, _ = http.Get(srv.URL + "/words?lang=xx")
if resp.StatusCode != 400 {
t.Errorf("bad lang: status = %d, want 400", resp.StatusCode)
}
resp.Body.Close()
}
func TestHealthEndpoint(t *testing.T) {
_, mux := setupTestServer(t)
srv := httptest.NewServer(mux)