fix: multi attach update

This commit is contained in:
John Yeo
2025-09-05 13:41:26 -07:00
parent a68ae075ea
commit f5e9a7caca
17 changed files with 624 additions and 124 deletions

View File

@@ -12,12 +12,12 @@ fi
$MOCHA_CMD 'tests/merged/group/*.ts'
$MOCHA_CMD 'tests/merged/add/*.ts' \
'tests/merged/downgrade/*.ts' \
'tests/merged/prepaid/*.ts' \
'tests/merged/separate/*.ts' \
'tests/merged/upgrade/*.ts' \
'tests/merged/trial/*.ts'
# $MOCHA_CMD 'tests/merged/add/*.ts' \
# 'tests/merged/downgrade/*.ts' \
# 'tests/merged/prepaid/*.ts' \
# 'tests/merged/separate/*.ts' \
# 'tests/merged/upgrade/*.ts' \
# 'tests/merged/trial/*.ts'
$MOCHA_CMD 'tests/merged/addOn/*.ts' \

View File

@@ -32,14 +32,17 @@ import { getRelatedCusPrice } from "./internal/customers/cusProducts/cusEnts/cus
import { checkCusSubCorrect } from "./utils/checkUtils/checkCustomerCorrect.js";
import { EntityService } from "./internal/api/entities/EntityService.js";
const { db, client } = initDrizzle({ maxConnections: 5 });
const { db } = initDrizzle({ maxConnections: 5 });
let orgSlugs = process.env.ORG_SLUGS!.split(",");
const skipEmails = process.env.SKIP_EMAILS!.split(",");
const skipIds = ["cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx"];
const skipIds = [
"cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx",
"DxG668K7uDd0Vahk54YWjvCGVgf2",
];
orgSlugs = ["lumenary"];
const customerId = "305fb694-6cdc-4148-bb9b-c73770629f75";
orgSlugs = ["supermemory"];
const customerId = "co1VPgUU59q43d5P2rFt4c";
const getSingleCustomer = async ({
stripeCli,

View File

@@ -9,6 +9,7 @@ import {
EntityExpand,
ErrCode,
Invoice,
OrgConfig,
} from "@autumn/shared";
import {
CancelParams,
@@ -48,11 +49,13 @@ export class AutumnInt {
secretKey,
baseUrl,
version,
orgConfig,
}: {
apiKey?: string;
secretKey?: string;
baseUrl?: string;
version?: string | APIVersion;
orgConfig?: Partial<OrgConfig>;
} = {}) {
// this.apiKey = apiKey || process.env.AUTUMN_API_KEY || "";
this.apiKey =
@@ -67,6 +70,10 @@ export class AutumnInt {
this.headers["x-api-version"] = version.toString();
}
if (orgConfig) {
this.headers["org-config"] = JSON.stringify(orgConfig);
}
this.baseUrl = baseUrl || "http://localhost:8080/v1";
}

View File

@@ -22,6 +22,7 @@ import {
type Organization,
type Subscription,
CusProductResponse,
notNullish,
} from "@autumn/shared";
export const getEntityResponse = async ({
@@ -85,10 +86,20 @@ export const getEntityResponse = async ({
);
let entityCusProducts = customer.customer_products.filter(
(p: FullCusProduct) =>
(p: FullCusProduct) => {
if (org.config.entity_product) {
return entities.some(
(e: Entity) =>
e.internal_id == p.internal_entity_id &&
notNullish(p.internal_entity_id)
);
}
return (
entities.some((e: Entity) => e.internal_id == p.internal_entity_id) ||
nullish(p.internal_entity_id)
);
}
);
let subs = customer.subscriptions || [];

View File

@@ -39,8 +39,6 @@ export const handleGetEntity = async (req: any, res: any) =>
features,
logger,
});
// const end = performance.now();
// logger.info(`getEntityResponse took ${(end - start).toFixed(2)}ms`);
let entity = entities[0];
let withInvoices = expand.includes(EntityExpand.Invoices);

View File

@@ -173,6 +173,8 @@ export const handleMultiAttachFlow = async ({
(p) => p.id === productOptions.product_id
)!;
if (productOptions.quantity === 0) continue;
const anchorToUnix = curSub
? getLatestPeriodEnd({ sub: curSub! }) * 1000
: undefined;

View File

@@ -1,12 +1,23 @@
import { decryptData, generatePublishableKey } from "@/utils/encryptUtils.js";
import RecaseError from "@/utils/errorUtils.js";
import { AppEnv, ErrCode, FrontendOrg, Organization } from "@autumn/shared";
import {
AppEnv,
ErrCode,
FrontendOrg,
Organization,
organizations,
OrgConfig,
} from "@autumn/shared";
import { createStripeCli } from "@/external/stripe/utils.js";
import { OrgService } from "./OrgService.js";
import { FeatureService } from "../features/FeatureService.js";
import { notNullish } from "@/utils/genUtils.js";
import Stripe from "stripe";
import { toSuccessUrl } from "./orgUtils/convertOrgUtils.js";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { CacheManager } from "@/external/caching/CacheManager.js";
import { eq } from "drizzle-orm";
import { clearOrgCache } from "./orgUtils/clearOrgCache.js";
export const shouldReconnectStripe = async ({
org,
@@ -167,3 +178,34 @@ export const getOrgAndFeatures = async ({ req }: { req: any }) => {
return { org, features };
};
export const updateOrgConfig = async ({
db,
org,
config,
disconnectCache = true,
}: {
db: DrizzleCli;
org: Organization;
config: Partial<OrgConfig>;
disconnectCache?: boolean;
}) => {
await db
.update(organizations)
.set({
config: {
...org.config,
...config,
},
})
.where(eq(organizations.id, org.id));
await clearOrgCache({
db,
orgId: org.id,
});
if (disconnectCache) {
await CacheManager.disconnect();
}
};

View File

@@ -92,6 +92,18 @@ export const verifySecretKey = async (req: any, res: any, next: any) => {
req.authType = AuthType.SecretKey;
req.userId = userId;
const orgConfig = await req.headers["org-config"];
if (orgConfig) {
console.log("Org config found!: ", orgConfig);
let newConfigFields = JSON.parse(orgConfig);
try {
req.org.config = {
...org.config,
...newConfigFields,
};
} catch (error) {}
}
next();
};

View File

@@ -64,7 +64,10 @@ const ops = [
const testCase = "multiAttach1";
describe(`${chalk.yellowBright("multiAttach1: Testing multi attach for trial products and update product quantities mid trial")}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let autumn: AutumnInt = new AutumnInt({
version: APIVersion.v1_4,
orgConfig: { entity_product: true },
});
let stripeCli: Stripe;
let testClockId: string;

View File

@@ -51,7 +51,10 @@ let pro = constructProduct({
const testCase = "multiAttach3";
describe(`${chalk.yellowBright("multiAttach3: Testing multi attach for trial products transfer to entity, then cancel products on entities...")}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let autumn: AutumnInt = new AutumnInt({
version: APIVersion.v1_4,
orgConfig: { entity_product: true },
});
let stripeCli: Stripe;
let testClockId: string;
@@ -137,12 +140,12 @@ describe(`${chalk.yellowBright("multiAttach3: Testing multi attach for trial pro
const results = [
{
product: pro,
quantity: 4,
quantity: 5,
status: CusProductStatus.Trialing,
},
{
product: premium,
quantity: 2,
quantity: 3,
status: CusProductStatus.Trialing,
},
{
@@ -172,6 +175,7 @@ describe(`${chalk.yellowBright("multiAttach3: Testing multi attach for trial pro
});
await expectResultsCorrect({
autumn,
customerId,
results,
});

View File

@@ -11,6 +11,7 @@ import {
AppEnv,
CusProductStatus,
Organization,
organizations,
} from "@autumn/shared";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { DrizzleCli } from "@/db/initDrizzle.js";
@@ -26,6 +27,12 @@ import { expectSubToBeCorrect } from "tests/merged/mergeUtils/expectSubCorrect.j
import { advanceTestClock } from "tests/utils/stripeUtils.js";
import { addDays } from "date-fns";
import { expect } from "chai";
import { OrgService } from "@/internal/orgs/OrgService.js";
import { clearOrgCache } from "@/internal/orgs/orgUtils/clearOrgCache.js";
import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js";
import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js";
import { eq } from "drizzle-orm";
import { CacheManager } from "@/external/caching/CacheManager.js";
let premium = constructProduct({
id: "premium",
@@ -67,6 +74,17 @@ describe(`${chalk.yellowBright("multiAttach5: Testing multi attach and get custo
stripeCli = this.stripeCli;
await OrgService.update({
db,
orgId: org.id,
updates: {
config: {
...org.config,
entity_product: true,
},
},
});
addPrefixToProducts({
products: [pro, premium],
prefix: testCase,
@@ -145,66 +163,38 @@ describe(`${chalk.yellowBright("multiAttach5: Testing multi attach and get custo
product_id: pro.id,
});
// await expectResultsCorrect({
// customerId,
// results: [
// {
// product: pro,
// quantity: 1,
// entityId: "1",
// status: CusProductStatus.Active,
// },
// ],
// });
// await expectSubToBeCorrect({
// db,
// customerId,
// org,
// env,
// });
});
return;
it("should cancel one entity's sub at end of cycle and have correct schedule...", async function () {
await autumn.cancel({
customer_id: customerId,
const results = [
{
product_id: pro.id,
entity_id: "2",
});
await expectSubToBeCorrect({
db,
customerId,
org,
env,
});
});
it("should cancel one entity's sub immediately", async function () {
await autumn.cancel({
customer_id: customerId,
quantity: 4,
product: pro,
status: CusProductStatus.Active,
},
{
product_id: premium.id,
entity_id: "1",
cancel_immediately: true,
// @ts-ignore
prorate: false,
});
quantity: 3,
product: premium,
status: CusProductStatus.Active,
},
{
product_id: pro.id,
quantity: 1,
product: pro,
entityId: "1",
status: CusProductStatus.Active,
},
{
product_id: pro.id,
quantity: 1,
product: pro,
entityId: "2",
status: CusProductStatus.Active,
},
];
await expectSubToBeCorrect({
db,
await expectResultsCorrect({
customerId,
org,
env,
});
});
it("should advance test clock to end of trial and have correct sub", async function () {
await advanceTestClock({
stripeCli,
testClockId,
advanceTo: addDays(new Date(), 8).getTime(),
waitForSeconds: 30,
results,
});
await expectSubToBeCorrect({
@@ -214,21 +204,115 @@ describe(`${chalk.yellowBright("multiAttach5: Testing multi attach and get custo
env,
});
const customer = await autumn.customers.get(customerId);
const latestInvoice = customer.invoices[0];
const entity1 = await autumn.entities.get(customerId, "1");
const entity2 = await autumn.entities.get(customerId, "2");
expectFeaturesCorrect({
customer: entity1,
product: pro,
});
expectFeaturesCorrect({
customer: entity2,
product: pro,
});
});
// Should only have paid for 4 pro and 2 premium...
const invoiceTotal =
getBasePrice({ product: pro }) * 4 +
getBasePrice({ product: premium }) * 2;
it("should try to reduce quantity of pro to less than number of entities and fail", async function () {
await expectAutumnError({
func: async () => {
await autumn.attach({
customer_id: customerId,
products: [
{
product_id: pro.id,
quantity: 1,
},
],
});
},
});
});
expect(invoiceTotal).to.equal(latestInvoice.total);
await expectSubToBeCorrect({
db,
it("should update pro product quantity and have correct amount", async function () {
await expectMultiAttachCorrect({
customerId,
products: [
{
product_id: pro.id,
quantity: 6,
},
],
results: [
{
product: pro,
quantity: 6,
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 1,
entityId: "1",
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 1,
entityId: "2",
status: CusProductStatus.Active,
},
],
db,
org,
env,
});
});
it("should decrease pro product quantity and have correct amount", async function () {
await expectMultiAttachCorrect({
customerId,
products: [
{
product_id: pro.id,
quantity: 5,
},
],
results: [
{
product: pro,
quantity: 5,
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 1,
entityId: "1",
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 1,
entityId: "2",
status: CusProductStatus.Active,
},
],
db,
org,
env,
});
});
after(async function () {
await OrgService.update({
db,
orgId: org.id,
updates: {
config: {
...org.config,
entity_product: false,
},
},
});
await CacheManager.disconnect();
});
return;
});

View File

@@ -0,0 +1,280 @@
import chalk from "chalk";
import { setupBefore } from "tests/before.js";
import { Stripe } from "stripe";
import { createProducts } from "tests/utils/productUtils.js";
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
import { TestFeature } from "tests/setup/v2Features.js";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
import {
APIVersion,
AppEnv,
CusProductStatus,
FullCusProduct,
nullish,
Organization,
} from "@autumn/shared";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js";
import {
expectMultiAttachCorrect,
expectResultsCorrect,
} from "tests/utils/expectUtils/expectMultiAttach.js";
import { expectSubToBeCorrect } from "tests/merged/mergeUtils/expectSubCorrect.js";
import { expect } from "chai";
import { OrgService } from "@/internal/orgs/OrgService.js";
import { CacheManager } from "@/external/caching/CacheManager.js";
import { CusService } from "@/internal/customers/CusService.js";
let premium = constructProduct({
id: "premium",
items: [
constructFeatureItem({ featureId: TestFeature.Words, includedUsage: 200 }),
],
type: "premium",
});
let pro = constructProduct({
id: "pro",
items: [
constructFeatureItem({
featureId: TestFeature.Words,
includedUsage: 300,
}),
],
type: "pro",
});
const testCase = "multiAttach6";
describe(`${chalk.yellowBright("multiAttach6: Testing multi attach and get customer")}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let stripeCli: Stripe;
let testClockId: string;
let curUnix: number;
let db: DrizzleCli;
let org: Organization;
let env: AppEnv;
before(async function () {
await setupBefore(this);
const { autumnJs } = this;
db = this.db;
org = this.org;
env = this.env;
stripeCli = this.stripeCli;
await OrgService.update({
db,
orgId: org.id,
updates: {
config: {
...org.config,
entity_product: true,
},
},
});
addPrefixToProducts({
products: [pro, premium],
prefix: testCase,
});
await createProducts({
autumn: autumnJs,
products: [pro, premium],
db,
orgId: org.id,
env,
customerId,
});
const { testClockId: testClockId1 } = await initCustomer({
autumn: autumnJs,
customerId,
db,
org,
env,
attachPm: "success",
});
testClockId = testClockId1!;
});
it("should run multi attach through checkout and have correct sub", async function () {
const productsList = [
{
product_id: pro.id,
quantity: 4,
product: pro,
status: CusProductStatus.Active,
},
{
product_id: premium.id,
quantity: 3,
product: premium,
status: CusProductStatus.Active,
},
];
await expectMultiAttachCorrect({
customerId,
products: productsList,
results: productsList,
db,
org,
env,
});
});
const entities = [
{
id: "1",
name: "Entity 1",
feature_id: TestFeature.Users,
},
{
id: "2",
name: "Entity 2",
feature_id: TestFeature.Users,
},
];
it("should transfer to entity 1 and 2", async function () {
await autumn.entities.create(customerId, entities);
await autumn.transfer(customerId, {
to_entity_id: "1",
product_id: pro.id,
});
await autumn.transfer(customerId, {
to_entity_id: "2",
product_id: pro.id,
});
const results = [
{
product_id: pro.id,
quantity: 4,
product: pro,
status: CusProductStatus.Active,
},
{
product_id: premium.id,
quantity: 3,
product: premium,
status: CusProductStatus.Active,
},
{
product_id: pro.id,
quantity: 1,
product: pro,
entityId: "1",
status: CusProductStatus.Active,
},
{
product_id: pro.id,
quantity: 1,
product: pro,
entityId: "2",
status: CusProductStatus.Active,
},
];
await expectResultsCorrect({
customerId,
results,
});
await expectSubToBeCorrect({
db,
customerId,
org,
env,
});
});
it("should try to reduce quantity of pro to 0 and have no top level cus product...", async function () {
await autumn.attach({
customer_id: customerId,
products: [
{
product_id: pro.id,
quantity: 2,
},
],
});
const results = [
{
product: pro,
quantity: 1,
entityId: "1",
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 1,
entityId: "2",
status: CusProductStatus.Active,
},
{
product: pro,
quantity: 2,
status: CusProductStatus.Active,
},
];
await expectResultsCorrect({
customerId,
results,
});
const fullCus = await CusService.getFull({
db,
orgId: org.id,
env,
idOrInternalId: customerId,
});
const proProduct = fullCus.customer_products.find(
(p: FullCusProduct) =>
p.product_id === pro.id && nullish(p.internal_entity_id)
);
expect(proProduct).to.be.undefined;
});
it("should increase pro product quantity and have correct amount", async function () {
await autumn.attach({
customer_id: customerId,
products: [
{
product_id: pro.id,
quantity: 4,
},
],
});
});
after(async function () {
await OrgService.update({
db,
orgId: org.id,
updates: {
config: {
...org.config,
entity_product: false,
},
},
});
await CacheManager.disconnect();
});
return;
});

View File

@@ -27,6 +27,7 @@ import { advanceTestClock } from "tests/utils/stripeUtils.js";
import { addDays } from "date-fns";
import { expect } from "chai";
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
import { updateOrgConfig } from "@/internal/orgs/orgUtils.js";
let premium = constructProduct({
id: "premium",
@@ -50,7 +51,10 @@ let pro = constructProduct({
const testCase = "multiUpgrade1";
describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade")}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let autumn: AutumnInt = new AutumnInt({
version: APIVersion.v1_4,
orgConfig: { entity_product: true },
});
let stripeCli: Stripe;
let testClockId: string;
@@ -61,6 +65,7 @@ describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade"
before(async function () {
await setupBefore(this);
const { autumnJs } = this;
db = this.db;
org = this.org;
@@ -111,6 +116,7 @@ describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade"
];
await expectMultiAttachCorrect({
autumn,
customerId,
products: productsList,
results: productsList,
@@ -131,7 +137,7 @@ describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade"
const results = [
{
product: pro,
quantity: 4,
quantity: 5,
status: CusProductStatus.Active,
},
{
@@ -156,6 +162,7 @@ describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade"
});
await expectResultsCorrect({
autumn,
customerId,
results,
});
@@ -193,7 +200,27 @@ describe(`${chalk.yellowBright("multiUpgrade1: Testing multi attach and upgrade"
env,
});
const results = [
{
product: pro,
quantity: 4,
status: CusProductStatus.Active,
},
{
product: premium,
quantity: 4,
status: CusProductStatus.Active,
},
{
product: premium,
quantity: 1,
entityId: "1",
status: CusProductStatus.Active,
},
];
await expectResultsCorrect({
autumn,
customerId,
results,
});

View File

@@ -14,19 +14,12 @@ import {
} from "@autumn/shared";
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
import { DrizzleCli } from "@/db/initDrizzle.js";
import {
addPrefixToProducts,
getBasePrice,
} from "tests/utils/testProductUtils/testProductUtils.js";
import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js";
import {
expectMultiAttachCorrect,
expectResultsCorrect,
} from "tests/utils/expectUtils/expectMultiAttach.js";
import { expectSubToBeCorrect } from "tests/merged/mergeUtils/expectSubCorrect.js";
import { advanceTestClock } from "tests/utils/stripeUtils.js";
import { addDays } from "date-fns";
import { expect } from "chai";
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
let premium = constructProduct({
id: "premium",
@@ -50,7 +43,10 @@ let pro = constructProduct({
const testCase = "multiUpgrade2";
describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update quantities downward")}`, () => {
let customerId = testCase;
let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 });
let autumn: AutumnInt = new AutumnInt({
version: APIVersion.v1_4,
orgConfig: { entity_product: true },
});
let stripeCli: Stripe;
let testClockId: string;
@@ -111,6 +107,7 @@ describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update q
];
await expectMultiAttachCorrect({
autumn,
customerId,
products: productsList,
results: productsList,
@@ -131,7 +128,7 @@ describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update q
const results = [
{
product: pro,
quantity: 4,
quantity: 5,
status: CusProductStatus.Active,
},
{
@@ -156,6 +153,7 @@ describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update q
});
await expectResultsCorrect({
autumn,
customerId,
results,
});
@@ -172,23 +170,23 @@ describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update q
const productsList = [
{
product_id: pro.id,
quantity: 3,
quantity: 6,
},
{
product_id: premium.id,
quantity: 2,
quantity: 1,
},
];
const results = [
{
product: pro,
quantity: 3,
quantity: 6,
status: CusProductStatus.Active,
},
{
product: premium,
quantity: 2,
quantity: 1,
status: CusProductStatus.Active,
},
{
@@ -200,6 +198,7 @@ describe(`${chalk.yellowBright("multiUpgrade2: Testing multi attach and update q
];
await expectMultiAttachCorrect({
autumn,
customerId,
products: productsList,
results,

View File

@@ -33,6 +33,7 @@ import { Decimal } from "decimal.js";
import { completeInvoiceCheckout } from "../stripeUtils/completeInvoiceCheckout.js";
export const expectMultiAttachCorrect = async ({
autumn,
customerId,
entityId,
products,
@@ -44,6 +45,7 @@ export const expectMultiAttachCorrect = async ({
org,
env,
}: {
autumn?: AutumnInt;
customerId: string;
entityId?: string;
products: ProductOptions[];
@@ -51,6 +53,7 @@ export const expectMultiAttachCorrect = async ({
product: ProductV2;
quantity: number;
status: CusProductStatus;
entityId?: string;
}[];
rewards?: string[];
expectedRewards?: string[];
@@ -59,7 +62,7 @@ export const expectMultiAttachCorrect = async ({
org: Organization;
env: AppEnv;
}) => {
const autumn = new AutumnInt({ version: APIVersion.v1_2 });
autumn = autumn || new AutumnInt({ version: APIVersion.v1_2 });
const checkoutRes = await autumn.checkout({
customer_id: customerId,
products: products,
@@ -91,12 +94,17 @@ export const expectMultiAttachCorrect = async ({
for (const result of results) {
let customer;
if (result.entityId) {
customer = await autumn.entities.get(customerId, result.entityId);
} else {
customer = await autumn.customers.get(customerId);
}
expectProductAttached({
customer,
product: result.product,
status: result.status,
entityId: result.entityId,
});
}
@@ -118,21 +126,33 @@ export const expectMultiAttachCorrect = async ({
};
export const expectResultsCorrect = async ({
autumn,
customerId,
results,
}: {
autumn?: AutumnInt;
customerId: string;
results: { product: ProductV2; quantity: number; status: CusProductStatus }[];
results: {
product: ProductV2;
quantity: number;
status: CusProductStatus;
entityId?: string;
}[];
}) => {
const autumn = new AutumnInt({ version: APIVersion.v1_2 });
autumn = autumn || new AutumnInt({ version: APIVersion.v1_2 });
for (const result of results) {
let customer;
if (result.entityId) {
customer = await autumn.entities.get(customerId, result.entityId);
} else {
customer = await autumn.customers.get(customerId);
}
expectProductAttached({
customer,
product: result.product,
status: result.status,
quantity: result.quantity,
});
}
};

View File

@@ -11,6 +11,7 @@ export const expectProductAttached = ({
status,
entityId,
isCanceled = false,
quantity,
}: {
customer: Customer;
product?: ProductV2;
@@ -18,6 +19,7 @@ export const expectProductAttached = ({
status?: CusProductStatus;
entityId?: string;
isCanceled?: boolean;
quantity?: number;
}) => {
const cusProducts = customer.products;
const finalProductId = productId || product?.id;
@@ -45,6 +47,11 @@ export const expectProductAttached = ({
).to.not.equal(CusProductStatus.Expired);
}
if (quantity) {
// @ts-ignore
expect(productAttached?.quantity).to.equal(quantity);
}
if (entityId) {
// @ts-ignore
expect(productAttached?.entity_id).to.equal(entityId);

View File

@@ -21,6 +21,7 @@ export const OrgConfigSchema = z.object({
allow_paid_default: z.boolean().default(false),
cache_customer: z.boolean().default(false),
invoice_memos: z.boolean().default(false),
entity_product: z.boolean().default(false),
});
export type OrgConfig = z.infer<typeof OrgConfigSchema>;