package router import ( "net/http" "net/http/httptest" "strings" "testing" "time" "hyapp-admin-server/internal/config" "hyapp-admin-server/internal/modules/opscenter" "hyapp-admin-server/internal/service" "github.com/gin-gonic/gin" ) func TestOpsCenterAppBootstrapDoesNotRequireAppCode(t *testing.T) { gin.SetMode(gin.TestMode) auth := service.NewAuthService("ops-center-router-test-secret", time.Hour) token, _, err := auth.GenerateAccessToken(7, "operator", nil) if err != nil { t.Fatalf("generate access token: %v", err) } engine := New(config.Config{}, auth, nil, Handlers{OpsCenter: opscenter.New(nil, nil)}) request := httptest.NewRequest(http.MethodGet, "/api/v1/admin/ops-center/apps", nil) request.Header.Set("Authorization", "Bearer "+token) response := httptest.NewRecorder() engine.ServeHTTP(response, request) // Ops Center 先读取跨 App 目录,再按 app_code 查询每个应用的数据;启动接口若要求 // X-App-Code,就会形成“先拿到 App 才能发 header、先发 header 才能拿 App”的死锁。 if response.Code != http.StatusOK { t.Fatalf("status = %d body=%s", response.Code, response.Body.String()) } if body := response.Body.String(); !strings.Contains(body, `"app_code":"yumi"`) || !strings.Contains(body, `"app_code":"aslan"`) { t.Fatalf("bootstrap app catalog missing fixed apps: %s", body) } // 从 appProtected 移出只解除 App 选择依赖;路由仍必须位于 protected 下,不能变成匿名应用目录。 unauthorized := httptest.NewRecorder() engine.ServeHTTP(unauthorized, httptest.NewRequest(http.MethodGet, "/api/v1/admin/ops-center/apps", nil)) if unauthorized.Code != http.StatusUnauthorized { t.Fatalf("unauthorized status = %d body=%s", unauthorized.Code, unauthorized.Body.String()) } }