223 lines
7.0 KiB
TypeScript
223 lines
7.0 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { ErrCode } from "@autumn/shared";
|
|
import { expectCustomerInvoiceCorrect } from "@tests/integration/billing/utils/expectCustomerInvoiceCorrect";
|
|
import { expectSubToBeCorrect } from "@tests/merged/mergeUtils/expectSubCorrect";
|
|
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";
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// VERSION ERRORS: Error handling for version update edge cases
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
// 3.1 Invalid version: version 99 doesn't exist
|
|
test.concurrent(`${chalk.yellowBright("version-errors: invalid version 404")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { customerId, autumnV1 } = await initScenario({
|
|
customerId: "version-err-invalid",
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [pro] }),
|
|
],
|
|
actions: [s.attach({ productId: "pro" })],
|
|
});
|
|
|
|
// Try to update to version 99 (doesn't exist)
|
|
await expectAutumnError({
|
|
errCode: ErrCode.ProductNotFound,
|
|
func: async () => {
|
|
await autumnV1.subscriptions.update({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: 99,
|
|
});
|
|
},
|
|
});
|
|
});
|
|
|
|
// 3.2 Preview with invalid version also returns 404
|
|
test.concurrent(`${chalk.yellowBright("version-errors: preview invalid version 404")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { customerId, autumnV1 } = await initScenario({
|
|
customerId: "version-err-preview-invalid",
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [pro] }),
|
|
],
|
|
actions: [s.attach({ productId: "pro" })],
|
|
});
|
|
|
|
// Try to preview with version 99 (doesn't exist)
|
|
await expectAutumnError({
|
|
errCode: ErrCode.ProductNotFound,
|
|
func: async () => {
|
|
await autumnV1.subscriptions.previewUpdate({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: 99,
|
|
});
|
|
},
|
|
});
|
|
});
|
|
|
|
// 3.3 Negative version is invalid
|
|
test.concurrent(`${chalk.yellowBright("version-errors: negative version invalid")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { customerId, autumnV1 } = await initScenario({
|
|
customerId: "version-err-negative",
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [pro] }),
|
|
],
|
|
actions: [s.attach({ productId: "pro" })],
|
|
});
|
|
|
|
// Try to update to version -1 (invalid)
|
|
await expectAutumnError({
|
|
errCode: ErrCode.ProductNotFound,
|
|
func: async () => {
|
|
await autumnV1.subscriptions.update({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: -1,
|
|
});
|
|
},
|
|
});
|
|
});
|
|
|
|
// 3.4 Update version for non-existent customer
|
|
test.concurrent(`${chalk.yellowBright("version-errors: non-existent customer")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { autumnV1 } = await initScenario({
|
|
customerId: "version-err-no-customer",
|
|
setup: [s.products({ list: [pro] })],
|
|
actions: [],
|
|
});
|
|
|
|
// Try to update version for customer that doesn't exist
|
|
await expectAutumnError({
|
|
errCode: ErrCode.CustomerNotFound,
|
|
func: async () => {
|
|
await autumnV1.subscriptions.update({
|
|
customer_id: "non-existent-customer-id",
|
|
product_id: pro.id,
|
|
version: 1,
|
|
});
|
|
},
|
|
});
|
|
});
|
|
|
|
// 3.5 Version downgrade (v2 -> v1) is allowed
|
|
test.concurrent(`${chalk.yellowBright("version-errors: downgrade v2 to v1 valid")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { customerId, autumnV1, ctx } = await initScenario({
|
|
customerId: "version-err-downgrade",
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [pro] }),
|
|
],
|
|
actions: [s.attach({ productId: "pro" })],
|
|
});
|
|
|
|
// Create v2 with higher price
|
|
const priceItemV2 = items.monthlyPrice({ price: 30 });
|
|
await autumnV1.products.update(pro.id, {
|
|
items: [messagesItem, priceItemV2],
|
|
});
|
|
|
|
// Update to v2 first
|
|
await autumnV1.subscriptions.update({
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: 2,
|
|
});
|
|
|
|
// Now downgrade back to v1
|
|
const updateParams = {
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: 1,
|
|
};
|
|
|
|
const preview = await autumnV1.subscriptions.previewUpdate(updateParams);
|
|
|
|
// Should credit $10 difference ($20 - $30)
|
|
expect(preview.total).toBe(-10);
|
|
|
|
await autumnV1.subscriptions.update(updateParams);
|
|
|
|
await expectCustomerInvoiceCorrect({
|
|
customerId,
|
|
count: 3, // Initial + upgrade to v2 + downgrade to v1
|
|
latestTotal: preview.total,
|
|
});
|
|
|
|
await expectSubToBeCorrect({
|
|
db: ctx.db,
|
|
customerId,
|
|
org: ctx.org,
|
|
env: ctx.env,
|
|
});
|
|
});
|
|
|
|
// 3.6 Same version update processes normally (no special case)
|
|
test.concurrent(`${chalk.yellowBright("version-errors: same version processes normally")}`, async () => {
|
|
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
|
|
const priceItem = items.monthlyPrice({ price: 20 });
|
|
const pro = products.base({ id: "pro", items: [messagesItem, priceItem] });
|
|
|
|
const { customerId, autumnV1, ctx } = await initScenario({
|
|
customerId: "version-err-same",
|
|
setup: [
|
|
s.customer({ paymentMethod: "success" }),
|
|
s.products({ list: [pro] }),
|
|
],
|
|
actions: [s.attach({ productId: "pro" })],
|
|
});
|
|
|
|
// Update to version 1 (current version) - processes normally
|
|
const updateParams = {
|
|
customer_id: customerId,
|
|
product_id: pro.id,
|
|
version: 1,
|
|
};
|
|
|
|
const preview = await autumnV1.subscriptions.previewUpdate(updateParams);
|
|
|
|
// No price change for same version
|
|
expect(preview.total).toBe(0);
|
|
|
|
await autumnV1.subscriptions.update(updateParams);
|
|
|
|
// No second invoice created for same version (no price change)
|
|
await expectCustomerInvoiceCorrect({
|
|
customerId,
|
|
count: 1,
|
|
latestTotal: 20,
|
|
});
|
|
|
|
await expectSubToBeCorrect({
|
|
db: ctx.db,
|
|
customerId,
|
|
org: ctx.org,
|
|
env: ctx.env,
|
|
});
|
|
});
|