Files
magpie/internal/gui/settings_keep_test.go
T
yetone ee8a371cf4 gui, update: saving the Settings page keeps the models hidden from each agent, and update checks go through magpie's proxy and say so (#294: 设置代理之后只有模型代理生效了,代理没有用于检查 magpie 是否有更新) — POST /api/settings carried over the models' names, levels and images but not HiddenModels, which the page never sends, so changing the theme, the language or anything else there showed every model hidden one by one on the Agents page again; it is carried over with the others. The update feed and the release came through http.DefaultTransport, proxied only because main installs netproxy first; the update client now takes netproxy.Func on a transport of its own (Settings' Proxy, else *_PROXY, else the system's). A check that failed at start-up, before a proxy was set, stayed failed for six hours: saving a new proxy now checks again through it. And the Settings row read "requests to vendors go through {proxy}", which says update checks don't: it now names both. TestSettingsSaveKeepsWhatItDoesNotSend (every field prefsKeep doesn't send survives a save of the theme alone; fails on main with HiddenModels emptied), TestUpdateGoesThroughTheProxy (feed and asset at hosts only a local proxy answers; fails without the transport), TestSettingsProxyRechecksAFailedUpdate (fails without the re-check). account-proxy, provider-proxy and click-scroll GUI tests pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, TestClaudeSignedOut, TestClaudeAccountNamedAsItsLogin, known flakes under load, pass alone).
2026-09-30 16:03:52 +08:00

109 lines
3.8 KiB
Go

package gui
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"
"github.com/yetone/magpie/internal/redact"
"github.com/yetone/magpie/internal/settings"
)
// What the models' page keeps in settings — names, levels, whether a model
// takes images — outlives a save of the Settings page, which never sends it.
func TestSettingsSaveKeepsModelChoices(t *testing.T) {
h := t.TempDir()
t.Setenv("HOME", h)
t.Setenv("XDG_CONFIG_HOME", filepath.Join(h, ".config"))
t.Setenv("XDG_CACHE_HOME", filepath.Join(h, ".cache"))
if err := settings.Save(settings.Settings{
ModelNames: map[string]string{"p/m": "Mine"},
ModelEfforts: map[string][]string{"p/m": {"low"}},
ModelImages: map[string]bool{"p/m": true},
}); err != nil {
t.Fatal(err)
}
rec := httptest.NewRecorder()
Handler(nil, nil).ServeHTTP(rec, httptest.NewRequest("POST", "/api/settings", strings.NewReader(`{"theme":"dark","lang":"en"}`)))
if rec.Code != http.StatusOK {
t.Fatalf("%d %s", rec.Code, rec.Body)
}
s := settings.Load()
if s.Theme != "dark" || s.ModelNames["p/m"] != "Mine" || len(s.ModelEfforts["p/m"]) != 1 || !s.ModelImages["p/m"] {
t.Fatalf("theme %q names %v efforts %v images %v", s.Theme, s.ModelNames, s.ModelEfforts, s.ModelImages)
}
}
// Everything in settings the Settings page doesn't send — the models hidden
// from an agent one by one among it — outlives a save of that page: each
// field prefsKeep leaves out is set here, and must be as it was after a
// save of the theme alone. A field added to settings is either sent by the
// page or has to be set here, and kept by the handler.
func TestSettingsSaveKeepsWhatItDoesNotSend(t *testing.T) {
h := t.TempDir()
t.Setenv("HOME", h)
t.Setenv("XDG_CONFIG_HOME", filepath.Join(h, ".config"))
t.Setenv("XDG_CACHE_HOME", filepath.Join(h, ".cache"))
js, err := os.ReadFile("assets/app.js")
if err != nil {
t.Fatal(err)
}
_, fn, _ := strings.Cut(string(js), "\nfunction prefsKeep(s) {")
fn, _, _ = strings.Cut(fn, "\n}\n")
sent := map[string]bool{}
for _, m := range regexp.MustCompile(`(\w+):`).FindAllStringSubmatch(fn, -1) {
sent[m[1]] = true
}
if !sent["theme"] || !sent["proxy"] {
t.Fatalf("prefsKeep not read from app.js: %v", sent)
}
was := settings.Settings{
AgentOrder: []string{"codex"},
AgentsHidden: []string{"goose"},
AgentsShown: []string{"pi"},
Visible: map[string][]string{"claude": {"anthropic"}},
HiddenModels: map[string][]string{"claude": {"p/m"}, "codex": {"p/n", "group/g"}},
ModelNames: map[string]string{"p/m": "Mine"},
ModelEfforts: map[string][]string{"p/m": {"low"}},
ModelImages: map[string]bool{"p/m": true},
RedactRules: []redact.Rule{{Kind: "prefix", Prefix: "oc_sk_"}},
LAN: true,
LANKey: "sk-lan",
QuotaLeft: true,
TextSize: 125,
Window: []int{900, 700},
}
if err := settings.Save(was); err != nil {
t.Fatal(err)
}
was = settings.Load()
rec := httptest.NewRecorder()
Handler(nil, nil).ServeHTTP(rec, httptest.NewRequest("POST", "/api/settings", strings.NewReader(`{"theme":"dark"}`)))
if rec.Code != http.StatusOK {
t.Fatalf("%d %s", rec.Code, rec.Body)
}
now := settings.Load()
if now.Theme != "dark" {
t.Fatalf("theme %q", now.Theme)
}
wv, nv, typ := reflect.ValueOf(was), reflect.ValueOf(now), reflect.TypeOf(was)
for i := range typ.NumField() {
name, _, _ := strings.Cut(typ.Field(i).Tag.Get("json"), ",")
if sent[name] {
continue
}
if wv.Field(i).IsZero() {
t.Errorf("%s: not sent by the Settings page; set it in this test and keep it in POST /api/settings", typ.Field(i).Name)
continue
}
if !reflect.DeepEqual(wv.Field(i).Interface(), nv.Field(i).Interface()) {
t.Errorf("%s: %v after a save of the Settings page, was %v", typ.Field(i).Name, nv.Field(i).Interface(), wv.Field(i).Interface())
}
}
}