- 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
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
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
|
||
}
|