180 lines
6.3 KiB
TypeScript
180 lines
6.3 KiB
TypeScript
/**
|
|
* Custom Line Items Error Tests (Attach V2)
|
|
*
|
|
* Tests that custom_line_items is rejected in invalid scenarios:
|
|
* 1. Creating a new subscription (free → paid)
|
|
* 2. Scheduled switch / downgrade
|
|
* 3. Removing a trial (Stripe-managed invoice)
|
|
*/
|
|
|
|
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";
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// TEST 1: custom_line_items on subscription create should fail
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Scenario:
|
|
* - Customer has free product
|
|
* - Attach pro ($20/mo) with custom_line_items
|
|
*
|
|
* Expected Result:
|
|
* - Error: custom_line_items only valid for subscription updates
|
|
*
|
|
* Why:
|
|
* - Creating a new subscription generates its own Stripe invoice.
|
|
* Custom line items can't override that.
|
|
*/
|
|
test.concurrent(`${chalk.yellowBright("error: custom_line_items on subscription create")}`, async () => {
|
|
const customerId = "cli-err-create-sub";
|
|
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const free = products.base({
|
|
id: "free",
|
|
items: [messagesItem],
|
|
});
|
|
|
|
const proMessagesItem = items.monthlyMessages({ includedUsage: 500 });
|
|
const pro = products.pro({
|
|
id: "pro",
|
|
items: [proMessagesItem],
|
|
});
|
|
|
|
const { autumnV1 } = await initScenario({
|
|
customerId,
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [free, pro] }),
|
|
],
|
|
actions: [s.billing.attach({ productId: free.id })],
|
|
});
|
|
|
|
await expectAutumnError({
|
|
func: async () => {
|
|
await autumnV1.billing.attach({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
redirect_mode: "if_required",
|
|
custom_line_items: [{ amount: 10, description: "Should not work" }],
|
|
});
|
|
},
|
|
errMessage:
|
|
"custom_line_items can only be used when updating an existing subscription",
|
|
});
|
|
});
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// TEST 2: custom_line_items on downgrade (scheduled switch) should fail
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Scenario:
|
|
* - Customer has premium ($50/mo)
|
|
* - Downgrade to pro ($20/mo) with custom_line_items
|
|
*
|
|
* Expected Result:
|
|
* - Error: custom_line_items only valid for subscription updates
|
|
*
|
|
* Why:
|
|
* - Downgrades are scheduled for end of cycle. There is no immediate invoice
|
|
* to override with custom line items.
|
|
*/
|
|
test.concurrent(`${chalk.yellowBright("error: custom_line_items on downgrade (scheduled switch)")}`, async () => {
|
|
const customerId = "cli-err-downgrade";
|
|
|
|
const proMessagesItem = items.monthlyMessages({ includedUsage: 500 });
|
|
const pro = products.pro({
|
|
id: "pro",
|
|
items: [proMessagesItem],
|
|
});
|
|
|
|
const premiumMessagesItem = items.monthlyMessages({ includedUsage: 1000 });
|
|
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.id })],
|
|
});
|
|
|
|
await expectAutumnError({
|
|
func: async () => {
|
|
await autumnV1.billing.attach({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
redirect_mode: "if_required",
|
|
custom_line_items: [{ amount: -10, description: "Should not work" }],
|
|
});
|
|
},
|
|
errMessage:
|
|
"custom_line_items can only be used when updating an existing subscription",
|
|
});
|
|
});
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// TEST 3: custom_line_items on trial removal should fail
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* Scenario:
|
|
* - Customer has proWithTrial (7-day trial, currently trialing)
|
|
* - Upgrade to premium (no trial) with custom_line_items
|
|
*
|
|
* Expected Result:
|
|
* - Error: custom_line_items cannot be used when creating Stripe-managed invoice
|
|
*
|
|
* Why:
|
|
* - When a trial is removed (trialing → non-trialing), Stripe automatically
|
|
* generates an invoice. Custom line items can't override Stripe's invoice.
|
|
*/
|
|
test.concurrent(`${chalk.yellowBright("error: custom_line_items on trial removal")}`, async () => {
|
|
const customerId = "cli-err-trial-removal";
|
|
|
|
const proMessagesItem = items.monthlyMessages({ includedUsage: 500 });
|
|
const proTrial = products.proWithTrial({
|
|
id: "pro-trial",
|
|
items: [proMessagesItem],
|
|
trialDays: 7,
|
|
cardRequired: true,
|
|
});
|
|
|
|
const premiumMessagesItem = items.monthlyMessages({ includedUsage: 1000 });
|
|
const premium = products.premium({
|
|
id: "premium",
|
|
items: [premiumMessagesItem],
|
|
});
|
|
|
|
const { autumnV1 } = await initScenario({
|
|
customerId,
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [proTrial, premium] }),
|
|
],
|
|
actions: [s.billing.attach({ productId: proTrial.id })],
|
|
});
|
|
|
|
await expectAutumnError({
|
|
func: async () => {
|
|
await autumnV1.billing.attach({
|
|
customer_id: customerId,
|
|
product_id: premium.id,
|
|
redirect_mode: "if_required",
|
|
custom_line_items: [{ amount: 50, description: "Should not work" }],
|
|
});
|
|
},
|
|
errMessage:
|
|
"custom_line_items cannot be used when the subscription update creates a Stripe-managed invoice",
|
|
});
|
|
});
|