feat(server): public /maintenance/site-status route + tests; P9 evidence
- isPublicRoute exempts site-status (anonymous banner polling) - httptest coverage: anonymous 200 + site_notice payload; route table guards - test report + playwright evidence (docker mcr playwright v1.63 noble vs real dev stack, 1 passed)
This commit is contained in:
@@ -21,7 +21,8 @@ func isPublicRoute(method, path string) bool {
|
||||
if path == "/" || path == "/health" || path == "/api/v1/health" ||
|
||||
path == "/ready" || path == "/api/v1/ready" ||
|
||||
path == "/api/v1/vault/status" ||
|
||||
path == "/api/v1/maintenance/status" {
|
||||
path == "/api/v1/maintenance/status" ||
|
||||
path == "/api/v1/maintenance/site-status" {
|
||||
return true
|
||||
}
|
||||
if method == http.MethodPost && path == "/api/v1/auth/login" {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/maintenance"
|
||||
)
|
||||
|
||||
// P9:site-status 为公开端点(登录页横幅轮询),匿名可达且不落鉴权错误。
|
||||
func TestMaintenanceSiteStatusPublic(t *testing.T) {
|
||||
svc := maintenance.NewService(&fakeSiteStore{})
|
||||
svc.SetSiteSettings(func(ctx context.Context) (string, bool) {
|
||||
return "公告内容", true
|
||||
})
|
||||
s := &Server{}
|
||||
s.setOverrideMaintenance(svc)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /api/v1/maintenance/site-status", s.handleMaintenanceSiteStatus)
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := http.Get(ts.URL + "/api/v1/maintenance/site-status")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("status = %d", resp.StatusCode)
|
||||
}
|
||||
var view maintenance.StatusView
|
||||
if err := json.NewDecoder(resp.Body).Decode(&view); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if view.Notice != "公告内容" || !view.ReadOnly || view.Status != "site_notice" {
|
||||
t.Fatalf("site view = %+v", view)
|
||||
}
|
||||
}
|
||||
|
||||
// isPublicRoute 必须豁免 site-status(匿名横幅)。
|
||||
func TestIsPublicRouteMaintenanceSiteStatus(t *testing.T) {
|
||||
if !isPublicRoute(http.MethodGet, "/api/v1/maintenance/site-status") {
|
||||
t.Fatal("site-status must be public")
|
||||
}
|
||||
if !isPublicRoute(http.MethodGet, "/api/v1/maintenance/status") {
|
||||
t.Fatal("status must stay public")
|
||||
}
|
||||
if isPublicRoute(http.MethodGet, "/api/v1/admin/maintenance") {
|
||||
t.Fatal("admin maintenance must not be public")
|
||||
}
|
||||
}
|
||||
|
||||
type fakeSiteStore struct{}
|
||||
|
||||
func (f *fakeSiteStore) GetOpen(ctx context.Context) (*maintenance.Window, error) {
|
||||
return nil, maintenance.ErrNotFound
|
||||
}
|
||||
func (f *fakeSiteStore) GetByID(ctx context.Context, id int64) (*maintenance.Window, error) {
|
||||
return nil, maintenance.ErrNotFound
|
||||
}
|
||||
func (f *fakeSiteStore) Insert(ctx context.Context, w maintenance.Window) (maintenance.Window, error) {
|
||||
return w, nil
|
||||
}
|
||||
func (f *fakeSiteStore) UpdateStatus(ctx context.Context, id int64, from, to string) (maintenance.Window, error) {
|
||||
return maintenance.Window{}, nil
|
||||
}
|
||||
func (f *fakeSiteStore) ListRecent(ctx context.Context, limit int) ([]maintenance.Window, error) {
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user