Files
cfw-autumn/server/tests/integration/billing/attach/errors/attach-invoice-errors.test.ts
2026-02-04 09:56:31 +00:00

185 lines
5.9 KiB
TypeScript

/**
* Attach Invoice Mode Error Tests
*
* Tests for validation errors when using invoice mode with incompatible configurations.
*
* Key behaviors:
* - Invoice mode with deferred activation (enable_product_immediately=false) cannot be used for downgrades
*/
import { test } from "bun:test";
import { expectAutumnError } from "@tests/utils/expectUtils/expectErrUtils";
import { items } from "@tests/utils/fixtures/items";
import { products } from "@tests/utils/fixtures/products";
import { initScenario, s } from "@tests/utils/testInitUtils/initScenario";
import chalk from "chalk";
// ═══════════════════════════════════════════════════════════════════════════════
// INVOICE MODE ERRORS
// ═══════════════════════════════════════════════════════════════════════════════
/**
* Test 1: Invoice mode deferred + downgrade is rejected
*
* Scenario:
* - Customer has premium ($50/mo)
* - Downgrade to pro ($20/mo) with invoice mode (deferred)
*
* Expected:
* - Error: Cannot use invoice mode with deferred activation for downgrades
*
* Reason:
* Downgrades are scheduled for end of cycle (planTiming="end_of_cycle") and
* there is no immediate invoice to pay. Deferred activation (waiting for payment)
* makes no sense in this context.
*/
test.concurrent(`${chalk.yellowBright("error: invoice mode deferred downgrade rejected")}`, async () => {
const customerId = "err-inv-deferred-downgrade";
const proMessagesItem = items.monthlyMessages({ includedUsage: 100 });
const pro = products.pro({
id: "pro",
items: [proMessagesItem],
});
const premiumMessagesItem = items.monthlyMessages({ includedUsage: 500 });
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.billing.attach({ productId: "premium" })],
});
// After initScenario, pro.id and premium.id are mutated to include the prefix
// Try to downgrade with invoice mode deferred - should fail
await expectAutumnError({
func: async () => {
await autumnV1.billing.attach({
customer_id: customerId,
product_id: pro.id,
invoice: true,
finalize_invoice: true,
enable_product_immediately: false, // Deferred - NOT allowed for downgrades
redirect_mode: "if_required",
});
},
errMessage:
"Cannot use invoice mode with deferred activation for downgrades",
});
});
/**
* Test 2: Invoice mode deferred + downgrade (draft invoice) is also rejected
*
* Scenario:
* - Customer has premium ($50/mo)
* - Downgrade to pro ($20/mo) with invoice mode (draft, deferred)
*
* Expected:
* - Error: Cannot use invoice mode with deferred activation for downgrades
*
* Same as test 1 but with finalize_invoice=false
*/
test.concurrent(`${chalk.yellowBright("error: invoice mode draft deferred downgrade rejected")}`, async () => {
const customerId = "err-inv-draft-deferred-downgrade";
const proMessagesItem = items.monthlyMessages({ includedUsage: 100 });
const pro = products.pro({
id: "pro",
items: [proMessagesItem],
});
const premiumMessagesItem = items.monthlyMessages({ includedUsage: 500 });
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.billing.attach({ productId: "premium" })],
});
// After initScenario, pro.id and premium.id are mutated to include the prefix
// Try to downgrade with invoice mode draft + deferred - should fail
await expectAutumnError({
func: async () => {
await autumnV1.billing.attach({
customer_id: customerId,
product_id: pro.id,
invoice: true,
finalize_invoice: false, // Draft invoice
enable_product_immediately: false, // Deferred - NOT allowed for downgrades
redirect_mode: "if_required",
});
},
errMessage:
"Cannot use invoice mode with deferred activation for downgrades",
});
});
/**
* Test 3: Invoice mode immediate + downgrade is allowed (control test)
*
* Scenario:
* - Customer has premium ($50/mo)
* - Downgrade to pro ($20/mo) with invoice mode (immediate)
*
* Expected:
* - No error - immediate activation is fine for downgrades
* - Premium should be canceling, pro should be scheduled
*/
test.concurrent(`${chalk.yellowBright("control: invoice mode immediate downgrade allowed")}`, async () => {
const customerId = "ctrl-inv-immediate-downgrade";
const proMessagesItem = items.monthlyMessages({ includedUsage: 100 });
const pro = products.pro({
id: "pro",
items: [proMessagesItem],
});
const premiumMessagesItem = items.monthlyMessages({ includedUsage: 500 });
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.billing.attach({ productId: "premium" })],
});
// After initScenario, pro.id and premium.id are mutated to include the prefix
// Downgrade with invoice mode immediate - should work
const result = await autumnV1.billing.attach({
customer_id: customerId,
product_id: pro.id,
invoice: true,
finalize_invoice: true,
enable_product_immediately: true, // Immediate - allowed for downgrades
redirect_mode: "if_required",
});
// Should succeed - no error thrown
// For downgrades, invoice/payment_url are not returned (no immediate charge)
// This is tested in the main invoice tests
});