141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
|
|
import { SchemaError } from "@aigxion/error";
|
|
import { BillingTier, calculateTieredPrices } from "./tiers";
|
|
|
|
describe("calculateTieredPrices", () => {
|
|
const testCases: {
|
|
name: string;
|
|
tiers: BillingTier[];
|
|
units: number;
|
|
expected: number;
|
|
}[] = [
|
|
{
|
|
name: "only reaches the first tier",
|
|
tiers: [
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "100" },
|
|
{ firstUnit: 11, lastUnit: 20, centsPerUnit: "50" },
|
|
{ firstUnit: 21, lastUnit: null, centsPerUnit: "25" },
|
|
],
|
|
units: 5,
|
|
expected: 500,
|
|
},
|
|
{
|
|
name: "only reaches the second tier",
|
|
tiers: [
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "100" },
|
|
{ firstUnit: 11, lastUnit: 20, centsPerUnit: "50" },
|
|
{ firstUnit: 21, lastUnit: null, centsPerUnit: "25" },
|
|
],
|
|
units: 15,
|
|
expected: 1250,
|
|
},
|
|
{
|
|
name: "reaches the third tier",
|
|
tiers: [
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "100" },
|
|
{ firstUnit: 11, lastUnit: 20, centsPerUnit: "50" },
|
|
{ firstUnit: 21, lastUnit: null, centsPerUnit: "25" },
|
|
],
|
|
units: 25,
|
|
expected: 1625, // 10*1 + 10*0.5 + 5*0.25
|
|
},
|
|
{
|
|
name: "single tier",
|
|
tiers: [{ firstUnit: 1, lastUnit: null, centsPerUnit: "32.3" }],
|
|
units: 12125,
|
|
expected: 391637.5,
|
|
},
|
|
{
|
|
name: "real world usage",
|
|
tiers: [
|
|
{ firstUnit: 1, lastUnit: 2_500, centsPerUnit: null },
|
|
{ firstUnit: 2_501, lastUnit: 100_000, centsPerUnit: "0.02" },
|
|
{ firstUnit: 100_001, lastUnit: 500_000, centsPerUnit: "0.015" },
|
|
{ firstUnit: 500_001, lastUnit: 1_000_000, centsPerUnit: "0.01" },
|
|
{ firstUnit: 1_000_001, lastUnit: null, centsPerUnit: "0.005" },
|
|
],
|
|
units: 3899437,
|
|
expected: 27447.185,
|
|
},
|
|
];
|
|
for (const tc of testCases) {
|
|
test(tc.name, () => {
|
|
const result = calculateTieredPrices(tc.tiers, tc.units);
|
|
expect(result.err).toBeUndefined();
|
|
expect(result.val).toBeDefined();
|
|
expect(result.val!.totalCentsEstimate).toBeCloseTo(tc.expected, 9);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("invalid tiers", () => {
|
|
describe("empty array", () => {
|
|
test("should fail", () => {
|
|
const result = calculateTieredPrices([], 5);
|
|
expect(result.err).toBeDefined();
|
|
expect(result.err).instanceOf(SchemaError);
|
|
|
|
expect(result.err?.message).toEqual("too_small: : Array must contain at least 1 element(s)");
|
|
});
|
|
});
|
|
|
|
describe("there is a gap between tiers", () => {
|
|
test("should fail", () => {
|
|
const result = calculateTieredPrices(
|
|
[
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "50" },
|
|
{ firstUnit: 12, lastUnit: 20, centsPerUnit: "100" },
|
|
],
|
|
20,
|
|
);
|
|
expect(result.err).toBeDefined();
|
|
expect(result.err).instanceOf(SchemaError);
|
|
expect(result.err?.message).toEqual("There is a gap between tiers");
|
|
});
|
|
});
|
|
describe("two tiers overlap", () => {
|
|
test("should fail", () => {
|
|
const result = calculateTieredPrices(
|
|
[
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "100" },
|
|
{ firstUnit: 10, lastUnit: null, centsPerUnit: "50" },
|
|
],
|
|
20,
|
|
);
|
|
expect(result.err).toBeDefined();
|
|
expect(result.err).instanceOf(SchemaError);
|
|
|
|
expect(result.err?.message).toEqual("There is an overlap between tiers");
|
|
});
|
|
});
|
|
describe("two tiers have a gap between", () => {
|
|
test("should fail", () => {
|
|
const result = calculateTieredPrices(
|
|
[
|
|
{ firstUnit: 1, lastUnit: 10, centsPerUnit: "100" },
|
|
{ firstUnit: 12, lastUnit: null, centsPerUnit: "50" },
|
|
],
|
|
20,
|
|
);
|
|
expect(result.err).toBeDefined();
|
|
expect(result.err).instanceOf(SchemaError);
|
|
expect(result.err!.message).toEqual("There is a gap between tiers");
|
|
});
|
|
});
|
|
|
|
describe("any tier but the last has no lastUnit set", () => {
|
|
test("should fail", () => {
|
|
const { val, err } = calculateTieredPrices(
|
|
[
|
|
{ firstUnit: 1, lastUnit: null, centsPerUnit: "100" },
|
|
{ firstUnit: 11, lastUnit: null, centsPerUnit: "50" },
|
|
],
|
|
20,
|
|
);
|
|
expect(val).toBeUndefined();
|
|
expect(err!.message).toEqual("Every tier except the last one must have a lastUnit");
|
|
});
|
|
});
|
|
});
|