| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { axisMoney, byProject, columns, defaultGrain, isStaff, money, quantity, receipt, resolveRange, ticks, usageCsv } from "./usage.ts"; |
| 5 | |
| 6 | const NOW = new Date("2026-10-08T15:00:00Z"); |
| 7 | |
| 8 | test("periods resolve to whole UTC days, both ends included", () => { |
| 9 | assert.deepEqual(resolveRange(null, {}, NOW), { period: "cycle", from: "2026-10-01", until: "2026-10-08" }); |
| 10 | assert.deepEqual(resolveRange("last_cycle", {}, NOW), { period: "last_cycle", from: "2026-09-01", until: "2026-09-30" }); |
| 11 | assert.deepEqual(resolveRange("7d", {}, NOW), { period: "7d", from: "2026-10-02", until: "2026-10-08" }); |
| 12 | assert.deepEqual(resolveRange("custom", { from: "2026-10-05", until: "2026-09-20" }, NOW), { period: "custom", from: "2026-09-20", until: "2026-10-05" }); |
| 13 | // A custom range that is not days falls back to the cycle. |
| 14 | assert.equal(resolveRange("custom", { from: "yesterday", until: "2026-10-01" }, NOW).period, "cycle"); |
| 15 | assert.equal(resolveRange("nonsense", {}, NOW).period, "cycle"); |
| 16 | }); |
| 17 | |
| 18 | test("columns cover every day, stack products, and add up when cumulative", () => { |
| 19 | const days = [ |
| 20 | { day: "2026-10-01", product: "agent", micros: 2_000_000 }, |
| 21 | { day: "2026-10-01", product: "sandboxes", micros: 500_000 }, |
| 22 | { day: "2026-10-03", product: "agent", micros: 1_000_000 }, |
| 23 | ]; |
| 24 | const daily = columns(days, "2026-10-01", "2026-10-03"); |
| 25 | assert.deepEqual(daily.map((c) => c.total), [2_500_000, 0, 1_000_000]); |
| 26 | assert.equal(daily[0]!.parts.sandboxes, 500_000); |
| 27 | const running = columns(days, "2026-10-01", "2026-10-03", "day", true); |
| 28 | assert.deepEqual(running.map((c) => c.total), [2_500_000, 2_500_000, 3_500_000]); |
| 29 | assert.equal(running[2]!.parts.agent, 3_000_000); |
| 30 | // Weeks start on Monday: Oct 1 2026 is a Thursday. |
| 31 | const weekly = columns(days, "2026-10-01", "2026-10-08", "week"); |
| 32 | assert.deepEqual(weekly.map((c) => c.key), ["2026-09-28", "2026-10-05"]); |
| 33 | assert.equal(weekly[0]!.total, 3_500_000); |
| 34 | assert.equal(columns(days, "2026-09-15", "2026-10-03", "month").length, 2); |
| 35 | assert.equal(defaultGrain("2026-10-01", "2026-10-31"), "day"); |
| 36 | assert.equal(defaultGrain("2026-07-01", "2026-10-01"), "week"); |
| 37 | }); |
| 38 | |
| 39 | test("the money axis has clean ticks, never the same label twice", () => { |
| 40 | assert.deepEqual(ticks(0), [0]); |
| 41 | const t = ticks(3_320_000); |
| 42 | assert.equal(t[0], 0); |
| 43 | assert.ok(t[t.length - 1]! >= 3_320_000); |
| 44 | const labels = t.map(axisMoney); |
| 45 | assert.equal(new Set(labels).size, labels.length, labels.join(" ")); |
| 46 | const small = ticks(4_000).map(axisMoney); |
| 47 | assert.equal(new Set(small).size, small.length, small.join(" ")); |
| 48 | assert.equal(axisMoney(2_000_000), "$2"); |
| 49 | assert.equal(axisMoney(1_500_000_000), "$1.5K"); |
| 50 | }); |
| 51 | |
| 52 | test("money and quantities read in their units", () => { |
| 53 | assert.equal(money(2_760_000), "$2.76"); |
| 54 | assert.equal(money(4_000), "$0.004"); |
| 55 | assert.equal(money(0), "$0.00"); |
| 56 | assert.equal(quantity(1_234_567, "tokens"), "1.2M tokens"); |
| 57 | assert.equal(quantity(3_725, "seconds"), "1h 2m"); |
| 58 | assert.equal(quantity(504_000_000, "bytes"), "504 MB"); |
| 59 | assert.equal(quantity(1, "entries"), "1 entry"); |
| 60 | }); |
| 61 | |
| 62 | test("the receipt shows usage at price, then what paid, then what is charged", () => { |
| 63 | const lines = receipt( |
| 64 | { priceMicros: 3_320_000, discountMicros: 3_320_000, includedMicros: 0, creditsMicros: 0, chargedMicros: 0, pendingMicros: 0, costMicros: 0 }, |
| 65 | 100, |
| 66 | ); |
| 67 | assert.deepEqual(lines.map((l) => l.label), ["Usage at price", "Discount (100%)", "Charged"]); |
| 68 | }); |
| 69 | |
| 70 | test("grouping by project adds a project's meters together", () => { |
| 71 | const meter = (label: string, parts: [string, number][]) => ({ |
| 72 | key: label, label, product: "agent", unit: "entries", quantity: 0, micros: 0, daily: [], |
| 73 | byProject: parts.map(([project, micros]) => ({ project, micros, quantity: 0 })), |
| 74 | }); |
| 75 | const rows = byProject({ |
| 76 | products: [{ key: "agent", label: "Agent", micros: 0, meters: [meter("Model tokens", [["acme/web", 300], ["acme/api", 500]]), meter("Agent rate", [["acme/web", 400]])] }], |
| 77 | }); |
| 78 | assert.deepEqual(rows.map((r) => [r.project, r.micros]), [["acme/web", 700], ["acme/api", 500]]); |
| 79 | }); |
| 80 | |
| 81 | test("the CSV has a row per day and meter with anything on it", () => { |
| 82 | const csv = usageCsv({ |
| 83 | from: "2026-10-01", |
| 84 | until: "2026-10-02", |
| 85 | products: [{ key: "agent", label: "Agent", micros: 0, meters: [{ key: "agent_models", label: "Model tokens", product: "agent", unit: "tokens", quantity: 0, micros: 0, daily: [0, 1_500_000], byProject: [] }] }], |
| 86 | }); |
| 87 | assert.equal(csv, "day,product,meter,usd\n2026-10-02,Agent,Model tokens,1.500000\n"); |
| 88 | }); |
| 89 | |
| 90 | test("test-mode hints are for g1t's own people", () => { |
| 91 | assert.ok(isStaff({ workspaces: [{ slug: "flagon-io" }] })); |
| 92 | assert.ok(!isStaff({ workspaces: [{ slug: "acme" }] })); |
| 93 | assert.ok(!isStaff(null)); |
| 94 | }); |
| 95 | |
| 96 | test("money under a hundredth of a cent reads $0.00, never $0.0000", () => { |
| 97 | assert.equal(money(3), "$0.00"); |
| 98 | assert.equal(money(0), "$0.00"); |
| 99 | assert.equal(money(4_000), "$0.004"); |
| 100 | assert.equal(money(250), "$0.0003"); |
| 101 | }); |