fix: 🐛 clean up
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Unit tests for filterCustomerProductsByProcessorType.
|
||||
*
|
||||
* Critical invariant: an unset `processor` field on a cus product MUST be
|
||||
* treated as Stripe. Legacy Stripe-managed cus products do not tag the
|
||||
* processor field; only RevenueCat-managed products explicitly set it.
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
filterCustomerProductsByProcessorType,
|
||||
type FullCusProduct,
|
||||
ProcessorType,
|
||||
} from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
|
||||
const baseCusProduct = (id: string): FullCusProduct =>
|
||||
({
|
||||
id,
|
||||
product: { name: id } as FullCusProduct["product"],
|
||||
}) as FullCusProduct;
|
||||
|
||||
describe(
|
||||
chalk.yellowBright("filterCustomerProductsByProcessorType"),
|
||||
() => {
|
||||
test("includes cus product with unset processor when filtering for Stripe", () => {
|
||||
const cp = baseCusProduct("legacy_stripe");
|
||||
// processor is undefined — legacy data shape
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.Stripe,
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe("legacy_stripe");
|
||||
});
|
||||
|
||||
test("includes cus product with null processor when filtering for Stripe", () => {
|
||||
const cp = {
|
||||
...baseCusProduct("null_stripe"),
|
||||
processor: null,
|
||||
} as unknown as FullCusProduct;
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.Stripe,
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("includes cus product with explicit Stripe processor when filtering for Stripe", () => {
|
||||
const cp = {
|
||||
...baseCusProduct("explicit_stripe"),
|
||||
processor: { type: ProcessorType.Stripe },
|
||||
} as FullCusProduct;
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.Stripe,
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("excludes cus product with unset processor when filtering for RevenueCat", () => {
|
||||
const cp = baseCusProduct("legacy_stripe_excluded");
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.RevenueCat,
|
||||
});
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("includes cus product with explicit RevenueCat processor when filtering for RevenueCat", () => {
|
||||
const cp = {
|
||||
...baseCusProduct("rc"),
|
||||
processor: { type: ProcessorType.RevenueCat },
|
||||
} as FullCusProduct;
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.RevenueCat,
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("excludes RevenueCat cus product when filtering for Stripe", () => {
|
||||
const cp = {
|
||||
...baseCusProduct("rc"),
|
||||
processor: { type: ProcessorType.RevenueCat },
|
||||
} as FullCusProduct;
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [cp],
|
||||
processorType: ProcessorType.Stripe,
|
||||
});
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("mixed list: filtering for Stripe keeps unset + explicit Stripe, drops RevenueCat", () => {
|
||||
const legacyStripe = baseCusProduct("legacy_stripe");
|
||||
const explicitStripe = {
|
||||
...baseCusProduct("explicit_stripe"),
|
||||
processor: { type: ProcessorType.Stripe },
|
||||
} as FullCusProduct;
|
||||
const rc = {
|
||||
...baseCusProduct("rc"),
|
||||
processor: { type: ProcessorType.RevenueCat },
|
||||
} as FullCusProduct;
|
||||
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [legacyStripe, explicitStripe, rc],
|
||||
processorType: ProcessorType.Stripe,
|
||||
});
|
||||
expect(result.map((cp) => cp.id).sort()).toEqual(
|
||||
["explicit_stripe", "legacy_stripe"].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
test("mixed list: filtering for RevenueCat keeps only explicit RC", () => {
|
||||
const legacyStripe = baseCusProduct("legacy_stripe");
|
||||
const rc = {
|
||||
...baseCusProduct("rc"),
|
||||
processor: { type: ProcessorType.RevenueCat },
|
||||
} as FullCusProduct;
|
||||
|
||||
const result = filterCustomerProductsByProcessorType({
|
||||
customerProducts: [legacyStripe, rc],
|
||||
processorType: ProcessorType.RevenueCat,
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe("rc");
|
||||
});
|
||||
|
||||
test("empty list returns empty list", () => {
|
||||
expect(
|
||||
filterCustomerProductsByProcessorType({
|
||||
customerProducts: [],
|
||||
processorType: ProcessorType.Stripe,
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -1247,7 +1247,9 @@ describe(
|
||||
options: stripeAddOn.allOptions,
|
||||
status: CusProductStatus.Active,
|
||||
subscriptionIds: [],
|
||||
// processor defaults to Stripe via cusProductToProcessorType
|
||||
// IMPORTANT: processor is intentionally unset here — legacy Stripe-managed
|
||||
// cus products have no `processor` field. The filter must treat unset as
|
||||
// Stripe (so this product's items get included). RevenueCat is always tagged.
|
||||
});
|
||||
|
||||
const ctx = contexts.create({ features: [] });
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { FullCusProduct } from "@models/cusProductModels/cusProductModels";
|
||||
import { ProcessorType } from "@models/genModels/genEnums";
|
||||
import { cusProductToProcessorType } from "../convertCusProduct.js";
|
||||
|
||||
/**
|
||||
* Filter customer products by processor type.
|
||||
*
|
||||
* `cusProductToProcessorType` treats an unset `processor` as Stripe (default),
|
||||
* so passing `ProcessorType.Stripe` keeps both legacy unset rows and explicit
|
||||
* Stripe-tagged rows.
|
||||
* IMPORTANT: a customer product with no `processor` set (or `processor.type` unset)
|
||||
* is treated as Stripe. Historically, Stripe-managed cus products were created
|
||||
* without explicitly tagging the processor field, so a null/undefined processor
|
||||
* defaults to Stripe. RevenueCat-managed products always have
|
||||
* `processor.type === ProcessorType.RevenueCat` explicitly set.
|
||||
*
|
||||
* @param customerProducts - The customer products to filter
|
||||
* @param processorType - The processor type to keep (e.g. `ProcessorType.Stripe`)
|
||||
@@ -20,8 +21,10 @@ export const filterCustomerProductsByProcessorType = ({
|
||||
customerProducts: FullCusProduct[];
|
||||
processorType: ProcessorType;
|
||||
}): FullCusProduct[] => {
|
||||
return customerProducts.filter(
|
||||
(customerProduct) =>
|
||||
cusProductToProcessorType(customerProduct) === processorType,
|
||||
);
|
||||
return customerProducts.filter((customerProduct) => {
|
||||
// Default unset processor to Stripe — RevenueCat is always explicitly tagged.
|
||||
const cusProductProcessorType =
|
||||
customerProduct.processor?.type ?? ProcessorType.Stripe;
|
||||
return cusProductProcessorType === processorType;
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user