CI / test (push) Successful in 2m52s
It told Continue every model uses tools and nothing of its context: a model whose calls come back as text stalled Continue's agent, and Continue sent prompts far over a CPU replica's 4096 tokens, each refused. tool_use is now written for a model whose catalog entry says tools, and the context length with a fitting maxTokens whenever the catalog knows it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
251 lines
9.6 KiB
Go
251 lines
9.6 KiB
Go
package web
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log/slog"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"net/url"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/account"
|
||
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
|
||
)
|
||
|
||
// The public and personal pages are the first a Turkish operator's customers
|
||
// see: sign-up, sign-in, the landing page, the claim link and their own
|
||
// account. Each is drawn here in Turkish, end to end.
|
||
|
||
// publicInTurkish is a request whose browser asks for Turkish.
|
||
func publicInTurkish(r *http.Request) *http.Request {
|
||
r.Header.Set("Accept-Language", "tr-TR,tr;q=0.9,en;q=0.5")
|
||
return r
|
||
}
|
||
|
||
// publicRefusingAccounts is an account service whose sign-in fails with err.
|
||
type publicRefusingAccounts struct {
|
||
fakeAccounts
|
||
err error
|
||
}
|
||
|
||
func (a publicRefusingAccounts) SignIn(context.Context, string, string) (string, string, error) {
|
||
return "", "", a.err
|
||
}
|
||
func (publicRefusingAccounts) SignupOpen() bool { return true }
|
||
|
||
func TestTheSignInPageSpeaksTurkishWhenAsked(t *testing.T) {
|
||
mux := http.NewServeMux()
|
||
NewAccountPages(publicRefusingAccounts{err: account.ErrBadCredentials}, nil, nil, "v0",
|
||
slog.New(slog.DiscardHandler)).Register(mux)
|
||
|
||
rec := httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, publicInTurkish(httptest.NewRequest(http.MethodGet, "/login", nil)))
|
||
body := rec.Body.String()
|
||
for _, want := range []string{`<html lang="tr">`, "<title>Giriş yap · llmgw</title>", ">Parola<",
|
||
">E-posta<", `Hesabınız yok mu? <a href="/signup">Kaydolun</a>.`, "bu ağ geçidi hakkında"} {
|
||
if !strings.Contains(body, want) {
|
||
t.Errorf("the Turkish sign-in page lacks %q", want)
|
||
}
|
||
}
|
||
if strings.Contains(body, "No account?") {
|
||
t.Error("the Turkish sign-in page still has English on it")
|
||
}
|
||
|
||
// A refused sign-in says why in Turkish: the account service's own
|
||
// sentence, looked up by the error.
|
||
var cookie *http.Cookie
|
||
for _, c := range rec.Result().Cookies() {
|
||
if c.Name == loginCSRFCookie {
|
||
cookie = c
|
||
}
|
||
}
|
||
form := url.Values{"email": {"a@b.c"}, "password": {"x"}, "csrf": {cookie.Value}}
|
||
req := publicInTurkish(httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode())))
|
||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||
req.AddCookie(cookie)
|
||
rec = httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, req)
|
||
if !strings.Contains(rec.Body.String(), "bu e-posta ve parola hiçbir hesapla eşleşmiyor") {
|
||
t.Errorf("a wrong password is not said in Turkish: %s", rec.Body.String())
|
||
}
|
||
|
||
// And in English, as it always was.
|
||
if got := english.Err(account.ErrBadCredentials); got != account.ErrBadCredentials.Error() {
|
||
t.Errorf("English says %q", got)
|
||
}
|
||
}
|
||
|
||
func TestTheSignupPageSaysWhatIsWrongInTurkish(t *testing.T) {
|
||
mux := http.NewServeMux()
|
||
NewAccountPages(signupAccounts{err: account.ErrWeakPassword}, nil, nil, "v0",
|
||
slog.New(slog.DiscardHandler)).Register(mux)
|
||
form := url.Values{"email": {"a@b.c"}, "password": {"short"}}
|
||
req := publicInTurkish(httptest.NewRequest(http.MethodPost, "/signup", strings.NewReader(form.Encode())))
|
||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||
rec := httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, req)
|
||
body := rec.Body.String()
|
||
for _, want := range []string{
|
||
"<h1>Hesap oluşturun</h1>",
|
||
fmt.Sprintf("parola en az %d karakter olmalı", account.MinPasswordLen),
|
||
fmt.Sprintf("En az %d karakter.", account.MinPasswordLen),
|
||
">Kaydol</button>",
|
||
} {
|
||
if !strings.Contains(body, want) {
|
||
t.Errorf("the Turkish signup page lacks %q", want)
|
||
}
|
||
}
|
||
// The English sentence is the account service's own, word for word.
|
||
if got := english.T("signup.err.weak_password", account.MinPasswordLen); got != account.ErrWeakPassword.Error() {
|
||
t.Errorf("English says %q, the service %q", got, account.ErrWeakPassword)
|
||
}
|
||
}
|
||
|
||
func TestTheProfilePageShowsTurkishNumbersAndDates(t *testing.T) {
|
||
ctx := context.Background()
|
||
st, err := store.OpenSQLite(ctx, filepath.Join(t.TempDir(), "s.db"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Cleanup(func() { _ = st.Close() })
|
||
budget := int64(50e6)
|
||
st.SetPersonalDefaults(&store.Limits{Budget: &budget, BudgetPeriod: store.PeriodMonth})
|
||
if err := st.WriteUsage(ctx, []store.Usage{
|
||
{At: time.Now(), KeyID: "session:ada", UserID: "ada", Model: "m", ServedModel: "m",
|
||
PromptTokens: 1200, CompletionTokens: 300, CostMicros: 45e6, Status: 200},
|
||
}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
ada := store.User{ID: "ada", Email: "ada@example.com", Name: "Ada", PasswordHash: "!",
|
||
State: store.UserActive, CreatedAt: time.Now()}
|
||
if err := st.CreateUser(ctx, ada); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
_, k, err := store.NewKey("key-tr", "dizüstü", "")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
made := time.Date(2026, 3, 9, 12, 0, 0, 0, time.UTC)
|
||
k.CreatedAt = made
|
||
if err := st.CreateUserKey(ctx, ada.ID, k); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
p := NewAccountPages(fakeAccounts{user: ada}, st, nil, "v0", slog.New(slog.DiscardHandler))
|
||
mux := http.NewServeMux()
|
||
p.Register(mux)
|
||
req := publicInTurkish(httptest.NewRequest(http.MethodGet, "/profile", nil))
|
||
req.AddCookie(&http.Cookie{Name: SessionCookie, Value: "good"})
|
||
rec := httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, req)
|
||
body := rec.Body.String()
|
||
|
||
for _, want := range []string{
|
||
"<h1>Hesabınız</h1>", "Çıkış yap", ">Hesap</a>",
|
||
"Kullanımınız ve sınırlarınız", "Günlük token",
|
||
// 1500 tokens, and 45 of 50 units this month, written the Turkish way.
|
||
">1,5 bin</div>", "token · 30 gün",
|
||
"Kullandığınız her şeyi kapsayan bütçeniz", "<b>45,00</b> / 50,00 (bu ay)",
|
||
"aylık 50,00",
|
||
// The key: made on 9 March, never used, and active, which stays its
|
||
// class while its word changes.
|
||
"<td>09.03.2026</td>", "<td>hiçbir zaman</td>", `class="state state-active">etkin<`,
|
||
"<td>Kişisel</td>", ">İptal et</button>",
|
||
// The budget bar's width is CSS, and CSS is not Turkish.
|
||
"width: 90%",
|
||
} {
|
||
if !strings.Contains(body, want) {
|
||
t.Errorf("the Turkish profile lacks %q", want)
|
||
}
|
||
}
|
||
for _, word := range []string{"Your use and limits", "this month", "Revoke"} {
|
||
if strings.Contains(body, word) {
|
||
t.Errorf("the Turkish profile still says %q", word)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestTheEditorSetupExplainsItselfInTurkish(t *testing.T) {
|
||
tr := langByCode("tr")
|
||
c := connectIn(tr, "cline", "https://gw.example", "m", "sk-llmgw-abc", ModelFacts{})
|
||
if c.Fields[2].Label != "API anahtarı (API Key)" || c.Fields[0].Value != "OpenAI Compatible" ||
|
||
!strings.Contains(c.Run, "dişli simgesine") {
|
||
t.Errorf("cline in Turkish = %+v", c)
|
||
}
|
||
x := connectIn(tr, "codex", "https://gw.example", "m", "sk-llmgw-abc", ModelFacts{})
|
||
// What is typed or written to a file stays what the tool reads.
|
||
if x.Run != "LLMGW_API_KEY='sk-llmgw-abc' codex --profile llmgw" || !strings.Contains(x.Files[0].Body, "[profiles.llmgw]") ||
|
||
!strings.Contains(x.Notes[0], "~/.codex/config.toml") {
|
||
t.Errorf("codex in Turkish = %+v", x)
|
||
}
|
||
if en := connectFor("continue", "https://gw.example", "m", "k", ModelFacts{}); en.Run != `In Continue, choose the "llmgw" assistant at the top of its panel.` {
|
||
t.Errorf("English Continue: %q", en.Run)
|
||
}
|
||
}
|
||
|
||
func TestTheLandingPageSpeaksTurkish(t *testing.T) {
|
||
h := Landing("v1", true, false, "platform@example.com", "")
|
||
rec := httptest.NewRecorder()
|
||
h.ServeHTTP(rec, publicInTurkish(httptest.NewRequest(http.MethodGet, "http://gw.example/", nil)))
|
||
body := rec.Body.String()
|
||
for _, want := range []string{
|
||
`<html lang="tr">`, "OpenAI uyumlu tek bir uç nokta", "Üç adımda başlayın",
|
||
`Anahtar için şuraya başvurun: <a href="mailto:platform@example.com">platform@example.com</a>.`,
|
||
`<a href="/login">giriş yapın</a>`,
|
||
// The examples are code, and code is the same in every language.
|
||
"http://gw.example/v1/chat/completions", "Authorization: Bearer",
|
||
} {
|
||
if !strings.Contains(body, want) {
|
||
t.Errorf("the Turkish landing page lacks %q", want)
|
||
}
|
||
}
|
||
if strings.Contains(body, "Three steps") || strings.Contains(body, "for one.") {
|
||
t.Error("the Turkish landing page still has English on it")
|
||
}
|
||
|
||
// English reads as it did, the contact a link inside its sentence.
|
||
rec = httptest.NewRecorder()
|
||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "http://gw.example/", nil))
|
||
if !strings.Contains(rec.Body.String(), `Ask <a href="mailto:platform@example.com">platform@example.com</a>`+"\n for one.") {
|
||
t.Errorf("the English contact sentence changed: %s", rec.Body.String())
|
||
}
|
||
|
||
// A contact that is not a link is emphasised, and escaped either way.
|
||
rec = httptest.NewRecorder()
|
||
Landing("v1", false, false, "<b>Ayşe</b>", "").ServeHTTP(rec,
|
||
publicInTurkish(httptest.NewRequest(http.MethodGet, "http://gw.example/", nil)))
|
||
if !strings.Contains(rec.Body.String(), "<strong><b>Ayşe</b></strong>") {
|
||
t.Errorf("a plain contact: %s", rec.Body.String())
|
||
}
|
||
}
|
||
|
||
func TestTheClaimPageSpeaksTurkish(t *testing.T) {
|
||
f := &fakeClaims{secret: "sk-llmgw-thesecret"}
|
||
mux := http.NewServeMux()
|
||
h := Claim(f, "v1", slog.New(slog.DiscardHandler))
|
||
mux.Handle("GET /claim/{token}", h)
|
||
mux.Handle("POST /claim/{token}", h)
|
||
get := func() string {
|
||
rec := httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, publicInTurkish(httptest.NewRequest(http.MethodGet, "/claim/abc", nil)))
|
||
return rec.Body.String()
|
||
}
|
||
|
||
body := get()
|
||
if !strings.Contains(body, ">Anahtarımı göster</button>") || !strings.Contains(body, "Son geçerlilik: ") {
|
||
t.Errorf("the Turkish claim page: %s", body)
|
||
}
|
||
rec := httptest.NewRecorder()
|
||
mux.ServeHTTP(rec, publicInTurkish(httptest.NewRequest(http.MethodPost, "/claim/abc", nil)))
|
||
if b := rec.Body.String(); !strings.Contains(b, "sk-llmgw-thesecret") || !strings.Contains(b, "parola yöneticisine") {
|
||
t.Errorf("the revealed key in Turkish: %s", b)
|
||
}
|
||
if b := get(); !strings.Contains(b, "bu bağlantı artık geçerli değil") {
|
||
t.Errorf("a used link in Turkish: %s", b)
|
||
}
|
||
}
|