fix: new version diff product group, attach removes old product

This commit is contained in:
John Yeo
2025-09-02 13:05:14 -07:00
parent 4eed24d21c
commit 3b079944e0
7 changed files with 434 additions and 8 deletions

View File

@@ -38,7 +38,8 @@ let orgSlugs = process.env.ORG_SLUGS!.split(",");
const skipEmails = process.env.SKIP_EMAILS!.split(",");
const skipIds = ["cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx"];
orgSlugs = ["popfly"];
orgSlugs = ["lumenary"];
const customerId = "305fb694-6cdc-4148-bb9b-c73770629f75";
const getSingleCustomer = async ({
stripeCli,
@@ -269,10 +270,6 @@ export const check = async () => {
console.log("--------------------------------");
console.log(`Running error check for ${org.name}`);
let customerId;
customerId = "company_316";
let customers: FullCustomer[] = [];
let stripeSubs: Stripe.Subscription[] = [];
let stripeSchedules: Stripe.SubscriptionSchedule[] = [];

View File

@@ -30,6 +30,7 @@ import { paramsToSubItems } from "../../mergeUtils/paramsToSubItems.js";
import { getExistingCusProducts } from "@/internal/customers/cusProducts/cusProductUtils/getExistingCusProducts.js";
import { shouldCancelSub } from "./upgradeFlowUtils.js";
import { handleUpgradeFlowSchedule } from "./handleUpgradeFlowSchedule.js";
import { logPhaseItems } from "../../mergeUtils/phaseUtils/phaseUtils.js";
export const handleUpgradeFlow = async ({
req,
@@ -70,6 +71,11 @@ export const handleUpgradeFlow = async ({
const { subItems } = newItemSet;
// for (const item of subItems) {
// const { autumnPrice, ...rest } = item;
// console.log("ITEM:", rest);
// }
// Delete scheduled products if needed
for (const product of attachParams.products) {
if (product.is_add_on) continue;
@@ -116,6 +122,11 @@ export const handleUpgradeFlow = async ({
logger.info(`UPGRADE FLOW, updating sub ${curSub!.id}`);
itemSet.subItems = subItems;
// await logPhaseItems({
// db: req.db,
// items: itemSet.subItems,
// });
const res = await updateStripeSub2({
req,
attachParams,

View File

@@ -58,7 +58,7 @@ export const getCusProductsToRemove = ({
}
// 1. If product is an add on, and there's current same, add it
else if (product.is_add_on && curSameProduct) {
else if (curSameProduct) {
cusProductsToRemove.push(curSameProduct);
}
@@ -119,6 +119,8 @@ export const paramsToSubItems = async ({
? removeCusProducts!
: getCusProductsToRemove({ attachParams });
console.log("CUS PRODUCTS TO REMOVE:", cusProductsToRemove);
let newSubItems = mergeNewSubItems({
itemSet,
curSubItems,
@@ -127,7 +129,7 @@ export const paramsToSubItems = async ({
const allCusProducts = attachParams.customer.customer_products;
// 3. Remove items related to cus products to remove
const printRemoveLogs = false;
const printRemoveLogs = true;
for (const cusProduct of cusProductsToRemove) {
const prices = cusProductToPrices({ cusProduct });

View File

@@ -80,7 +80,9 @@ const compareActualItems = async ({
db,
stripePriceId: expectedItem.price,
});
console.log(`(${type}) Missing item:`, expectedItem);
const { autumnPrice, ...rest } = expectedItem;
console.log(`(${type}) Missing item:`, rest);
// if (price) {
// console.log(`Autumn price:`, `${price.id} - ${formatPrice({ price })}`);
// }
@@ -147,6 +149,20 @@ const compareActualItems = async ({
}
}
if (actualItems.length !== expectedItems.length) {
console.log("Actual items:");
await logPhaseItems({
db,
items: actualItems,
});
console.log("Expected items:");
await logPhaseItems({
db,
items: expectedItems,
});
}
assert(
actualItems.length === expectedItems.length,
`actual items length should be equals to expected items length`

View File

@@ -0,0 +1,104 @@
import chalk from "chalk";
import Stripe from "stripe";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { APIVersion, AppEnv, Organization } from "@autumn/shared";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { setupBefore } from "tests/before.js";
import { createProducts } from "tests/utils/productUtils.js";
import { addPrefixToProducts } from "../utils.js";
import {
constructProduct,
constructRawProduct,
} from "@/utils/scriptUtils/createTestProducts.js";
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { TestFeature } from "tests/setup/v2Features.js";
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js";
export let pro = constructProduct({
type: "pro",
items: [],
});
export const addOn = constructRawProduct({
id: "addOn",
isAddOn: true,
items: [
constructFeatureItem({
featureId: TestFeature.Credits,
}),
],
});
const testCase = "addOn2";
describe(`${chalk.yellowBright(`${testCase}: Testing attach free add on twice (should be treated as one off?)`)}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let db: DrizzleCli, org: Organization, env: AppEnv;
let stripeCli: Stripe;
before(async function () {
await setupBefore(this);
db = this.db;
org = this.org;
env = this.env;
stripeCli = this.stripeCli;
await initCustomer({
db,
org,
env,
autumn: this.autumnJs,
customerId,
fingerprint: "test",
attachPm: "success",
});
addPrefixToProducts({
products: [pro, addOn],
prefix: testCase,
});
await createProducts({
db,
orgId: org.id,
env,
autumn,
products: [pro, addOn],
});
});
it("should attach pro product and free add on", async function () {
await attachAndExpectCorrect({
autumn,
customerId,
product: pro,
stripeCli,
db,
org,
env,
skipSubCheck: true,
});
await autumn.attach({
customer_id: customerId,
product_id: addOn.id,
});
const customer = await autumn.customers.get(customerId);
expectProductAttached({
customer,
product: addOn,
});
expectFeaturesCorrect({
customer,
product: addOn,
});
});
});

View File

@@ -0,0 +1,204 @@
// import { expect } from "chai";
// import { AutumnInt } from "@/external/autumn/autumnCli.js";
// import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
// import { AppEnv, Organization, ProductV2 } from "@autumn/shared";
// import chalk from "chalk";
// import Stripe from "stripe";
// import { DrizzleCli } from "@/db/initDrizzle.js";
// import { setupBefore } from "tests/before.js";
// import { createProducts } from "tests/utils/productUtils.js";
// import { addPrefixToProducts } from "../utils.js";
// import { constructRawProduct } from "@/utils/scriptUtils/createTestProducts.js";
// import { constructArrearProratedItem } from "@/utils/scriptUtils/constructItem.js";
// import { TestFeature } from "tests/setup/v2Features.js";
// import { replaceItems } from "../utils.js";
// import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
// import { expectSubToBeCorrect } from "tests/merged/mergeUtils/expectSubCorrect.js";
// import { defaultApiVersion } from "tests/constants.js";
// export let pro = constructRawProduct({
// id: "pro",
// items: [
// constructArrearProratedItem({
// featureId: TestFeature.Users,
// pricePerUnit: 30,
// includedUsage: 0,
// }),
// ],
// });
// const testCase = "newVersion3";
// describe(`${chalk.yellowBright(`${testCase}: Testing attach new version for cont use product`)}`, () => {
// let customerId = testCase;
// let autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion });
// let testClockId: string;
// let db: DrizzleCli, org: Organization, env: AppEnv;
// let stripeCli: Stripe;
// let curUnix = new Date().getTime();
// before(async function () {
// await setupBefore(this);
// const { autumnJs } = this;
// db = this.db;
// org = this.org;
// env = this.env;
// stripeCli = this.stripeCli;
// addPrefixToProducts({
// products: [pro],
// prefix: testCase,
// });
// await createProducts({
// db,
// orgId: org.id,
// env,
// autumn,
// products: [pro],
// customerId,
// });
// const { testClockId: testClockId1 } = await initCustomer({
// autumn: autumnJs,
// customerId,
// db,
// org,
// env,
// attachPm: "success",
// });
// testClockId = testClockId1!;
// });
// const entities = [
// {
// id: "1",
// name: "test",
// feature_id: TestFeature.Users,
// },
// ];
// it("should attach pro product", async function () {
// await autumn.entities.create(customerId, entities);
// await attachAndExpectCorrect({
// autumn,
// customerId,
// product: pro,
// stripeCli,
// db,
// org,
// env,
// entities,
// usage: [
// {
// featureId: TestFeature.Users,
// value: 1,
// },
// ],
// });
// });
// return;
// let newPro: ProductV2;
// it("should update product to new version", async function () {
// newPro = structuredClone(pro);
// let newItems = replaceItems({
// items: pro.items,
// featureId: TestFeature.Users,
// newItem: constructArrearProratedItem({
// featureId: TestFeature.Users,
// pricePerUnit: 50,
// includedUsage: 0,
// }),
// });
// newPro.version = 2;
// newPro.items = newItems;
// await autumn.products.update(pro.id, {
// items: newItems,
// });
// });
// return;
// it("should attach pro v2", async function () {
// const checkout = await autumn.checkout({
// customer_id: customerId,
// product_id: newPro.id,
// });
// expect(checkout.total).to.equal(0);
// const cusBefore = await autumn.customers.get(customerId);
// const res = await autumn.attach({
// customer_id: customerId,
// product_id: newPro.id,
// });
// const cusAfter = await autumn.customers.get(customerId);
// expect(cusBefore.invoices.length).to.equal(cusAfter.invoices.length);
// await expectSubToBeCorrect({
// db,
// customerId,
// org,
// env,
// });
// // await runUpdateEntsTest({
// // autumn,
// // stripeCli,
// // customerId,
// // customProduct: newPro,
// // newVersion: 2,
// // db,
// // org,
// // env,
// // });
// });
// // it("should have correct invoice total on next cycle", async function () {
// // const invoiceTotal = await getExpectedInvoiceTotal({
// // org,
// // env,
// // customerId,
// // productId: pro.id,
// // stripeCli,
// // db,
// // usage: [
// // {
// // featureId: TestFeature.Words,
// // value: usage,
// // },
// // ],
// // onlyIncludeMonthly: true,
// // });
// // let curUnix = Date.now();
// // curUnix = await advanceTestClock({
// // stripeCli,
// // testClockId,
// // advanceTo: addMonths(curUnix, 1).getTime(),
// // waitForSeconds: 30,
// // });
// // await advanceTestClock({
// // stripeCli,
// // testClockId,
// // advanceTo: addHours(curUnix, hoursToFinalizeInvoice).getTime(),
// // waitForSeconds: 10,
// // });
// // const customer = await autumn.customers.get(customerId);
// // const invoice = customer.invoices[0];
// // expect(invoice.total).to.equal(
// // invoiceTotal,
// // "invoice total after 1 cycle should be correct"
// // );
// // });
// });

View File

@@ -0,0 +1,92 @@
import chalk from "chalk";
import Stripe from "stripe";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { APIVersion, AppEnv, Organization } from "@autumn/shared";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { setupBefore } from "tests/before.js";
import { createProducts } from "tests/utils/productUtils.js";
import { addPrefixToProducts } from "../utils.js";
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { TestFeature } from "tests/setup/v2Features.js";
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
export let free = constructProduct({
items: [
constructFeatureItem({
featureId: TestFeature.Words,
}),
],
isAnnual: false,
type: "free",
isDefault: false,
});
// Pro trial
// Pro
const testCase = "others9";
describe(`${chalk.yellowBright(`${testCase}: Testing attach free product again`)}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let db: DrizzleCli, org: Organization, env: AppEnv;
let stripeCli: Stripe;
before(async function () {
await setupBefore(this);
db = this.db;
org = this.org;
env = this.env;
stripeCli = this.stripeCli;
await initCustomer({
db,
org,
env,
autumn: this.autumnJs,
customerId,
fingerprint: "test",
attachPm: "success",
});
addPrefixToProducts({
products: [free],
prefix: testCase,
});
await createProducts({
db,
orgId: org.id,
env,
autumn,
products: [free],
});
});
it("should attach free product, then try again and hit error", async function () {
await attachAndExpectCorrect({
autumn,
customerId,
product: free,
stripeCli,
db,
org,
env,
skipSubCheck: true,
});
await expectAutumnError({
func: async () => {
await autumn.attach({
customer_id: customerId,
product_id: free.id,
});
},
});
});
});