package httpapi import ( "testing" "github.com/descrybe/descrybe-v2/apps/api/internal/billing" ) func TestFeatureKeyForAPIPath(t *testing.T) { cases := []struct { path string want string }{ {"/api/products", "catalog.products"}, {"/api/products/", "catalog.products"}, {"/api/products/8f14e45f-ceea-467a-9e0f-000000000000", "catalog.products"}, {"/api/api-keys", "settings.api_keys"}, {"/api/api-keys/42", "settings.api_keys"}, {"/api/marketing/calendar", "marketing.content_calendar"}, {"/api/marketing/calendar/prepare", "marketing.content_calendar"}, {"/api/integrations/ai/prompts", "integrations.ai"}, {"/api/integrations/email/test", "integrations.email"}, {"/api/woocommerce/orders", "stores.woocommerce"}, {"/api/billing/checkout", "billing.checkout"}, // Unmapped paths stay open — the middleware must not guess. {"/api/auth/me", ""}, {"/api/billing/credits", ""}, {"/api/billing/capabilities", ""}, {"/api/unknown-thing", ""}, {"/api", ""}, } for _, tc := range cases { if got := featureKeyForAPIPath(tc.path); got != tc.want { t.Errorf("featureKeyForAPIPath(%q) = %q, want %q", tc.path, got, tc.want) } } } // A prefix must never match a longer sibling path segment: /api/feeds governs // feeds.list, but /api/feeds-something-else is not a feeds route. func TestFeatureKeyForAPIPathDoesNotMatchPartialSegments(t *testing.T) { if got := featureKeyForAPIPath("/api/feedsomething"); got != "" { t.Errorf("got %q, want empty", got) } if got := featureKeyForAPIPath("/api/products-export"); got != "" { t.Errorf("got %q, want empty", got) } } // Every key the middleware enforces must be one the owner can actually toggle, // otherwise a route could be blocked with no way to unblock it. func TestAPIFeatureRoutesAreGrantable(t *testing.T) { for path, key := range apiFeatureRoutes { if !billing.IsKnownFeatureKey(key) { t.Errorf("%s maps to unknown feature key %q", path, key) continue } if !billing.IsGrantableFeatureKey(key) { t.Errorf("%s maps to %q, which the owner cannot toggle", path, key) } } } // The capability matrix itself must stay reachable — it is what tells the // dashboard which surfaces to hide. func TestAlwaysReachablePathsAreNotGated(t *testing.T) { for _, path := range alwaysReachableAPIPaths { if got := featureKeyForAPIPath(path); got != "" { t.Errorf("%s resolved to feature %q but must stay open", path, got) } } }