Files
cfw-autumn/server/tests/integration/cron/void-invoice-cron.test.ts
John Yeo 56f3826cc2 wip
2026-02-07 19:57:55 -08:00

102 lines
3.5 KiB
TypeScript

/**
* Void Invoice Cron Tests
*
* Tests that the handleVoidInvoiceCron function correctly voids open invoices
* from failed payment attempts (e.g., 3DS required but not completed).
*
* Migrated from: invoice-action-required2.test.ts
*/
import { expect, test } from "bun:test";
import type { ApiCustomerV3 } from "@autumn/shared";
import { items } from "@tests/utils/fixtures/items";
import { products } from "@tests/utils/fixtures/products";
import ctx from "@tests/utils/testInitUtils/createTestContext";
import { initScenario, s } from "@tests/utils/testInitUtils/initScenario";
import chalk from "chalk";
import { handleVoidInvoiceCron } from "@/cron/invoiceCron/runInvoiceCron";
import { MetadataService } from "@/internal/metadata/MetadataService";
// ═══════════════════════════════════════════════════════════════════════════════
// TEST 1: Void open invoice from failed upgrade
// ═══════════════════════════════════════════════════════════════════════════════
/**
* Scenario:
* - Attach pro with success PM
* - Swap to authenticate PM
* - Upgrade to premium → open invoice (checkout_url returned)
* - Retrieve Stripe invoice, get autumn metadata
* - Call handleVoidInvoiceCron → invoice voided
* - Verify customer reflects voided invoice
*/
test.concurrent(`${chalk.yellowBright("void-invoice-cron 1: void open invoice from failed upgrade")}`, async () => {
const customerId = "void-invoice-cron-upgrade";
const proMessagesItem = items.monthlyMessages({ includedUsage: 200 });
const pro = products.pro({
id: "pro",
items: [proMessagesItem],
});
const premiumMessagesItem = items.monthlyMessages({
includedUsage: 100,
});
const premium = products.premium({
id: "premium",
items: [premiumMessagesItem],
});
const { autumnV1 } = await initScenario({
customerId,
setup: [
s.customer({ paymentMethod: "success" }),
s.products({ list: [pro, premium] }),
],
actions: [
s.attach({ productId: pro.id }),
s.attachPaymentMethod({ type: "authenticate" }),
],
});
// Upgrade to premium — should fail with checkout_url
await autumnV1.attach({
customer_id: customerId,
product_id: premium.id,
});
// Verify open invoice on customer
const customer = await autumnV1.customers.get<ApiCustomerV3>(customerId);
expect(customer.invoices?.[0].status).toBe("open");
// Get the Stripe invoice and its autumn metadata
const stripeInvoices = await ctx.stripeCli.invoices.list({
customer: customer.stripe_id!,
});
const latestInvoice = stripeInvoices.data[0];
expect(latestInvoice.metadata?.autumn_metadata_id).toBeDefined();
const metadata = await MetadataService.get({
db: ctx.db,
id: latestInvoice.metadata?.autumn_metadata_id ?? "",
});
expect(metadata).toBeDefined();
// Run the void invoice cron handler
await handleVoidInvoiceCron({
metadata: metadata!,
ctx,
});
// Verify the Stripe invoice is now voided
const voidedInvoice = await ctx.stripeCli.invoices.retrieve(latestInvoice.id);
expect(voidedInvoice.status).toBe("void");
// // Wait for cache to update, then verify customer reflects voided status
// await timeout(3000);
// const customerAfter = await autumnV1.customers.get<ApiCustomerV3>(customerId);
// expect(customerAfter.invoices?.[0].status).toBe("void");
});