package web import ( "context" "errors" "io" "log/slog" "net/http" "net/url" "strings" "testing" "time" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) type fakeSettings struct{ versions []store.SettingsVersion } func (f *fakeSettings) ClusterSettings(context.Context) (store.SettingsVersion, error) { if len(f.versions) == 0 { return store.SettingsVersion{}, nil } return f.versions[len(f.versions)-1], nil } func (f *fakeSettings) ClusterSettingsAt(_ context.Context, v int64) (store.SettingsVersion, error) { for _, s := range f.versions { if s.Version == v { return s, nil } } return store.SettingsVersion{}, nil } func (f *fakeSettings) ClusterSettingsHistory(context.Context, int) ([]store.SettingsVersion, error) { out := make([]store.SettingsVersion, len(f.versions)) for i, v := range f.versions { out[len(f.versions)-1-i] = v } return out, nil } func (f *fakeSettings) PutClusterSettings(_ context.Context, base int64, body, by, note string) (int64, error) { cur, _ := f.ClusterSettings(context.Background()) if base != cur.Version { return 0, store.ErrSettingsChanged } n := cur.Version + 1 f.versions = append(f.versions, store.SettingsVersion{Version: n, Body: body, By: by, Note: note, At: time.Now()}) return n, nil } type auditSink struct{ events []store.AuditEvent } func (a *auditSink) WriteAudit(_ context.Context, e store.AuditEvent) error { a.events = append(a.events, e) return nil } func clusterSettingsPage(t *testing.T, user store.User) (*http.ServeMux, *fakeSettings, *auditSink) { t.Helper() s := &fakeSettings{versions: []store.SettingsVersion{{Version: 1, Body: "guard:\n enabled: true\n", By: "init", At: time.Now()}}} p := NewAccountPages(fakeAccounts{user: user}, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil))) sink := &auditSink{} p.SetAudit(sink) mux := http.NewServeMux() p.RegisterAdminSettings(mux, s, func(b []byte) error { if strings.Contains(string(b), "bogus") { return errors.New("unknown key bogus") } return nil }, []string{"listen", "store"}) return mux, s, sink } func TestTheSettingsPageIsForAdministratorsOnly(t *testing.T) { for _, u := range []store.User{ {ID: "u1", Email: "v@example.com", Role: store.RoleViewer}, {ID: "u2", Email: "o@example.com", Role: store.RoleOperator}, {ID: "u3", Email: "b@example.com", Role: store.RoleBilling}, } { mux, _, _ := clusterSettingsPage(t, u) if w := mirrorGet(t, mux, "/admin/settings"); w.Code != http.StatusNotFound { t.Errorf("%s saw the settings: %d", u.Role, w.Code) } } mux, _, _ := clusterSettingsPage(t, store.User{ID: "a", Email: "a@example.com", IsAdmin: true}) body := mirrorGet(t, mux, "/admin/settings").Body.String() for _, want := range []string{"guard:\n enabled: true", "Version 1, in force", "listen", `href="/admin/settings" aria-current="page"`} { if !strings.Contains(body, want) { t.Errorf("no %q in:\n%s", want, body) } } } func TestSettingsAreCheckedAppliedAndRolledBack(t *testing.T) { mux, s, sink := clusterSettingsPage(t, store.User{ID: "a", Email: "a@example.com", IsAdmin: true}) post := func(form url.Values) string { form.Set("csrf", "tok") return mirrorPost(t, mux, "/admin/settings", form).Body.String() } // Refused: does not load. The edit stays in the editor; nothing kept. body := post(url.Values{"base": {"1"}, "body": {"bogus: 1\n"}}) if !strings.Contains(body, "unknown key bogus") || !strings.Contains(body, "bogus: 1") || len(s.versions) != 1 { t.Errorf("an invalid document: %d versions\n%s", len(s.versions), body) } // Applied, with its note and who. body = post(url.Values{"base": {"1"}, "body": {"guard:\r\n enabled: false\r\n"}, "note": {"maintenance"}}) if !strings.Contains(body, "Version 2 is in force") || len(s.versions) != 2 || s.versions[1].Body != "guard:\n enabled: false\n" || s.versions[1].By != "a@example.com" || s.versions[1].Note != "maintenance" { t.Errorf("applying: %+v\n%s", s.versions, body) } // Made from a version no longer the newest: refused, not overwritten. body = post(url.Values{"base": {"1"}, "body": {"guard:\n enabled: true\n x: 1\n"}}) if !strings.Contains(body, "someone changed the settings") || len(s.versions) != 2 { t.Errorf("a stale edit: %d versions\n%s", len(s.versions), body) } // An earlier version is shown, and put back as a new one. if body := mirrorGet(t, mux, "/admin/settings?version=1").Body.String(); !strings.Contains(body, "Put version 1 back in force") { t.Errorf("viewing version 1:\n%s", body) } w := mirrorPost(t, mux, "/admin/settings/rollback", url.Values{"csrf": {"tok"}, "version": {"1"}}) if len(s.versions) != 3 || s.versions[2].Body != s.versions[0].Body || !strings.Contains(w.Body.String(), "as version 3") { t.Errorf("rollback: %+v\n%s", s.versions, w.Body.String()) } // Without the CSRF token, nothing. if w := mirrorPost(t, mux, "/admin/settings", url.Values{"base": {"3"}, "body": {"a: 1\n"}}); w.Code != http.StatusBadRequest || len(s.versions) != 3 { t.Errorf("no token: %d", w.Code) } // Audited, without the document itself: it can hold secrets. var saw bool for _, e := range sink.events { if strings.Contains(e.Detail, "enabled") { t.Errorf("the document is in the audit log: %+v", e) } if e.Action == "POST /admin/settings" && strings.Contains(e.Detail, "version 2") { saw = true } } if !saw { t.Errorf("no audit line for version 2: %+v", sink.events) } }