Files
cfw-autumn/server/tests/alex/utils.ts
2025-11-11 18:04:55 +00:00

242 lines
5.7 KiB
TypeScript

import {
Entitlement,
FeatureType,
AllowanceType,
Feature,
} from "@autumn/shared";
import { expect } from "chai";
import { timeout } from "@tests/utils/genUtils.js";
import { AutumnCli } from "@tests/cli/AutumnCli.js";
import { alexFeatures } from "./init.js";
const checkEntitledOnFeatures = async (customerId: string, product: any) => {
const entitlements: Entitlement[] = Object.values(product.entitlements);
for (const entitlement of entitlements) {
const { allowed, balanceObj }: any = await AutumnCli.entitled(
customerId,
entitlement.feature_id!,
true,
);
// Type assertion to tell TypeScript that feature_id is a key of alexFeatures
let feature =
alexFeatures[entitlement.feature_id! as keyof typeof alexFeatures];
try {
if (feature.type === FeatureType.Boolean) {
expect(allowed).to.equal(true);
return;
}
if (entitlement.allowance === 0) {
expect(allowed).to.equal(false);
expect(balanceObj?.balance).to.equal(0);
return;
} else if (entitlement.allowance_type === AllowanceType.Unlimited) {
expect(allowed).to.equal(true);
expect(balanceObj?.balance).to.equal(null);
expect(balanceObj?.unlimited).to.equal(true);
return;
}
expect(allowed).to.equal(true);
expect(balanceObj?.balance).to.equal(entitlement.allowance);
} catch (error) {
console.log("Checking entitlement: ", entitlement);
console.log("Expected balance: ", entitlement.allowance);
console.log("Entitled response: ", { allowed, balanceObj });
throw error;
}
}
};
const getFeatureFromEntitlement = (entitlement: Entitlement) => {
return alexFeatures[
entitlement.feature_id! as keyof typeof alexFeatures
] as Feature & { eventName: string };
};
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).to.equal(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,
);
try {
expect(cusEnt).to.exist;
} catch (error) {
console.log(
`Expected cus ent ${feature.id}, interval ${entitlement.interval} to exist`,
);
throw error;
}
if (entitlement.allowance_type === AllowanceType.Unlimited) {
// Cus ent
expect(cusEnt.balance).to.equal(null);
expect(cusEnt.used).to.equal(null);
expect(cusEnt.unlimited).to.equal(true);
// Entitled res
expect(allowed).to.equal(true);
expect(balanceObj?.balance).to.equal(null);
expect(balanceObj?.unlimited).to.equal(true);
return;
}
if (expectedBalance === 0) {
expect(allowed).to.equal(false);
expect(balanceObj?.balance).to.equal(0);
expect(cusEnt.balance).to.equal(0);
return;
}
expect(balanceObj?.balance).to.equal(expectedBalance);
expect(cusEnt.balance).to.equal(expectedBalance);
};
export const runEventsAndCheckBalances = async ({
customerId,
entitlements,
}: {
customerId: string;
entitlements: Entitlement[];
}) => {
for (const entitlement of entitlements) {
if (
entitlement.allowance === 0 &&
entitlement.allowance_type !== AllowanceType.Unlimited
) {
continue;
}
let feature = getFeatureFromEntitlement(entitlement);
if (
entitlement.allowance_type === AllowanceType.Unlimited ||
feature.type === FeatureType.Boolean
) {
await checkFeatureHasCorrectBalance({
customerId,
feature,
entitlement,
expectedBalance: entitlement.allowance!,
});
continue;
}
// 1. Check that feature has full balance
await checkFeatureHasCorrectBalance({
customerId,
feature,
entitlement,
expectedBalance: entitlement.allowance!,
});
// console.log(" - Running events & entitled check for:", feature.id);
let firstHalf = Math.min(Math.floor(entitlement.allowance! / 2), 50);
let secondHalf = entitlement.allowance! - firstHalf;
// 1. Send first half
let batchUpdate = [];
for (let i = 0; i < firstHalf; i++) {
batchUpdate.push(
AutumnCli.sendEvent({
customerId,
eventName: feature.eventName!,
}),
);
}
let timeoutMilli = Math.max(Math.floor(firstHalf / 2.5), 2) * 2500;
await timeout(timeoutMilli);
await Promise.all(batchUpdate);
await checkFeatureHasCorrectBalance({
customerId,
feature,
entitlement,
expectedBalance: entitlement.allowance! - firstHalf,
});
if (secondHalf > 50) {
continue;
// TODO: Make balance 0 and check that it's blocked...
await AutumnCli.sendEvent({
customerId,
eventName: feature.eventName!,
properties: {
value: secondHalf,
},
});
await timeout(1000);
await checkFeatureHasCorrectBalance({
customerId,
feature,
entitlement,
expectedBalance: 0,
});
return;
}
for (let i = 0; i < secondHalf; i++) {
batchUpdate.push(
AutumnCli.sendEvent({
customerId,
eventName: feature.eventName!,
}),
);
}
await timeout(timeoutMilli);
await Promise.all(batchUpdate);
await checkFeatureHasCorrectBalance({
customerId,
feature,
entitlement,
expectedBalance: 0,
});
}
};