429 lines
12 KiB
TypeScript
429 lines
12 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
ABSOLUTE_ALERT_BASE_CENTS,
|
|
clearedAlertsPayload,
|
|
emailsMatchSaved,
|
|
getAlertPreviewLimitCents,
|
|
getBillingLimitMode,
|
|
getConfiguredBillingLimitCents,
|
|
getUsageBarBillingLimitDollars,
|
|
hadSavedAlertsToClearOnLimitChange,
|
|
hasConfiguredAlerts,
|
|
hasLegacySpikeAlertLevels,
|
|
isLegacyDollarAmountField,
|
|
normalizeBillingAlertsFromApi,
|
|
percentageAlertLevelsToUiThresholds,
|
|
previewDollarAmountForPercent,
|
|
shouldClearAlertsOnLimitChange,
|
|
shouldResetAlertsOnLimitChange,
|
|
storedAlertsToThresholds,
|
|
thresholdsMatchSaved,
|
|
thresholdsToAlertPayload,
|
|
thresholdValuesAreUnique,
|
|
} from "~/components/billing/billingAlertsFormat";
|
|
|
|
const legacyDefaultLevels = [0.75, 0.9, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0];
|
|
|
|
const hundredDollarLimitContext = {
|
|
planLimitCents: 10_000,
|
|
effectiveLimitCents: 10_000,
|
|
};
|
|
|
|
const fiveDollarLimitContext = {
|
|
planLimitCents: 500,
|
|
effectiveLimitCents: 500,
|
|
};
|
|
|
|
describe("billingAlertsFormat", () => {
|
|
it("uses percentage thresholds saved in the new format", () => {
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 50, emails: [], alertLevels: [0.75, 0.9, 1.0] },
|
|
"plan",
|
|
5000,
|
|
5000
|
|
)
|
|
).toEqual([75, 90, 100]);
|
|
});
|
|
|
|
it("filters legacy spike multipliers above 100%", () => {
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 50, emails: [], alertLevels: legacyDefaultLevels },
|
|
"plan",
|
|
5000,
|
|
5000
|
|
)
|
|
).toEqual([75, 90, 100]);
|
|
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 50, emails: [], alertLevels: legacyDefaultLevels },
|
|
"none",
|
|
5000,
|
|
5000
|
|
)
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("reads legacy alerts saved against plan included usage", () => {
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 100, emails: [], alertLevels: [0.1, 0.5, 0.8, 2.0] },
|
|
"plan",
|
|
25_000,
|
|
10_000
|
|
)
|
|
).toEqual([10, 50, 80]);
|
|
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 100, emails: [], alertLevels: [10, 50, 80, 200] },
|
|
"plan",
|
|
25_000,
|
|
10_000
|
|
)
|
|
).toEqual([10, 50, 80]);
|
|
|
|
expect(
|
|
getAlertPreviewLimitCents({ amount: 100, emails: [], alertLevels: [] }, 25_000, 10_000)
|
|
).toBe(10_000);
|
|
});
|
|
|
|
it("normalizes legacy API alerts with dollar amount field and whole percents", () => {
|
|
expect(
|
|
normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: 10_000,
|
|
emails: ["a@example.com"],
|
|
alertLevels: [10, 50, 80, 200],
|
|
},
|
|
hundredDollarLimitContext
|
|
)
|
|
).toEqual({
|
|
amount: 100,
|
|
emails: ["a@example.com"],
|
|
alertLevels: [10, 50, 80, 200],
|
|
});
|
|
|
|
expect(percentageAlertLevelsToUiThresholds([10, 50, 80, 200])).toEqual([10, 50, 80]);
|
|
});
|
|
|
|
it("normalizes platform API alerts stored in cents", () => {
|
|
expect(
|
|
normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: 10_000,
|
|
emails: [],
|
|
alertLevels: [0.75, 0.9],
|
|
},
|
|
hundredDollarLimitContext
|
|
)
|
|
).toEqual({
|
|
amount: 100,
|
|
emails: [],
|
|
alertLevels: [0.75, 0.9],
|
|
});
|
|
});
|
|
|
|
it("normalizes cents-based alerts for billing limits under $10", () => {
|
|
const normalized = normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: 500,
|
|
emails: [],
|
|
alertLevels: [0.75, 0.9],
|
|
},
|
|
fiveDollarLimitContext
|
|
);
|
|
|
|
expect(normalized).toEqual({
|
|
amount: 5,
|
|
emails: [],
|
|
alertLevels: [0.75, 0.9],
|
|
});
|
|
|
|
expect(storedAlertsToThresholds(normalized, "plan", 500, 500)).toEqual([75, 90]);
|
|
|
|
expect(getAlertPreviewLimitCents(normalized, 500, 500)).toBe(500);
|
|
expect(previewDollarAmountForPercent(75, getAlertPreviewLimitCents(normalized, 500, 500))).toBe(
|
|
3.75
|
|
);
|
|
});
|
|
|
|
it("normalizes cents-based alerts with whole-number levels when amount matches limit", () => {
|
|
const normalized = normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: 500,
|
|
emails: [],
|
|
alertLevels: [50, 75],
|
|
},
|
|
{ planLimitCents: 500, effectiveLimitCents: 500 }
|
|
);
|
|
|
|
expect(normalized).toEqual({
|
|
amount: 5,
|
|
emails: [],
|
|
alertLevels: [50, 75],
|
|
});
|
|
|
|
expect(storedAlertsToThresholds(normalized, "custom", 500, 500)).toEqual([50, 75]);
|
|
});
|
|
|
|
it("still normalizes legacy dollar alerts when amount matches plan dollars", () => {
|
|
expect(
|
|
normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: 100,
|
|
emails: [],
|
|
alertLevels: [10, 50, 80],
|
|
},
|
|
{ planLimitCents: 10_000, effectiveLimitCents: 10_000 }
|
|
)
|
|
).toEqual({
|
|
amount: 100,
|
|
emails: [],
|
|
alertLevels: [10, 50, 80],
|
|
});
|
|
|
|
expect(
|
|
isLegacyDollarAmountField(500, [50, 75], {
|
|
planLimitCents: 50_000,
|
|
effectiveLimitCents: 50_000,
|
|
})
|
|
).toBe(true);
|
|
|
|
expect(
|
|
isLegacyDollarAmountField(250, [10, 50, 80], {
|
|
planLimitCents: 25_000,
|
|
effectiveLimitCents: 25_000,
|
|
})
|
|
).toBe(true);
|
|
|
|
expect(isLegacyDollarAmountField(250, [10, 50, 80], hundredDollarLimitContext)).toBe(false);
|
|
});
|
|
|
|
it("returns no default thresholds when alerts are empty", () => {
|
|
expect(
|
|
storedAlertsToThresholds({ amount: 50, emails: [], alertLevels: [] }, "plan", 5000, 5000)
|
|
).toEqual([]);
|
|
expect(
|
|
storedAlertsToThresholds({ amount: 1, emails: [], alertLevels: [] }, "none", 5000, 5000)
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("uses dollar thresholds for none mode with absolute base", () => {
|
|
expect(
|
|
storedAlertsToThresholds(
|
|
{ amount: 1, emails: [], alertLevels: [100, 250] },
|
|
"none",
|
|
5000,
|
|
5000
|
|
)
|
|
).toEqual([100, 250]);
|
|
});
|
|
|
|
it("loads absolute dollar alerts after save with unlimited billing limit", () => {
|
|
const normalized = normalizeBillingAlertsFromApi(
|
|
{
|
|
amount: ABSOLUTE_ALERT_BASE_CENTS,
|
|
emails: ["a@example.com"],
|
|
alertLevels: [100],
|
|
},
|
|
{ planLimitCents: 5000, effectiveLimitCents: 5000 }
|
|
);
|
|
|
|
expect(normalized).toEqual({
|
|
amount: 1,
|
|
emails: ["a@example.com"],
|
|
alertLevels: [100],
|
|
});
|
|
|
|
expect(storedAlertsToThresholds(normalized, "none", 5000, 5000)).toEqual([100]);
|
|
});
|
|
|
|
it("converts percentage UI values to API payload", () => {
|
|
expect(thresholdsToAlertPayload([75, 90], "plan", 5000)).toEqual({
|
|
amount: 5000,
|
|
alertLevels: [0.75, 0.9],
|
|
});
|
|
});
|
|
|
|
it("converts absolute UI values to API payload", () => {
|
|
expect(thresholdsToAlertPayload([100, 250], "none", 5000)).toEqual({
|
|
amount: 100,
|
|
alertLevels: [100, 250],
|
|
});
|
|
});
|
|
|
|
it("previews dollar amount from percentage and limit", () => {
|
|
expect(previewDollarAmountForPercent(75, 5000)).toBe(37.5);
|
|
expect(previewDollarAmountForPercent(10, 10_000)).toBe(10);
|
|
});
|
|
|
|
it("defaults unconfigured billing limit to none mode", () => {
|
|
expect(getBillingLimitMode({ isConfigured: false, gracePeriodMs: 86_400_000 })).toBe("none");
|
|
});
|
|
|
|
it("detects configured alerts for the current billing limit mode", () => {
|
|
const billingLimit = {
|
|
isConfigured: true,
|
|
mode: "plan" as const,
|
|
effectiveAmountCents: 5000,
|
|
gracePeriodMs: 86_400_000,
|
|
};
|
|
|
|
expect(
|
|
hasConfiguredAlerts({ amount: 50, emails: [], alertLevels: [0.75, 0.9] }, billingLimit, 5000)
|
|
).toBe(true);
|
|
|
|
expect(
|
|
hasConfiguredAlerts({ amount: 50, emails: [], alertLevels: [] }, billingLimit, 5000)
|
|
).toBe(false);
|
|
});
|
|
|
|
it("clears percentage alerts when switching from plan or custom to none", () => {
|
|
expect(shouldClearAlertsOnLimitChange("plan", "none")).toBe(true);
|
|
expect(shouldClearAlertsOnLimitChange("custom", "none")).toBe(true);
|
|
expect(shouldClearAlertsOnLimitChange("none", "none")).toBe(false);
|
|
expect(shouldClearAlertsOnLimitChange("plan", "custom")).toBe(false);
|
|
});
|
|
|
|
it("resets alerts when switching between percentage and dollar alert modes", () => {
|
|
expect(shouldResetAlertsOnLimitChange("none", "plan")).toBe(true);
|
|
expect(shouldResetAlertsOnLimitChange("none", "custom")).toBe(true);
|
|
expect(shouldResetAlertsOnLimitChange("plan", "none")).toBe(true);
|
|
expect(shouldResetAlertsOnLimitChange("plan", "custom")).toBe(false);
|
|
});
|
|
|
|
it("builds a cleared alerts payload for none mode", () => {
|
|
expect(clearedAlertsPayload(["a@example.com"])).toEqual({
|
|
amount: 100,
|
|
alertLevels: [],
|
|
emails: ["a@example.com"],
|
|
});
|
|
});
|
|
|
|
it("detects legacy spike alert levels above 100%", () => {
|
|
expect(
|
|
hasLegacySpikeAlertLevels(
|
|
{ amount: 50, emails: [], alertLevels: [0.75, 0.9, 1.0, 2.0] },
|
|
"plan",
|
|
5000,
|
|
5000
|
|
)
|
|
).toBe(true);
|
|
|
|
expect(
|
|
hasLegacySpikeAlertLevels(
|
|
{ amount: 100, emails: [], alertLevels: [0.1, 0.5, 0.8, 2.0] },
|
|
"plan",
|
|
25_000,
|
|
10_000
|
|
)
|
|
).toBe(true);
|
|
|
|
expect(
|
|
hasLegacySpikeAlertLevels(
|
|
{ amount: 50, emails: [], alertLevels: [0.75, 0.9] },
|
|
"plan",
|
|
5000,
|
|
5000
|
|
)
|
|
).toBe(false);
|
|
|
|
expect(
|
|
hasLegacySpikeAlertLevels(
|
|
{ amount: 1, emails: [], alertLevels: [100, 250] },
|
|
"none",
|
|
5000,
|
|
5000
|
|
)
|
|
).toBe(false);
|
|
});
|
|
|
|
it("detects when saved alerts should be cleared on a limit format change", () => {
|
|
const billingLimit = {
|
|
isConfigured: true,
|
|
mode: "plan" as const,
|
|
effectiveAmountCents: 5000,
|
|
gracePeriodMs: 86_400_000,
|
|
};
|
|
|
|
expect(
|
|
hadSavedAlertsToClearOnLimitChange(
|
|
{ amount: 50, emails: [], alertLevels: [0.75, 0.9] },
|
|
billingLimit,
|
|
5000
|
|
)
|
|
).toBe(true);
|
|
|
|
expect(
|
|
hadSavedAlertsToClearOnLimitChange(
|
|
{ amount: 50, emails: ["a@example.com"], alertLevels: [] },
|
|
billingLimit,
|
|
5000
|
|
)
|
|
).toBe(false);
|
|
});
|
|
|
|
it("compares threshold and email values for dirty form state", () => {
|
|
expect(thresholdsMatchSaved([90, 75], [75, 90])).toBe(true);
|
|
expect(thresholdsMatchSaved([75], [75, 90])).toBe(false);
|
|
expect(emailsMatchSaved(["a@example.com", ""], ["a@example.com"])).toBe(true);
|
|
expect(emailsMatchSaved(["b@example.com"], ["a@example.com"])).toBe(false);
|
|
});
|
|
|
|
it("detects duplicate alert thresholds", () => {
|
|
expect(thresholdValuesAreUnique([75, 90, 100])).toBe(true);
|
|
expect(thresholdValuesAreUnique([75, 75])).toBe(false);
|
|
expect(thresholdValuesAreUnique([100, 250, 100])).toBe(false);
|
|
});
|
|
|
|
it("returns configured billing limit cents for plan and custom modes", () => {
|
|
expect(
|
|
getConfiguredBillingLimitCents(
|
|
{
|
|
isConfigured: true,
|
|
mode: "custom",
|
|
amountCents: 25_000,
|
|
cancelInProgressRuns: false,
|
|
limitState: { status: "ok" },
|
|
effectiveAmountCents: 25_000,
|
|
gracePeriodMs: 86_400_000,
|
|
},
|
|
5_000
|
|
)
|
|
).toBe(25_000);
|
|
|
|
expect(
|
|
getConfiguredBillingLimitCents(
|
|
{
|
|
isConfigured: true,
|
|
mode: "none",
|
|
cancelInProgressRuns: false,
|
|
limitState: { status: "ok" },
|
|
effectiveAmountCents: null,
|
|
gracePeriodMs: 86_400_000,
|
|
},
|
|
5_000
|
|
)
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("maps usage bar billing limit dollars and hides when same as plan limit", () => {
|
|
const customLimit = {
|
|
isConfigured: true as const,
|
|
mode: "custom" as const,
|
|
amountCents: 25_000,
|
|
cancelInProgressRuns: false,
|
|
limitState: { status: "ok" as const },
|
|
effectiveAmountCents: 25_000,
|
|
gracePeriodMs: 86_400_000,
|
|
};
|
|
|
|
expect(getUsageBarBillingLimitDollars(customLimit, 5_000)).toBe(250);
|
|
expect(getUsageBarBillingLimitDollars(customLimit, 25_000)).toBeUndefined();
|
|
expect(getUsageBarBillingLimitDollars(undefined, 5_000)).toBeUndefined();
|
|
});
|
|
});
|