215 lines
5.4 KiB
TypeScript
215 lines
5.4 KiB
TypeScript
import { expect } from "bun:test";
|
|
import {
|
|
AllowanceType,
|
|
CusProductStatus,
|
|
type Entitlement,
|
|
type Feature,
|
|
type FeatureOptions,
|
|
FeatureType,
|
|
type UsagePriceConfig,
|
|
} from "@autumn/shared";
|
|
import type { ApiCustomerV1 } from "@shared/api/customers/previousVersions/apiCustomerV1.js";
|
|
import { AutumnCli } from "@tests/cli/AutumnCli.js";
|
|
// import { expect } from "chai";
|
|
import { Decimal } from "decimal.js";
|
|
|
|
export const checkProductIsScheduled = ({
|
|
cusRes,
|
|
product,
|
|
}: {
|
|
cusRes: any;
|
|
product: any;
|
|
}) => {
|
|
const { products, add_ons, entitlements } = cusRes;
|
|
const prod = products.find((p: any) => p.id === product.id);
|
|
try {
|
|
expect(prod).toBeDefined();
|
|
expect(prod.status).toEqual(CusProductStatus.Scheduled);
|
|
} catch (error) {
|
|
console.group();
|
|
console.log(`Expected product ${product.id} to be scheduled`);
|
|
console.log("Received: ", cusRes.products);
|
|
console.groupEnd();
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const compareMainProduct = ({
|
|
sent,
|
|
cusRes,
|
|
status = CusProductStatus.Active,
|
|
optionsList = [],
|
|
skipEntitlements = false,
|
|
}: {
|
|
sent: any;
|
|
cusRes: any;
|
|
status?: CusProductStatus;
|
|
optionsList?: FeatureOptions[];
|
|
skipEntitlements?: boolean;
|
|
}) => {
|
|
const { products, add_ons, entitlements } = cusRes;
|
|
const prod = products.find(
|
|
(p: any) => p.id === sent.id && p.status === status && !sent.is_add_on,
|
|
);
|
|
|
|
expect(
|
|
prod,
|
|
`Product ${sent.id} not found (status: ${status}), (${sent.is_add_on ? "add-on" : "main"})`,
|
|
).toBeDefined();
|
|
|
|
if (skipEntitlements) return;
|
|
|
|
// Check entitlements
|
|
const sentEntitlements = Object.values(sent.entitlements) as Entitlement[];
|
|
const recEntitlements = entitlements;
|
|
|
|
for (const entitlement of sentEntitlements) {
|
|
// Corresponding entitlement in received
|
|
const recEntitlement = recEntitlements.find(
|
|
(e: ApiCustomerV1["entitlements"][number]) => {
|
|
if (e.feature_id !== entitlement.feature_id) return false;
|
|
|
|
if (entitlement.allowance_type === AllowanceType.Unlimited) {
|
|
return true;
|
|
}
|
|
|
|
if (entitlement.interval && e.interval !== entitlement.interval) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
);
|
|
|
|
// If options list provideed, and feature
|
|
const options = optionsList.find(
|
|
(o: any) => o.feature_id === entitlement.feature_id,
|
|
);
|
|
|
|
let expectedBalance = entitlement.allowance;
|
|
if (options?.quantity) {
|
|
// Get price from sent
|
|
const price = sent.prices.find(
|
|
(p: any) => p.config.feature_id === entitlement.feature_id,
|
|
);
|
|
const config = price.config as UsagePriceConfig;
|
|
expectedBalance = new Decimal(expectedBalance || 0)
|
|
.add(options.quantity * (config.billing_units || 1))
|
|
.toNumber();
|
|
}
|
|
|
|
expect(
|
|
recEntitlement,
|
|
`Entitlement ${entitlement.feature_id} not found`,
|
|
).toBeDefined();
|
|
|
|
if (entitlement.allowance_type === AllowanceType.Unlimited) {
|
|
expect(recEntitlement).toMatchObject({
|
|
unlimited: true,
|
|
balance: null,
|
|
used: null,
|
|
});
|
|
} else if ("balance" in entitlement) {
|
|
expect(
|
|
recEntitlement.balance,
|
|
`Balance for ${entitlement.feature_id} does not match expected balance`,
|
|
).toStrictEqual(expectedBalance);
|
|
}
|
|
}
|
|
};
|
|
|
|
export const checkFeatureHasCorrectBalance = async ({
|
|
customerId,
|
|
feature,
|
|
entitlement,
|
|
expectedBalance,
|
|
}: {
|
|
customerId: string;
|
|
feature: Feature;
|
|
entitlement: Entitlement;
|
|
expectedBalance: number;
|
|
}) => {
|
|
const [entitledRes, cusRes] = await Promise.all([
|
|
AutumnCli.entitled(customerId, feature.id, true),
|
|
AutumnCli.getCustomer(customerId),
|
|
]);
|
|
|
|
if (feature.type === FeatureType.Boolean) {
|
|
console.log(" - Checking boolean feature: ", feature.id);
|
|
const { allowed, balanceObj }: any = entitledRes;
|
|
expect(allowed, `Allowed for ${feature.id} is not true`).toStrictEqual(
|
|
true,
|
|
);
|
|
return;
|
|
}
|
|
|
|
// console.log(
|
|
// ` - Checking entitlement ${feature.id} has ${
|
|
// entitlement.allowance_type == AllowanceType.Unlimited
|
|
// ? "unlimited balance"
|
|
// : `balance of ${expectedBalance}`
|
|
// }`
|
|
// );
|
|
|
|
// Get ent from cusRes
|
|
const { entitlements: cusEnts }: any = cusRes;
|
|
const { allowed, balanceObj }: any = entitledRes;
|
|
const cusEnt = cusEnts.find(
|
|
(e: any) =>
|
|
e.feature_id === feature.id && e.interval === entitlement.interval,
|
|
);
|
|
|
|
expect(cusEnt, `Cus ent for ${feature.id} not found`).toBeDefined();
|
|
|
|
if (entitlement.allowance_type === AllowanceType.Unlimited) {
|
|
// Cus ent
|
|
expect(
|
|
cusEnt.balance,
|
|
`Balance for ${feature.id} is not null`,
|
|
).toStrictEqual(null);
|
|
expect(cusEnt.used, `Used for ${feature.id} is not null`).toStrictEqual(
|
|
null,
|
|
);
|
|
expect(
|
|
cusEnt.unlimited,
|
|
`Unlimited for ${feature.id} is not true`,
|
|
).toStrictEqual(true);
|
|
|
|
// Entitled res
|
|
expect(allowed, `Allowed for ${feature.id} is not true`).toStrictEqual(
|
|
true,
|
|
);
|
|
expect(
|
|
balanceObj?.balance,
|
|
`Balance for ${feature.id} is not null`,
|
|
).toStrictEqual(null);
|
|
expect(
|
|
balanceObj?.unlimited,
|
|
`Unlimited for ${feature.id} is not true`,
|
|
).toStrictEqual(true);
|
|
return;
|
|
}
|
|
|
|
if (expectedBalance === 0) {
|
|
expect(allowed, `Allowed for ${feature.id} is not false`).toStrictEqual(
|
|
false,
|
|
);
|
|
expect(
|
|
balanceObj?.balance,
|
|
`Balance for ${feature.id} is not 0`,
|
|
).toStrictEqual(0);
|
|
expect(cusEnt.balance, `Balance for ${feature.id} is not 0`).toStrictEqual(
|
|
0,
|
|
);
|
|
return;
|
|
}
|
|
|
|
expect(
|
|
balanceObj?.balance,
|
|
`Balance for ${feature.id} does not match expected balance`,
|
|
).toStrictEqual(expectedBalance);
|
|
expect(
|
|
cusEnt.balance,
|
|
`Balance for ${feature.id} does not match expected balance`,
|
|
).toStrictEqual(expectedBalance);
|
|
};
|