mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-22 19:39:01 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 046c56b9dd |
@@ -33,14 +33,14 @@ describe("Password Management", () => {
|
|||||||
const hashedPassword = await hashPassword(password);
|
const hashedPassword = await hashPassword(password);
|
||||||
const isValid = await verifyPassword(password, hashedPassword);
|
const isValid = await verifyPassword(password, hashedPassword);
|
||||||
expect(isValid).toBe(true);
|
expect(isValid).toBe(true);
|
||||||
});
|
}, 15000);
|
||||||
|
|
||||||
test("verifyPassword should reject an incorrect password", async () => {
|
test("verifyPassword should reject an incorrect password", async () => {
|
||||||
const password = "testPassword123";
|
const password = "testPassword123";
|
||||||
const hashedPassword = await hashPassword(password);
|
const hashedPassword = await hashPassword(password);
|
||||||
const isValid = await verifyPassword("wrongPassword", hashedPassword);
|
const isValid = await verifyPassword("wrongPassword", hashedPassword);
|
||||||
expect(isValid).toBe(false);
|
expect(isValid).toBe(false);
|
||||||
});
|
}, 15000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Organization Access", () => {
|
describe("Organization Access", () => {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("Crypto Utils", () => {
|
|||||||
|
|
||||||
const isValid = await verifySecret(secret, hash);
|
const isValid = await verifySecret(secret, hash);
|
||||||
expect(isValid).toBe(true);
|
expect(isValid).toBe(true);
|
||||||
});
|
}, 15000);
|
||||||
|
|
||||||
test("should reject wrong secrets", async () => {
|
test("should reject wrong secrets", async () => {
|
||||||
const secret = "test-secret-123";
|
const secret = "test-secret-123";
|
||||||
@@ -43,7 +43,7 @@ describe("Crypto Utils", () => {
|
|||||||
|
|
||||||
const isValid = await verifySecret(wrongSecret, hash);
|
const isValid = await verifySecret(wrongSecret, hash);
|
||||||
expect(isValid).toBe(false);
|
expect(isValid).toBe(false);
|
||||||
});
|
}, 15000);
|
||||||
|
|
||||||
test("should generate different hashes for the same secret (due to salt)", async () => {
|
test("should generate different hashes for the same secret (due to salt)", async () => {
|
||||||
const secret = "test-secret-123";
|
const secret = "test-secret-123";
|
||||||
@@ -64,7 +64,7 @@ describe("Crypto Utils", () => {
|
|||||||
// Verify the cost factor is in the hash
|
// Verify the cost factor is in the hash
|
||||||
expect(hash).toMatch(/^\$2[aby]\$10\$/);
|
expect(hash).toMatch(/^\$2[aby]\$10\$/);
|
||||||
expect(await verifySecret(secret, hash)).toBe(true);
|
expect(await verifySecret(secret, hash)).toBe(true);
|
||||||
});
|
}, 15000);
|
||||||
|
|
||||||
test("should return false for invalid hash format", async () => {
|
test("should return false for invalid hash format", async () => {
|
||||||
const secret = "test-secret-123";
|
const secret = "test-secret-123";
|
||||||
|
|||||||
@@ -1021,6 +1021,6 @@ describe("updateSurveyDraftAction", () => {
|
|||||||
|
|
||||||
// Expect validation error (skipValidation = false)
|
// Expect validation error (skipValidation = false)
|
||||||
await expect(updateSurveyInternal(incompleteSurvey, false)).rejects.toThrow();
|
await expect(updateSurveyInternal(incompleteSurvey, false)).rejects.toThrow();
|
||||||
});
|
}, 15000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,6 +159,12 @@ describe("organization-billing", () => {
|
|||||||
mocks.getCloudPlanFromProduct.mockReturnValue("pro");
|
mocks.getCloudPlanFromProduct.mockReturnValue("pro");
|
||||||
mocks.subscriptionsList.mockResolvedValue({ data: [] });
|
mocks.subscriptionsList.mockResolvedValue({ data: [] });
|
||||||
mocks.customersList.mockResolvedValue({ data: [] });
|
mocks.customersList.mockResolvedValue({ data: [] });
|
||||||
|
mocks.customersRetrieve.mockResolvedValue({
|
||||||
|
id: "cus_1",
|
||||||
|
deleted: false,
|
||||||
|
invoice_settings: { default_payment_method: null },
|
||||||
|
default_source: null,
|
||||||
|
});
|
||||||
mocks.prismaMembershipFindFirst.mockResolvedValue(null);
|
mocks.prismaMembershipFindFirst.mockResolvedValue(null);
|
||||||
mocks.productsList.mockResolvedValue({
|
mocks.productsList.mockResolvedValue({
|
||||||
data: [
|
data: [
|
||||||
@@ -639,6 +645,64 @@ describe("organization-billing", () => {
|
|||||||
expect(mocks.cacheDel).toHaveBeenCalledWith(["billing-cache-key"]);
|
expect(mocks.cacheDel).toHaveBeenCalledWith(["billing-cache-key"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("syncOrganizationBillingFromStripe marks migrated customers with customer-level payment methods", async () => {
|
||||||
|
mocks.prismaOrganizationBillingFindUnique.mockResolvedValue({
|
||||||
|
stripeCustomerId: "cus_1",
|
||||||
|
limits: {
|
||||||
|
projects: 3,
|
||||||
|
monthly: {
|
||||||
|
responses: 1500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usageCycleAnchor: new Date(),
|
||||||
|
stripe: { lastSyncedEventId: null },
|
||||||
|
});
|
||||||
|
mocks.subscriptionsList.mockResolvedValue({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: "sub_1",
|
||||||
|
status: "active",
|
||||||
|
default_payment_method: null,
|
||||||
|
billing_cycle_anchor: 1739923200,
|
||||||
|
items: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
price: {
|
||||||
|
metadata: {},
|
||||||
|
product: { id: "prod_pro", metadata: { formbricks_plan: "pro" } },
|
||||||
|
recurring: { usage_type: "licensed", interval: "month" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
mocks.customersRetrieve.mockResolvedValue({
|
||||||
|
id: "cus_1",
|
||||||
|
deleted: false,
|
||||||
|
invoice_settings: { default_payment_method: "pm_legacy_default" },
|
||||||
|
default_source: null,
|
||||||
|
});
|
||||||
|
mocks.entitlementsList.mockResolvedValue({
|
||||||
|
data: [],
|
||||||
|
has_more: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await syncOrganizationBillingFromStripe("org_1");
|
||||||
|
|
||||||
|
expect(result?.stripe?.hasPaymentMethod).toBe(true);
|
||||||
|
expect(mocks.prismaOrganizationBillingUpdate).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: expect.objectContaining({
|
||||||
|
stripe: expect.objectContaining({
|
||||||
|
hasPaymentMethod: true,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("createPaidPlanCheckoutSession rejects mixed-interval yearly checkout", async () => {
|
test("createPaidPlanCheckoutSession rejects mixed-interval yearly checkout", async () => {
|
||||||
await expect(
|
await expect(
|
||||||
createPaidPlanCheckoutSession({
|
createPaidPlanCheckoutSession({
|
||||||
|
|||||||
@@ -1107,6 +1107,21 @@ const resolvePendingPlanChange = async (subscription: Stripe.Subscription | null
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resolveHasPaymentMethod = (
|
||||||
|
subscription: Stripe.Subscription | null,
|
||||||
|
customer: Stripe.Customer | Stripe.DeletedCustomer
|
||||||
|
) => {
|
||||||
|
if (subscription?.default_payment_method != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customer.deleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return customer.invoice_settings.default_payment_method != null || customer.default_source != null;
|
||||||
|
};
|
||||||
|
|
||||||
export const syncOrganizationBillingFromStripe = async (
|
export const syncOrganizationBillingFromStripe = async (
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
event?: { id: string; created: number }
|
event?: { id: string; created: number }
|
||||||
@@ -1132,9 +1147,10 @@ export const syncOrganizationBillingFromStripe = async (
|
|||||||
return billing;
|
return billing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [subscription, featureLookupKeys] = await Promise.all([
|
const [subscription, featureLookupKeys, customer] = await Promise.all([
|
||||||
resolveCurrentSubscription(customerId),
|
resolveCurrentSubscription(customerId),
|
||||||
listAllActiveEntitlements(customerId),
|
listAllActiveEntitlements(customerId),
|
||||||
|
stripeClient.customers.retrieve(customerId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const cloudPlan = resolveCloudPlanFromSubscription(subscription);
|
const cloudPlan = resolveCloudPlanFromSubscription(subscription);
|
||||||
@@ -1160,7 +1176,7 @@ export const syncOrganizationBillingFromStripe = async (
|
|||||||
interval: billingInterval,
|
interval: billingInterval,
|
||||||
subscriptionStatus,
|
subscriptionStatus,
|
||||||
subscriptionId: subscription?.id ?? null,
|
subscriptionId: subscription?.id ?? null,
|
||||||
hasPaymentMethod: subscription?.default_payment_method != null,
|
hasPaymentMethod: resolveHasPaymentMethod(subscription, customer),
|
||||||
features: featureLookupKeys,
|
features: featureLookupKeys,
|
||||||
pendingChange,
|
pendingChange,
|
||||||
lastStripeEventCreatedAt: toIsoStringOrNull(incomingEventDate ?? previousEventDate),
|
lastStripeEventCreatedAt: toIsoStringOrNull(incomingEventDate ?? previousEventDate),
|
||||||
|
|||||||
Reference in New Issue
Block a user