125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { assessLagoCatalog, buildDesiredLagoCatalog, toLagoPlanPayload, toLagoTaxPayload, type LagoCatalogSnapshot } from "./catalog";
|
|
|
|
const desired = buildDesiredLagoCatalog();
|
|
|
|
function matchingSnapshot(): LagoCatalogSnapshot {
|
|
return {
|
|
organization: { name: "HiveGrid Test", idSha256: "a".repeat(64) },
|
|
tax: {
|
|
code: desired.tax.code,
|
|
name: desired.tax.name,
|
|
description: desired.tax.description,
|
|
rate: desired.tax.rate,
|
|
applied_to_organization: desired.tax.appliedToOrganization,
|
|
},
|
|
plans: desired.plans.map((plan) => ({
|
|
code: plan.code,
|
|
name: plan.name,
|
|
invoice_display_name: plan.invoiceDisplayName,
|
|
description: plan.description,
|
|
interval: plan.interval,
|
|
amount_cents: plan.amountCents,
|
|
amount_currency: plan.amountCurrency,
|
|
pay_in_advance: plan.payInAdvance,
|
|
taxes: plan.taxCodes.map((code) => ({ code })),
|
|
metadata: { ...plan.metadata },
|
|
})),
|
|
};
|
|
}
|
|
|
|
describe("HiveGrid Lago billing catalog", () => {
|
|
it("builds base plans, recurring storage products, and five email-marketing tiers", () => {
|
|
expect(desired.plans.map((plan) => plan.code)).toEqual([
|
|
"business",
|
|
"business_annual_sepa",
|
|
"email_marketing_l",
|
|
"email_marketing_m",
|
|
"email_marketing_s",
|
|
"email_marketing_xl",
|
|
"email_marketing_xxl",
|
|
"pro",
|
|
"pro_annual_sepa",
|
|
"starter",
|
|
"starter_annual_sepa",
|
|
"storage_100gb",
|
|
"storage_1tb",
|
|
"storage_500gb",
|
|
"storage_5tb",
|
|
]);
|
|
expect(desired.plans.every((plan) => plan.interval === "monthly")).toBe(true);
|
|
expect(desired.plans.every((plan) => plan.taxCodes.includes("de-vat-19"))).toBe(true);
|
|
expect(desired.plans.find((plan) => plan.code === "email_marketing_m")).toMatchObject({
|
|
amountCents: 14_900,
|
|
metadata: {
|
|
hivegrid_resource_kind: "email_marketing_add_on",
|
|
hivegrid_monthly_email_limit: "50000",
|
|
},
|
|
});
|
|
expect(desired.plans.find((plan) => plan.code === "pro")).toMatchObject({
|
|
metadata: {
|
|
hivegrid_included_storage_gb: "500",
|
|
hivegrid_storage_per_additional_seat_gb: "20",
|
|
},
|
|
});
|
|
expect(desired.plans.find((plan) => plan.code === "storage_1tb")).toMatchObject({
|
|
amountCents: 1_299,
|
|
metadata: {
|
|
hivegrid_resource_kind: "storage_add_on",
|
|
hivegrid_included_storage_gb: "1000",
|
|
hivegrid_purchasable: "true",
|
|
},
|
|
});
|
|
expect(desired.plans.find((plan) => plan.code === "storage_100gb")).toMatchObject({
|
|
amountCents: 125,
|
|
metadata: {
|
|
hivegrid_resource_kind: "storage_add_on",
|
|
hivegrid_included_storage_gb: "100",
|
|
hivegrid_purchasable: "false",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("produces a deterministic machine-readable contract digest", () => {
|
|
expect(buildDesiredLagoCatalog()).toEqual(desired);
|
|
expect(desired.digestSha256).toMatch(/^[a-f0-9]{64}$/u);
|
|
});
|
|
|
|
it("reports a complete idempotent catalog as noops", () => {
|
|
const assessment = assessLagoCatalog(matchingSnapshot());
|
|
expect(assessment.complete).toBe(true);
|
|
expect(assessment.actions.every((action) => action.action === "noop")).toBe(true);
|
|
});
|
|
|
|
it("detects price, tax, and catalog-version drift", () => {
|
|
const snapshot = matchingSnapshot();
|
|
snapshot.tax!.rate = 7;
|
|
snapshot.plans[0]!.amount_cents += 1;
|
|
snapshot.plans[1]!.metadata = {
|
|
...snapshot.plans[1]!.metadata,
|
|
hivegrid_catalog_version: "old",
|
|
};
|
|
|
|
const assessment = assessLagoCatalog(snapshot);
|
|
expect(assessment.complete).toBe(false);
|
|
expect(assessment.actions.find((action) => action.resource === "tax")?.driftFields).toContain("rate");
|
|
expect(assessment.actions.find((action) => action.code === snapshot.plans[0]!.code)?.driftFields).toContain("amountCents");
|
|
expect(assessment.actions.find((action) => action.code === snapshot.plans[1]!.code)?.driftFields).toContain("metadata");
|
|
});
|
|
|
|
it("creates explicit Lago tax and non-cascading plan update payloads", () => {
|
|
expect(toLagoTaxPayload(desired.tax)).toMatchObject({
|
|
tax: { code: "de-vat-19", rate: 19, applied_to_organization: false },
|
|
});
|
|
expect(toLagoPlanPayload(desired.plans[0]!, true)).toMatchObject({
|
|
plan: {
|
|
interval: "monthly",
|
|
amount_currency: "EUR",
|
|
tax_codes: ["de-vat-19"],
|
|
cascade_updates: false,
|
|
},
|
|
});
|
|
});
|
|
});
|