import { describe, expect, it } from "vite-plus/test"; import { buildPeriodColumns, niceScale } from "./UsageProviderChart";import { providersWithUsage } from "./usageProviders"; describe("niceScale", () => { it("never puts the peak above the top of the scale", () => { // Regression: an earlier version stopped at the last step below the peak, // so the tallest day was drawn past the plot and clipped. for (const peak of [1122.71, 999, 1, 0.04, 1_400_000_000, 37.5, 5000, 100.001]) { const { max } = niceScale(peak, 4); expect(max, `peak ${peak}`).toBeGreaterThanOrEqual(peak); } }); it("starts at zero and ends at the maximum", () => { const { max, ticks } = niceScale(1122.71, 4); expect(ticks[0]).toBe(0); expect(ticks[ticks.length - 1]).toBeCloseTo(max, 6); }); it("uses evenly spaced 1/2/5 steps", () => { const { ticks } = niceScale(1122.71, 4); const steps = ticks.slice(1).map((tick, index) => tick - (ticks[index] ?? 0)); for (const step of steps) expect(step).toBeCloseTo(steps[0] ?? 0, 6); const [first = 0] = steps; const normalized = first / 10 ** Math.floor(Math.log10(first)); expect([1, 2, 5, 10]).toContain(Math.round(normalized)); }); it("keeps the tick count near the requested resolution", () => { const { ticks } = niceScale(1122.71, 4); expect(ticks.length).toBeGreaterThanOrEqual(3); expect(ticks.length).toBeLessThanOrEqual(7); }); it("degrades to a single zero tick with no data", () => { expect(niceScale(0, 4)).toEqual({ max: 0, ticks: [0] }); });}); describe("buildPeriodColumns", () => { const days = ["2026-08-01", "2026-08-02", "2026-08-03"]; const byDay = new Map([ [ "2026-08-01", { day: "2026-08-01", costUsd: 30, totalTokens: 300, byProvider: new Map([ ["codex" as const, { costUsd: 10, totalTokens: 100 }], ["claude" as const, { costUsd: 20, totalTokens: 200 }], ]), }, ], // 2026-08-02 is deliberately absent: a day with no activity. [ "2026-08-03", { day: "2026-08-03", costUsd: 5, totalTokens: 50, byProvider: new Map([["claude" as const, { costUsd: 5, totalTokens: 50 }]]), }, ], ]); it("plots each day on its own", () => { expect(buildPeriodColumns(days, byDay, "cost").map((column) => column.total)).toEqual([ 30, 0, 5, ]); }); it("reads the requested metric", () => { expect(buildPeriodColumns(days, byDay, "tokens").map((column) => column.total)).toEqual([ 300, 0, 50, ]); }); it("keeps band values absolute rather than cumulative", () => { // Regression: the bands were once stack offsets, which drew Claude Code // permanently above Codex regardless of which provider spent more. const [first] = buildPeriodColumns(days, byDay, "cost"); expect(first?.bands).toEqual([ { provider: "codex", value: 10 }, { provider: "claude", value: 20 }, { provider: "grok", value: 0 }, ]); }); it("reports the total as the sum of its bands", () => { for (const column of buildPeriodColumns(days, byDay, "cost")) { const sum = column.bands.reduce((running, band) => running + band.value, 0); expect(column.total).toBeCloseTo(sum, 9); } });}); describe("providersWithUsage", () => { it("omits providers with no cost or tokens", () => { expect( providersWithUsage([ { provider: "codex", costUsd: 0, totalTokens: 0 }, { provider: "claude", costUsd: 0, totalTokens: 200 }, ]), ).toEqual(["claude"]); });}); describe("hourly chart columns", () => { it("zero-fills inactive hours and preserves hourly provider values", () => { const byHour = new Map([ [ "2026-08-11T09:37:00.000Z", { day: "2026-08-11", hourStart: "2026-08-11T09:37:00.000Z", costUsd: 4, totalTokens: 40, byProvider: new Map([["codex" as const, { costUsd: 4, totalTokens: 40 }]]), }, ], ]); expect( buildPeriodColumns( ["2026-08-11T08:37:00.000Z", "2026-08-11T09:37:00.000Z", "2026-08-11T10:37:00.000Z"], byHour, "cost", ).map((column) => column.total), ).toEqual([0, 4, 0]); });});
import { describe, expect, it } from "vite-plus/test";
import { buildPeriodColumns, niceScale } from "./UsageProviderChart";
import { providersWithUsage } from "./usageProviders";
describe("niceScale", () => {
it("never puts the peak above the top of the scale", () => {
// Regression: an earlier version stopped at the last step below the peak,
// so the tallest day was drawn past the plot and clipped.
for (const peak of [1122.71, 999, 1, 0.04, 1_400_000_000, 37.5, 5000, 100.001]) {
const { max } = niceScale(peak, 4);
expect(max, `peak ${peak}`).toBeGreaterThanOrEqual(peak);
}
});
it("starts at zero and ends at the maximum", () => {
const { max, ticks } = niceScale(1122.71, 4);
expect(ticks[0]).toBe(0);
expect(ticks[ticks.length - 1]).toBeCloseTo(max, 6);
});
it("uses evenly spaced 1/2/5 steps", () => {
const { ticks } = niceScale(1122.71, 4);
const steps = ticks.slice(1).map((tick, index) => tick - (ticks[index] ?? 0));
for (const step of steps) expect(step).toBeCloseTo(steps[0] ?? 0, 6);
const [first = 0] = steps;
const normalized = first / 10 ** Math.floor(Math.log10(first));
expect([1, 2, 5, 10]).toContain(Math.round(normalized));
});
it("keeps the tick count near the requested resolution", () => {
const { ticks } = niceScale(1122.71, 4);
expect(ticks.length).toBeGreaterThanOrEqual(3);
expect(ticks.length).toBeLessThanOrEqual(7);
});
it("degrades to a single zero tick with no data", () => {
expect(niceScale(0, 4)).toEqual({ max: 0, ticks: [0] });
});
});
describe("buildPeriodColumns", () => {
const days = ["2026-08-01", "2026-08-02", "2026-08-03"];
const byDay = new Map([
[
"2026-08-01",
{
day: "2026-08-01",
costUsd: 30,
totalTokens: 300,
byProvider: new Map([
["codex" as const, { costUsd: 10, totalTokens: 100 }],
["claude" as const, { costUsd: 20, totalTokens: 200 }],
]),
},
],
// 2026-08-02 is deliberately absent: a day with no activity.
[
"2026-08-03",
{
day: "2026-08-03",
costUsd: 5,
totalTokens: 50,
byProvider: new Map([["claude" as const, { costUsd: 5, totalTokens: 50 }]]),
},
],
]);
it("plots each day on its own", () => {
expect(buildPeriodColumns(days, byDay, "cost").map((column) => column.total)).toEqual([
30, 0, 5,
]);
});
it("reads the requested metric", () => {
expect(buildPeriodColumns(days, byDay, "tokens").map((column) => column.total)).toEqual([
300, 0, 50,
]);
});
it("keeps band values absolute rather than cumulative", () => {
// Regression: the bands were once stack offsets, which drew Claude Code
// permanently above Codex regardless of which provider spent more.
const [first] = buildPeriodColumns(days, byDay, "cost");
expect(first?.bands).toEqual([
{ provider: "codex", value: 10 },
{ provider: "claude", value: 20 },
{ provider: "grok", value: 0 },
]);
});
it("reports the total as the sum of its bands", () => {
for (const column of buildPeriodColumns(days, byDay, "cost")) {
const sum = column.bands.reduce((running, band) => running + band.value, 0);
expect(column.total).toBeCloseTo(sum, 9);
}
});
});
describe("providersWithUsage", () => {
it("omits providers with no cost or tokens", () => {
expect(
providersWithUsage([
{ provider: "codex", costUsd: 0, totalTokens: 0 },
{ provider: "claude", costUsd: 0, totalTokens: 200 },
]),
).toEqual(["claude"]);
});
});
describe("hourly chart columns", () => {
it("zero-fills inactive hours and preserves hourly provider values", () => {
const byHour = new Map([
[
"2026-08-11T09:37:00.000Z",
{
day: "2026-08-11",
hourStart: "2026-08-11T09:37:00.000Z",
costUsd: 4,
totalTokens: 40,
byProvider: new Map([["codex" as const, { costUsd: 4, totalTokens: 40 }]]),
},
],
]);
expect(
buildPeriodColumns(
["2026-08-11T08:37:00.000Z", "2026-08-11T09:37:00.000Z", "2026-08-11T10:37:00.000Z"],
byHour,
"cost",
).map((column) => column.total),
).toEqual([0, 4, 0]);
});
});