package httpapi import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/alexedwards/scs/v2" "github.com/descrybe/descrybe-v2/apps/api/internal/auth" "github.com/descrybe/descrybe-v2/apps/api/internal/config" "github.com/descrybe/descrybe-v2/apps/api/internal/platformsettings" "github.com/google/uuid" ) // TestRouterAdminSettingsAIConfigPresent locks GET /api/admin/settings AI surface: // legacy openai block always; multi-role ai_roles with all catalog roles masked. func TestRouterAdminSettingsAIConfigPresent(t *testing.T) { t.Parallel() sm := scs.New() sm.Cookie.Name = "descrybe_session" uid := uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") s := &Server{ Config: config.Config{ CSRFCookieName: "descrybe_csrf", WebOrigin: "http://localhost:5173", }, Sessions: sm, Auth: &auth.Service{}, PlatformSettings: platformsettings.NewService(nil, platformsettings.EnvConfig{}), testPlatformAdmin: func(_ context.Context, got uuid.UUID) (bool, error) { return got == uid, nil }, } var token string seed := LoadSession(sm)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sm.Put(r.Context(), auth.SessionUserIDKey, uid.String()) w.WriteHeader(http.StatusNoContent) })) seedRec := httptest.NewRecorder() seed.ServeHTTP(seedRec, httptest.NewRequest(http.MethodGet, "/seed", nil)) for _, c := range seedRec.Result().Cookies() { if c.Name == sm.Cookie.Name { token = c.Value } } if token == "" { t.Fatal("expected session cookie from seed request") } h := s.Router() rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/admin/settings", nil) req.AddCookie(&http.Cookie{Name: sm.Cookie.Name, Value: token}) h.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status=%d want 200 body=%s", rec.Code, rec.Body.String()) } var body map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("decode: %v body=%s", err, rec.Body.String()) } openai, ok := body["openai"].(map[string]any) if !ok { t.Fatalf("missing openai object: %s", rec.Body.String()) } for _, key := range []string{"configured", "has_api_key", "source"} { if _, ok := openai[key]; !ok { t.Fatalf("openai missing %q: %#v", key, openai) } } if raw, exists := openai["api_key"]; exists && raw != nil && raw != "" { t.Fatalf("openai must not leak api_key, got %#v", raw) } rawConfigs, hasConfigs := body["ai_roles"] if !hasConfigs || rawConfigs == nil { t.Fatalf("ai_roles missing body=%s", rec.Body.String()) } configs, ok := rawConfigs.(map[string]any) if !ok { t.Fatalf("ai_roles type=%T want object body=%s", rawConfigs, rec.Body.String()) } for _, role := range platformsettings.AIRoles { slot, ok := configs[role].(map[string]any) if !ok { t.Fatalf("ai_roles missing role %q: %#v", role, configs) } if slot["role"] != role { t.Fatalf("role %q slot.role=%v", role, slot["role"]) } if raw, exists := slot["api_key"]; exists && raw != nil && raw != "" { t.Fatalf("ai_roles.%s must not leak api_key", role) } } }