mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-21 19:39:28 -05:00
fix: restrict selected entitlements during trial
This commit is contained in:
@@ -31,6 +31,7 @@ const baseContext: TOrganizationEntitlementsContext = {
|
||||
licenseStatus: "no-license",
|
||||
licenseFeatures: null,
|
||||
stripeCustomerId: "cus_1",
|
||||
subscriptionStatus: null,
|
||||
usageCycleAnchor: null,
|
||||
};
|
||||
|
||||
@@ -61,6 +62,33 @@ describe("hasOrganizationEntitlementWithLicenseGuard", () => {
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "rbac")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for trial-restricted follow-ups while trialing", async () => {
|
||||
mockGetContext.mockResolvedValue({
|
||||
...baseContext,
|
||||
features: ["follow-ups"],
|
||||
subscriptionStatus: "trialing",
|
||||
});
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "follow-ups")).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false for trial-restricted custom links while trialing", async () => {
|
||||
mockGetContext.mockResolvedValue({
|
||||
...baseContext,
|
||||
features: ["custom-links-in-surveys"],
|
||||
subscriptionStatus: "trialing",
|
||||
});
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "custom-links-in-surveys")).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false for trial-restricted custom redirect while trialing", async () => {
|
||||
mockGetContext.mockResolvedValue({
|
||||
...baseContext,
|
||||
features: ["custom-redirect-url"],
|
||||
subscriptionStatus: "trialing",
|
||||
});
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "custom-redirect-url")).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when feature not present", async () => {
|
||||
mockGetContext.mockResolvedValue(baseContext);
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "hide-branding")).toBe(false);
|
||||
@@ -102,10 +130,20 @@ describe("hasOrganizationEntitlementWithLicenseGuard", () => {
|
||||
...baseContext,
|
||||
features: ["custom-redirect-url"],
|
||||
licenseStatus: "active",
|
||||
subscriptionStatus: "active",
|
||||
licenseFeatures: {} as TOrganizationEntitlementsContext["licenseFeatures"],
|
||||
});
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "custom-redirect-url")).toBe(true);
|
||||
});
|
||||
|
||||
test("does not affect unrelated features while trialing", async () => {
|
||||
mockGetContext.mockResolvedValue({
|
||||
...baseContext,
|
||||
features: ["rbac"],
|
||||
subscriptionStatus: "trialing",
|
||||
});
|
||||
expect(await hasOrganizationEntitlementWithLicenseGuard("org1", "rbac")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOrganizationEntitlementLimits", () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import "server-only";
|
||||
import { CLOUD_STRIPE_FEATURE_LOOKUP_KEYS } from "@/modules/billing/lib/stripe-catalog";
|
||||
import type { TEnterpriseLicenseFeatures } from "@/modules/ee/license-check/types/enterprise-license";
|
||||
import { getOrganizationEntitlementsContext } from "./provider";
|
||||
import { isEntitlementFeature } from "./types";
|
||||
@@ -11,6 +12,12 @@ const LICENSE_GUARDED_ENTITLEMENTS: Partial<Record<string, keyof TEnterpriseLice
|
||||
contacts: "contacts",
|
||||
};
|
||||
|
||||
const TRIAL_RESTRICTED_ENTITLEMENTS = new Set<string>([
|
||||
CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.FOLLOW_UPS,
|
||||
CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.CUSTOM_LINKS_IN_SURVEYS,
|
||||
CLOUD_STRIPE_FEATURE_LOOKUP_KEYS.CUSTOM_REDIRECT_URL,
|
||||
]);
|
||||
|
||||
export const hasOrganizationEntitlement = async (
|
||||
organizationId: string,
|
||||
featureLookupKey: string
|
||||
@@ -37,6 +44,14 @@ export const hasOrganizationEntitlementWithLicenseGuard = async (
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
context.source === "cloud_stripe" &&
|
||||
context.subscriptionStatus === "trialing" &&
|
||||
TRIAL_RESTRICTED_ENTITLEMENTS.has(featureLookupKey)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.licenseStatus === "no-license") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ describe("getCloudOrganizationEntitlementsContext", () => {
|
||||
licenseStatus: "no-license",
|
||||
licenseFeatures: null,
|
||||
stripeCustomerId: "cus_1",
|
||||
subscriptionStatus: null,
|
||||
usageCycleAnchor,
|
||||
});
|
||||
});
|
||||
@@ -67,6 +68,7 @@ describe("getCloudOrganizationEntitlementsContext", () => {
|
||||
expect(result.features).toEqual([]);
|
||||
expect(result.limits).toEqual({ projects: null, monthlyResponses: null });
|
||||
expect(result.stripeCustomerId).toBeNull();
|
||||
expect(result.subscriptionStatus).toBeNull();
|
||||
expect(result.usageCycleAnchor).toBeNull();
|
||||
});
|
||||
|
||||
@@ -97,4 +99,18 @@ describe("getCloudOrganizationEntitlementsContext", () => {
|
||||
|
||||
expect(result.features).toEqual(["rbac"]);
|
||||
});
|
||||
|
||||
test("exposes subscription status from billing stripe snapshot", async () => {
|
||||
mockGetBilling.mockResolvedValue({
|
||||
stripeCustomerId: "cus_1",
|
||||
limits: { projects: 5, monthly: { responses: 1000 } },
|
||||
usageCycleAnchor: null,
|
||||
stripe: { features: ["follow-ups"], subscriptionStatus: "trialing" },
|
||||
} as any);
|
||||
mockGetLicense.mockResolvedValue({ status: "no-license", features: null, active: false });
|
||||
|
||||
const result = await getCloudOrganizationEntitlementsContext("org1");
|
||||
|
||||
expect(result.subscriptionStatus).toBe("trialing");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ export const getCloudOrganizationEntitlementsContext = async (
|
||||
licenseStatus: license.status,
|
||||
licenseFeatures: license.features,
|
||||
stripeCustomerId: billing.stripeCustomerId ?? null,
|
||||
subscriptionStatus: billing.stripe?.subscriptionStatus ?? null,
|
||||
usageCycleAnchor: toDateOrNull(billing.usageCycleAnchor),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ describe("getSelfHostedOrganizationEntitlementsContext", () => {
|
||||
licenseStatus: "no-license",
|
||||
licenseFeatures: null,
|
||||
stripeCustomerId: null,
|
||||
subscriptionStatus: null,
|
||||
usageCycleAnchor: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,7 @@ export const getSelfHostedOrganizationEntitlementsContext = async (
|
||||
licenseStatus: license.status,
|
||||
licenseFeatures: license.features,
|
||||
stripeCustomerId: null,
|
||||
subscriptionStatus: null,
|
||||
usageCycleAnchor: null,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { TOrganizationStripeSubscriptionStatus } from "@formbricks/types/organizations";
|
||||
import { CLOUD_STRIPE_FEATURE_LOOKUP_KEYS } from "@/modules/billing/lib/stripe-catalog";
|
||||
import type {
|
||||
TEnterpriseLicenseFeatures,
|
||||
@@ -35,5 +36,6 @@ export type TOrganizationEntitlementsContext = {
|
||||
licenseStatus: TEnterpriseLicenseStatusReturn;
|
||||
licenseFeatures: TEnterpriseLicenseFeatures | null;
|
||||
stripeCustomerId: string | null;
|
||||
subscriptionStatus: TOrganizationStripeSubscriptionStatus | null;
|
||||
usageCycleAnchor: Date | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user