Files
weichen 914d0aa39d feat(maintenance): wire sysadmin ui.* whitelist settings to public site status
- sysadmin.Service.GetSettingValue: whitelist read-through (DB > env > default)
- maintenance.Service: SiteSettingsProvider injection; StatusView carries notice/readOnly; new CurrentSiteStatus + GET /api/v1/maintenance/site-status
- composition root wires ui.maintenance_notice / ui.read_only_mode into maintenance site settings
- MaintenanceBanner: persistent notice banner + read-only mode strip
- chore(web): drop dead 'dirty' var in EnvSettingsSection (baseline TS6133 build blocker)
- KB ops actions: deferred out-of-repo (shared infra container side); documented in task record
2026-09-25 16:33:56 +00:00

91 lines
2.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package maintenance
import (
"context"
"errors"
"testing"
"time"
)
// P9 白名单消费方接线:site provider 注入后 CurrentStatus 无窗口时也返回 notice/readOnly。
func TestCurrentStatusCarriesSiteSettings(t *testing.T) {
svc := NewService(&fakeStore{err: ErrNotFound})
svc.SetSiteSettings(func(ctx context.Context) (string, bool) {
return "系统今晚 22:00 例行维护", true
})
view, err := svc.CurrentStatus(context.Background())
if err != nil {
t.Fatal(err)
}
if view.Status != "none" {
t.Fatalf("status = %s, want none (no window)", view.Status)
}
if view.Notice != "系统今晚 22:00 例行维护" {
t.Fatalf("notice = %q", view.Notice)
}
if !view.ReadOnly {
t.Fatal("readOnly must carry through")
}
}
// CurrentSiteStatus:provider 返回非空 notice 时状态切 site_notice;未接线为零值。
func TestCurrentSiteStatus(t *testing.T) {
svc := NewService(&fakeStore{err: ErrNotFound})
if v := svc.CurrentSiteStatus(context.Background()); v.Status != "none" || v.Notice != "" || v.ReadOnly {
t.Fatalf("unwired provider should zero, got %+v", v)
}
svc.SetSiteSettings(func(ctx context.Context) (string, bool) { return "公告", false })
v := svc.CurrentSiteStatus(context.Background())
if v.Status != "site_notice" || v.Notice != "公告" || v.ReadOnly {
t.Fatalf("site status = %+v", v)
}
}
// 有维护窗口时 notice/readOnly 同样随窗口状态返回。
func TestCurrentStatusWindowPlusSite(t *testing.T) {
start := time.Now().Add(time.Hour).UTC()
svc := NewService(&fakeStore{w: &Window{
ID: 7, ScheduledStart: start, ScheduledEnd: start.Add(2 * time.Hour),
Message: "升级", Status: StatusScheduled,
}})
svc.SetSiteSettings(func(ctx context.Context) (string, bool) { return "横幅公告", false })
view, err := svc.CurrentStatus(context.Background())
if err != nil {
t.Fatal(err)
}
if !view.Scheduled || view.Message != "升级" {
t.Fatalf("window view = %+v", view)
}
if view.Notice != "横幅公告" {
t.Fatalf("notice lost: %+v", view)
}
}
type fakeStore struct {
w *Window
err error
}
func (f *fakeStore) GetOpen(ctx context.Context) (*Window, error) {
if f.err != nil {
return nil, f.err
}
return f.w, nil
}
func (f *fakeStore) GetByID(ctx context.Context, id int64) (*Window, error) {
return f.w, nil
}
func (f *fakeStore) Insert(ctx context.Context, w Window) (Window, error) {
return w, nil
}
func (f *fakeStore) UpdateStatus(ctx context.Context, id int64, from, to string) (Window, error) {
if f.w != nil {
f.w.Status = to
return *f.w, nil
}
return Window{}, errors.New("no window")
}
func (f *fakeStore) ListRecent(ctx context.Context, limit int) ([]Window, error) {
return nil, nil
}