Files
dreamdict/cmd/server/bench_test.go
prosolis 207086da08 Add Phase 2+3 features: antonyms, backing, pronunciation, etymology, difficulty, affix expansion
New endpoints: /antonyms, /backing, /pronunciation, /etymology with difficulty
scoring on /random. Cross-language synset backing links French/Portuguese words
to English equivalents via WordNet 3.0 synset IDs (matching WOLF and OMW offsets).

New loaders: Hunspell affix expansion (fr, pt-PT), English programmatic inflector,
CMU Pronouncing Dictionary, SUBTLEX-US frequency, CETEMPúblico frequency,
Open Multilingual Wordnet (Portuguese), and SQL-based difficulty scoring.

Schema v2 adds tables: antonyms, synsets, word_synsets, pronunciations, etymology
with migration support for existing databases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 01:01:06 -07:00

109 lines
2.9 KiB
Go

package main
import (
"database/sql"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"dreamdict/internal/dictionary"
_ "modernc.org/sqlite"
)
func setupBenchServer(b *testing.B) *http.ServeMux {
b.Helper()
db, err := sql.Open("sqlite", ":memory:?_pragma=foreign_keys(on)")
if err != nil {
b.Fatal(err)
}
db.SetMaxOpenConns(1)
if err := dictionary.BootstrapSchema(db); err != nil {
b.Fatal(err)
}
exec := func(q string, args ...any) {
if _, err := db.Exec(q, args...); err != nil {
b.Fatalf("seed: %s: %v", q, err)
}
}
exec("INSERT INTO meta (key, value) VALUES ('schema_version', '1')")
exec("INSERT INTO meta (key, value) VALUES ('imported_at', '2025-01-01T00:00:00Z')")
poses := []string{"noun", "verb", "adjective", "adverb"}
for i := 1; i <= 500; i++ {
pos := poses[i%len(poses)]
word := fmt.Sprintf("word%04d", i)
exec("INSERT INTO words (id, word, lang, pos, frequency) VALUES (?, ?, 'en', ?, ?)",
i, word, pos, i%100)
exec("INSERT INTO definitions (word_id, pos, gloss, source, priority) VALUES (?, ?, ?, 'wordnet', 10)",
i, pos, fmt.Sprintf("def of %s", word))
exec("INSERT INTO synonyms (word_id, synonym, source) VALUES (?, ?, 'wordnet')",
i, fmt.Sprintf("syn_%s", word))
exec("INSERT INTO translations (word_id, translation, target_lang, source) VALUES (?, ?, 'fr', 'wiktionary')",
i, fmt.Sprintf("fr_%s", word))
}
dict := dictionary.NewFromDB(db)
b.Cleanup(func() { db.Close() })
mux := http.NewServeMux()
mux.HandleFunc("GET /valid", handleValid(dict))
mux.HandleFunc("GET /random", handleRandom(dict))
mux.HandleFunc("GET /define", handleDefine(dict))
mux.HandleFunc("GET /synonyms", handleSynonyms(dict))
mux.HandleFunc("GET /translate", handleTranslate(dict))
mux.HandleFunc("GET /health", handleHealth(dict, ":memory:"))
return mux
}
func benchRequest(b *testing.B, mux *http.ServeMux, path string) {
b.Helper()
req := httptest.NewRequest("GET", path, nil)
b.ResetTimer()
for b.Loop() {
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != 200 {
b.Fatalf("status %d for %s", w.Code, path)
}
}
}
func BenchmarkHTTPValid(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/valid?word=word0003&lang=en")
}
func BenchmarkHTTPRandom(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/random?lang=en")
}
func BenchmarkHTTPRandomFiltered(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/random?lang=en&pos=noun&min=5&max=10")
}
func BenchmarkHTTPDefine(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/define?word=word0003&lang=en")
}
func BenchmarkHTTPSynonyms(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/synonyms?word=word0003&lang=en")
}
func BenchmarkHTTPTranslate(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/translate?word=word0003&from=en&to=fr")
}
func BenchmarkHTTPHealth(b *testing.B) {
mux := setupBenchServer(b)
benchRequest(b, mux, "/health")
}