Files
descrybe/apps/api/internal/httpapi/member_feature_middleware_test.go
T
2026-08-22 18:51:17 +02:00

73 lines
2.4 KiB
Go

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)
}
}
}