package httpapi import ( "context" "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/billing" "github.com/descrybe/descrybe-v2/apps/api/internal/config" "github.com/google/uuid" ) func TestRouterPlanFeaturesMounted(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{}, Billing: &billing.Service{}, 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() unauth := httptest.NewRecorder() h.ServeHTTP(unauth, httptest.NewRequest(http.MethodGet, "/api/admin/feature-gates", nil)) if unauth.Code != http.StatusUnauthorized { t.Fatalf("unauth status=%d want 401 body=%s", unauth.Code, unauth.Body.String()) } adminPaths := []string{ "/api/admin/plans/1/features", "/api/admin/feature-gates", } for _, path := range adminPaths { mounted := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, path, nil) req.AddCookie(&http.Cookie{Name: sm.Cookie.Name, Value: token}) h.ServeHTTP(mounted, req) if mounted.Code == http.StatusNotFound { t.Fatalf("%s not mounted: status=404 body=%s", path, mounted.Body.String()) } // No DB pool in this unit test — handlers may 500/503/400, but must not 404. if mounted.Code == http.StatusUnauthorized { t.Fatalf("%s: unexpected 401 for platform admin session", path) } } bulkPaths := []struct { method string path string }{ {http.MethodPost, "/api/admin/plans/1/features/enable-all"}, {http.MethodPost, "/api/admin/plans/1/features/disable-all"}, {http.MethodPut, "/api/admin/feature-gates/sections/marketing"}, } for _, tc := range bulkPaths { mounted := httptest.NewRecorder() req := httptest.NewRequest(tc.method, tc.path, nil) req.AddCookie(&http.Cookie{Name: sm.Cookie.Name, Value: token}) h.ServeHTTP(mounted, req) if mounted.Code == http.StatusNotFound { t.Fatalf("%s %s not mounted: status=404", tc.method, tc.path) } } // Tenant capabilities require company context — expect 401 without company selection. caps := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/billing/capabilities", nil) req.AddCookie(&http.Cookie{Name: sm.Cookie.Name, Value: token}) h.ServeHTTP(caps, req) if caps.Code == http.StatusNotFound { t.Fatalf("capabilities not mounted: status=404") } if caps.Code != http.StatusUnauthorized && caps.Code != http.StatusForbidden { // Company middleware may return 401 or 400 depending on setup; not 404. if caps.Code == http.StatusOK { t.Fatalf("capabilities unexpectedly OK without company") } } } func TestRouterPublicPlansMounted(t *testing.T) { t.Parallel() s := &Server{ Config: config.Config{ CSRFCookieName: "descrybe_csrf", WebOrigin: "http://localhost:5173", }, Billing: &billing.Service{}, } h := s.Router() for _, path := range []string{"/api/public/plans", "/api/public/credit-packs"} { rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil)) if rec.Code == http.StatusNotFound { t.Fatalf("%s not mounted: status=404 body=%s", path, rec.Body.String()) } } }