Every template and handler message in internal/web goes through a JSON
catalogue under internal/web/locales/<code>/, with English as the fallback.
The language is the person's choice (a cookie set by /_lang/{code}), else
Accept-Language, else landing.language. Tests refuse a key some language
lacks, a translation with other arguments than English, and template text
left in English.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
151 lines
6.3 KiB
Go
151 lines
6.3 KiB
Go
package web
|
||
|
||
import (
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"net/url"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog"
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/hf"
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/mirror"
|
||
)
|
||
|
||
// mirrorInTurkish asks the mirror page for path as somebody who chose
|
||
// Turkish, with a form when there is one.
|
||
func mirrorInTurkish(t *testing.T, mux *http.ServeMux, path string, form url.Values) string {
|
||
t.Helper()
|
||
method, body := http.MethodGet, ""
|
||
if form != nil {
|
||
method, body = http.MethodPost, form.Encode()
|
||
}
|
||
r := httptest.NewRequest(method, path, strings.NewReader(body))
|
||
if form != nil {
|
||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||
}
|
||
r.AddCookie(&http.Cookie{Name: SessionCookie, Value: "good"})
|
||
r.AddCookie(&http.Cookie{Name: csrfCookie, Value: "tok"})
|
||
r.AddCookie(&http.Cookie{Name: langCookie, Value: "tr"})
|
||
w := httptest.NewRecorder()
|
||
mux.ServeHTTP(w, r)
|
||
return w.Body.String()
|
||
}
|
||
|
||
func wantAll(t *testing.T, what, body string, want ...string) {
|
||
t.Helper()
|
||
for _, s := range want {
|
||
if !strings.Contains(body, s) {
|
||
t.Errorf("%s lacks %q", what, s)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestTheMirrorPageSpeaksTurkishWhenAsked(t *testing.T) {
|
||
m := &fakeMirror{models: []mirror.Status{
|
||
{Model: "qwen", Version: "2026-09", File: "model.gguf", State: mirror.Present,
|
||
BytesDone: 491400032, BytesTotal: 491400032},
|
||
{Model: "llama", Version: "v1", File: "l.gguf", State: mirror.Fetching,
|
||
BytesDone: 1 << 20, BytesTotal: 4 << 20},
|
||
}}
|
||
reg := &fakeRegistry{results: []hf.Model{{ID: "Qwen/Qwen3-8B-GGUF", Downloads: 1247,
|
||
CreatedAt: time.Now().Add(-3 * time.Hour)}}}
|
||
mux := mirrorPageWith(t, m, reg, true)
|
||
|
||
held := mirrorInTurkish(t, mux, "/admin/mirror", nil)
|
||
wantAll(t, "the held tab", held,
|
||
`<html lang="tr">`, "<h1>Model aynası</h1>", ">Aynadakiler <span class=\"count\">1 · 1 iniyor</span>",
|
||
"Hugging Face'i keşfet", "URL ile indir",
|
||
"468,6 MiB", // the size, written the Turkish way
|
||
"4,0 MiB içinden %25",
|
||
"adresi kopyala", "sunulmuyor",
|
||
`data-confirm="qwen/2026-09 ve içindeki tüm dosyalar kaldırılsın mı?"`,
|
||
`"kopyalandı"`, // the copy button's script speaks it too
|
||
)
|
||
for _, english := range []string{"Model mirror", "copy address", "not served", "so far"} {
|
||
if strings.Contains(held, english) {
|
||
t.Errorf("the Turkish page still says %q", english)
|
||
}
|
||
}
|
||
|
||
explore := mirrorInTurkish(t, mux, "/admin/mirror?tab=explore", nil)
|
||
wantAll(t, "the explorer", explore,
|
||
"Gündemde", "En çok indirilen", "Her boyut", ">Ara</button>",
|
||
"<th>Depo</th>", "1,2 bin", ">3 sa<", `"Yükleniyor…"`)
|
||
|
||
fetch := mirrorInTurkish(t, mux, "/admin/mirror?tab=fetch", nil)
|
||
wantAll(t, "the fetch tab", fetch, "Kaynak URL", "İndirmeyi başlat", "Yine de indir")
|
||
|
||
// What a change answers is in the same language as the page it came from.
|
||
done := mirrorInTurkish(t, mux, "/admin/mirror/fetch", url.Values{"csrf": {"tok"},
|
||
"source": {"http://x/a.gguf", "http://x/b.gguf"}, "model": {"qwen"}, "version": {"2026-10"}})
|
||
wantAll(t, "the fetch's answer", done, "qwen/2026-10 indirilmeye başlandı (2 dosya)")
|
||
refused := mirrorInTurkish(t, mux, "/admin/mirror/fetch", url.Values{"csrf": {"tok"}, "tab": {"fetch"}})
|
||
wantAll(t, "the refusal", refused, "bir kaynak URL gerekli")
|
||
}
|
||
|
||
func TestAHeldModelsCardSpeaksTurkish(t *testing.T) {
|
||
fleet := &roomyFleet{more: 2, blocked: []string{"n2: 2 GB free of 32, profile needs 6"}}
|
||
cat := coderCatalog()
|
||
mux := mirrorPageDeps(t, MirrorDeps{Mirror: &fakeMirror{models: []mirror.Status{
|
||
{Model: "coder", Version: "v1", File: "c.gguf", State: mirror.Present, BytesDone: 5 << 30},
|
||
}}, Catalog: cat, Fleet: fleet}, true)
|
||
|
||
card := mirrorInTurkish(t, mux, "/admin/mirror?tab=held&open=coder:v1", nil)
|
||
wantAll(t, "the card", card,
|
||
"Şu anda 2 tane daha için yer var: n1, n1.", ">hazır</span>", "1sa01dk", "3,5 çekirdek",
|
||
"Sürekli çalışan", "varsayılan (standart)", "Kaydet", "LoRA adaptörleri")
|
||
|
||
msg := mirrorInTurkish(t, mux, "/admin/mirror/replicas", url.Values{"csrf": {"tok"},
|
||
"model": {"coder"}, "min": {"2"}, "max": {"4"}, "open": {"coder:v1"}})
|
||
wantAll(t, "the change's answer", msg,
|
||
"coder 2 replikayla, yük altında en çok 4 replikayla çalışıyor; şu anda 3 tanesine yer var")
|
||
}
|
||
|
||
func TestServingPlansAreDescribedInTurkish(t *testing.T) {
|
||
tr := langByCode("tr")
|
||
form := url.Values{
|
||
"name": {"m"}, "source": {"http://x/m"}, "engine": {"llamacpp"}, "replicas": {"1"},
|
||
"context_length": {"4096"}, "ctx_size": {"16384"}, "parallel": {"4"},
|
||
"placement": {"gpu:t4"}, "gpus": {"4"}, "min_memory_gb": {"8"},
|
||
}
|
||
m, err := buildModel(tr, form.Get)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if got, want := describeServe(tr, m), "llama.cpp, tek düğümün 4 adet t4 GPU'su üzerinde, "+
|
||
"8 GB ana makine belleği, 16.384 token bağlam havuzu, 4 paralel isteğe bölünmüş (her birine 4.096)"; got != want {
|
||
t.Errorf("got %q, want %q", got, want)
|
||
}
|
||
cpu := catalog.Model{Profiles: []catalog.Profile{{Engine: "llamacpp", CPUCores: 6}}}
|
||
if got := describeServe(tr, cpu); got != "llama.cpp, CPU üzerinde, 6 çekirdek" {
|
||
t.Errorf("on the CPU: %q", got)
|
||
}
|
||
|
||
form.Set("context_length", "çok")
|
||
if _, err := buildModel(tr, form.Get); err == nil || err.Error() != "bağlam uzunluğu tam sayı olmalı" {
|
||
t.Errorf("a bad number is refused as %v", err)
|
||
}
|
||
form.Set("context_length", "4096")
|
||
form.Set("engine", "vllm")
|
||
form.Set("placement", "cpu")
|
||
if _, err := buildModel(tr, form.Get); err == nil || !strings.Contains(err.Error(), "GPU sınıfı seçin") {
|
||
t.Errorf("vLLM on the CPU is refused as %v", err)
|
||
}
|
||
|
||
// The plan the form opens with, on a fleet with no GPU and no vLLM.
|
||
page := mirrorInTurkish(t, servePage(t, []mirror.Status{held("coder", "v1", "m.gguf")}, &fakeCatalog{}, 0),
|
||
"/admin/mirror?serve=coder:v1", nil)
|
||
wantAll(t, "the serve form", page,
|
||
"<code>http://mirror:8081/coder/v1/m.gguf</code>, <strong>llamacpp</strong> ile sunulacak.",
|
||
`<option value="cpu" selected>CPU: bu kümede GPU yok</option>`,
|
||
"Kataloğa ekle", "Gereken RAM (GB)", "Bellek: ağırlıkların bir buçuk katı artı iki.",
|
||
`"Şöyle çalışacak: %s."`)
|
||
|
||
vllm := mirrorInTurkish(t, servePage(t, []mirror.Status{held("big", "v1", "model.safetensors")}, &fakeCatalog{}, 0, "llamacpp"),
|
||
"/admin/mirror?serve=big:v1", nil)
|
||
wantAll(t, "the vLLM warning", vllm,
|
||
"hiçbir düğüm vllm kurulu olduğunu bildirmiyor", "Bu kümede GPU da yok")
|
||
}
|