package server import ( "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" "agent-system-backend/internal/bus" "agent-system-backend/internal/plugins" "agent-system-backend/internal/profiles" "agent-system-backend/internal/runmgr" "agent-system-backend/internal/runtime" "agent-system-backend/internal/settings" "agent-system-backend/internal/store" "agent-system-backend/internal/tools" ) // WP3-I3:GET /api/admin-zone/sections 展平编译插件的分区声明并按 // Order→ID 稳定排序;无声明时空数组(非 null)。 func TestListAdminZoneSections(t *testing.T) { dir := t.TempDir() st, err := store.OpenSQLite(filepath.Join(dir, "test.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { st.Close() }) b := bus.New() reg := tools.NewRegistry() reg.RegisterBuiltin() setSvc := settings.NewService(st) profSvc := profiles.New(st) profSvc.EnsureDefault() mgr := runmgr.New(st, b, reg, profSvc, setSvc, &runtime.LLMRunner{}, &runtime.SubLLMRunner{}, &runtime.MockRunner{}, &runtime.MockSubagent{}) ph := plugins.New(st, reg, setSvc, filepath.Join(dir, "plugins")) srv := New(st, mgr, reg, profSvc, setSvc, ph) srv.Auth = NewAuthenticator(st, true) srv.CompiledPlugins = []CompiledPlugin{ {ID: "rdm", AdminZones: []AdminZoneSection{ {ID: "rdm.audit", Title: "项目追溯与审计", Icon: "search", Order: 20}, }}, {ID: "other", AdminZones: []AdminZoneSection{ {ID: "other.z", Title: "Z", Order: 10}, {ID: "other.a", Title: "A", Order: 10}, }}, } h := srv.Handler() key, err := st.CreateAPIKey("alice", "e2e") if err != nil { t.Fatal(err) } get := func(path string) *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodGet, path, nil) req.Header.Set("Authorization", "Bearer "+key) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) return rec } // 未认证拒绝。 if rec := httptest.NewRecorder(); func() *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodGet, "/api/admin-zone/sections", nil) h.ServeHTTP(rec, req) return rec }().Code != http.StatusUnauthorized { t.Fatalf("unauthenticated request should 401") } rec := get("/api/admin-zone/sections") if rec.Code != http.StatusOK { t.Fatalf("status %d: %s", rec.Code, rec.Body.String()) } var got []AdminZoneSection if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { t.Fatal(err) } wantIDs := []string{"other.a", "other.z", "rdm.audit"} // Order 同为 10 的按 ID,再 Order 20 if len(got) != 3 { t.Fatalf("sections = %+v", got) } for i, w := range wantIDs { if got[i].ID != w { t.Fatalf("sections[%d] = %s, want %s (full: %+v)", i, got[i].ID, w, got) } } // 无声明时空数组非 null。 srv.CompiledPlugins = nil if rec := get("/api/admin-zone/sections"); rec.Body.String() == "null" { t.Fatalf("empty listing must be [] not null: %s", rec.Body.String()) } }