Files
cfw-autumn/server/tests/basic/03_cancel.ts
2025-05-27 17:34:12 +01:00

212 lines
5.8 KiB
TypeScript

import { createStripeCli } from "@/external/stripe/utils.js";
import { AutumnCli } from "../cli/AutumnCli.js";
import { features, products } from "../global.js";
import { initCustomer } from "../utils/init.js";
import { timeout } from "../utils/genUtils.js";
import chalk from "chalk";
import {
checkFeatureHasCorrectBalance,
compareMainProduct,
compareProductEntitlements,
} from "../utils/compare.js";
import { expect } from "chai";
import {
advanceTestClock,
completeCheckoutForm,
} from "../utils/stripeUtils.js";
import { CusProductStatus } from "@autumn/shared";
import { attachFailedPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
import { addDays, addMonths } from "date-fns";
describe(`${chalk.yellowBright(
"03_cancel: Testing cancel (at period end and now)",
)}`, () => {
const customerId = "cancelCustomer";
before(async function () {
this.timeout(30000);
await initCustomer({
customer_data: {
id: customerId,
name: "Test Customer",
email: "test@test.com",
},
db: this.db,
org: this.org,
env: this.env,
});
});
it("should attach pro product", async function () {
this.timeout(30000);
const res: any = await AutumnCli.attach({
customerId: customerId,
productId: products.pro.id,
});
await completeCheckoutForm(res.checkout_url);
await timeout(5000);
console.log(` ${chalk.greenBright("Attached pro product")}`);
});
// 1. Cancel pro product
it("should cancel pro product (at period end)", async function () {
this.timeout(10000);
const stripeCli = createStripeCli({ org: this.org, env: this.env });
// 1. Cancel pro product
const cusRes: any = await AutumnCli.getCustomer(customerId);
const proProduct = cusRes.products.find(
(p: any) => p.id === products.pro.id,
);
for (const subId of proProduct.subscription_ids) {
await stripeCli.subscriptions.update(subId, {
cancel_at_period_end: true,
});
}
await timeout(3000);
console.log(` ${chalk.greenBright("Cancelled pro product")}`);
});
it("should have pro product active, and canceled_at != null", async function () {
this.timeout(10000);
const cusRes: any = await AutumnCli.getCustomer(customerId);
compareMainProduct({
sent: products.pro,
cusRes: cusRes,
});
const proProduct = cusRes.products.find(
(p: any) => p.id === products.pro.id,
);
expect(proProduct.canceled_at).to.not.equal(null);
expect(proProduct.status).to.equal(CusProductStatus.Active);
});
// CANCEL SUB NOW, SUBSCRIPTION.DELETED WEBHOOK
it("should cancel pro product (now)", async function () {
this.timeout(10000);
const stripeCli = createStripeCli({ org: this.org, env: this.env });
const cusRes: any = await AutumnCli.getCustomer(customerId);
const proProduct = cusRes.products.find(
(p: any) => p.id === products.pro.id,
);
for (const subId of proProduct.subscription_ids) {
await stripeCli.subscriptions.cancel(subId);
}
await timeout(3000);
console.log(` ${chalk.greenBright("Cancelled pro product now")}`);
});
it("should have free product active, and pro product not returned", async function () {
this.timeout(10000);
const cusRes: any = await AutumnCli.getCustomer(customerId);
compareMainProduct({
sent: products.free,
cusRes: cusRes,
});
});
it("should have correct entitlements (for free)", async function () {
for (const entitlement of Object.values(products.free.entitlements)) {
let feature = features[entitlement.feature_id!];
await checkFeatureHasCorrectBalance({
customerId,
feature: feature,
entitlement,
expectedBalance: entitlement.allowance || 0,
});
}
});
});
describe(`${chalk.yellowBright(
"03_cancel: Testing subscription past_due",
)}`, () => {
const customerId = "03_cancel_past_due";
before(async function () {
this.timeout(30000);
const stripeCli = createStripeCli({ org: this.org, env: this.env });
const testClock = await stripeCli.testHelpers.testClocks.create({
frozen_time: Math.round(Date.now() / 1000),
});
this.testClockId = testClock.id;
await initCustomer({
customer_data: {
id: customerId,
name: "Test Customer",
email: "test@test.com",
},
db: this.db,
org: this.org,
env: this.env,
attachPm: true,
testClockId: testClock.id,
});
});
it("should attach pro product", async function () {
this.timeout(10000);
await AutumnCli.attach({
customerId: customerId,
productId: products.pro.id,
});
});
it("should attach failed payment method and advance to next billing date", async function () {
// 1. Swap customer's card
const stripeCli = createStripeCli({ org: this.org, env: this.env });
const cusRes: any = await AutumnCli.getCustomer(customerId);
await attachFailedPaymentMethod({
stripeCli,
customer: cusRes.customer,
});
// const advanceDate = addDays(addMonths(new Date(), 1), 1);
// await stripeCli.testHelpers.testClocks.advance(this.testClockId, {
// frozen_time: Math.round(advanceDate.getTime() / 1000),
// });
await advanceTestClock({
stripeCli,
testClockId: this.testClockId,
advanceTo: addDays(addMonths(new Date(), 1), 1).getTime(),
});
});
it("should have free product active and correct entitlements", async function () {
const cusRes: any = await AutumnCli.getCustomer(customerId);
// compareMainProduct({
// sent: products.free,
// cusRes: cusRes,
// });
// TODO: Check why this line messes up the test
// compareProductEntitlements({
// customerId,
// product: products.free,
// features,
// });
});
});