package routes import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/gin-gonic/gin" ) func newCommonRoutesTestRouter() *gin.Engine { gin.SetMode(gin.TestMode) r := gin.New() RegisterCommonRoutes(r) return r } func TestCommonRoutes_ClaudeCodeBootstrap(t *testing.T) { r := newCommonRoutesTestRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodGet, "/api/claude_cli/bootstrap", nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want %d", w.Code, http.StatusOK) } var body struct { ClientData any `json:"client_data"` AdditionalModelOptions []map[string]any `json:"additional_model_options"` AdditionalModelCosts map[string]map[string]any `json:"additional_model_costs"` } if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { t.Fatalf("unmarshal response: %v", err) } if body.ClientData != nil { t.Fatalf("client_data = %#v, want nil", body.ClientData) } if body.AdditionalModelOptions == nil || len(body.AdditionalModelOptions) != 0 { t.Fatalf("additional_model_options = %#v, want empty array", body.AdditionalModelOptions) } if body.AdditionalModelCosts == nil || len(body.AdditionalModelCosts) != 0 { t.Fatalf("additional_model_costs = %#v, want empty object", body.AdditionalModelCosts) } } func TestCommonRoutes_ClaudeCodePolicyLimits(t *testing.T) { r := newCommonRoutesTestRouter() w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodGet, "/api/claude_code/policy_limits", nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want %d", w.Code, http.StatusOK) } var body struct { Restrictions map[string]struct { Allowed bool `json:"allowed"` } `json:"restrictions"` } if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { t.Fatalf("unmarshal response: %v", err) } if body.Restrictions == nil || len(body.Restrictions) != 0 { t.Fatalf("restrictions = %#v, want empty object", body.Restrictions) } } func TestCommonRoutes_GrowthBookJSONRoutes(t *testing.T) { r := newCommonRoutesTestRouter() testCases := []struct { name string method string path string }{ {name: "features", method: http.MethodGet, path: "/api/features/sdk-zAZezfDKGoZuXXKe"}, {name: "eval", method: http.MethodPost, path: "/api/eval/sdk-zAZezfDKGoZuXXKe"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest(tc.method, tc.path, strings.NewReader(`{"attributes":{}}`)) req.Header.Set("Content-Type", "application/json") r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want %d", w.Code, http.StatusOK) } if got := w.Header().Get("x-sse-support"); got != "enabled" { t.Fatalf("x-sse-support = %q, want %q", got, "enabled") } var body struct { Features map[string]any `json:"features"` DateUpdated string `json:"dateUpdated"` } if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { t.Fatalf("unmarshal response: %v", err) } if body.Features == nil || len(body.Features) != 0 { t.Fatalf("features = %#v, want empty object", body.Features) } if body.DateUpdated != claudeCodeGrowthBookDateUpdated { t.Fatalf("dateUpdated = %q, want %q", body.DateUpdated, claudeCodeGrowthBookDateUpdated) } }) } } func TestCommonRoutes_GrowthBookSSERoutes(t *testing.T) { r := newCommonRoutesTestRouter() testPaths := []string{ "/sub/sdk-zAZezfDKGoZuXXKe", "/sub/features/sdk-zAZezfDKGoZuXXKe", } for _, path := range testPaths { t.Run(path, func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest(http.MethodGet, path, nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want %d", w.Code, http.StatusOK) } if got := w.Header().Get("Content-Type"); !strings.HasPrefix(got, "text/event-stream") { t.Fatalf("Content-Type = %q, want text/event-stream*", got) } body := w.Body.String() if !strings.Contains(body, "event:features") { t.Fatalf("SSE body missing features event: %q", body) } if !strings.Contains(body, `"dateUpdated":"`+claudeCodeGrowthBookDateUpdated+`"`) { t.Fatalf("SSE body missing dateUpdated payload: %q", body) } }) } }