Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

181 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { money } from "./money.ts";
5import {
6 byProject,
7 columns,
8 defaultGrain,
9 isStaff,
10 monthSpan,
11 pendingSentence,
12 productStyle,
13 quantity,
14 receipt,
15 resolveRange,
16 ticks,
17 usageCsv,
18} from "./usage.ts";
19
20const axisMoney = (micros: number) => money(micros, { compact: true });
21
22const NOW = new Date("2026-10-08T15:00:00Z");
23
24test("periods resolve to whole UTC days, both ends included", () => {
25 assert.deepEqual(resolveRange(null, {}, NOW), { period: "cycle", from: "2026-10-01", until: "2026-10-08" });
26 // The current cycle is the one "this month" Home asks billing for.
27 assert.deepEqual(resolveRange("cycle", {}, NOW), { period: "cycle", ...monthSpan(NOW) });
28 assert.deepEqual(monthSpan(NOW), { from: "2026-10-01", until: "2026-10-08" });
29 assert.deepEqual(resolveRange("last_cycle", {}, NOW), { period: "last_cycle", from: "2026-09-01", until: "2026-09-30" });
30 assert.deepEqual(resolveRange("7d", {}, NOW), { period: "7d", from: "2026-10-02", until: "2026-10-08" });
31 assert.deepEqual(resolveRange("custom", { from: "2026-10-05", until: "2026-09-20" }, NOW), { period: "custom", from: "2026-09-20", until: "2026-10-05" });
32 // A custom range that is not days falls back to the cycle.
33 assert.equal(resolveRange("custom", { from: "yesterday", until: "2026-10-01" }, NOW).period, "cycle");
34 assert.equal(resolveRange("nonsense", {}, NOW).period, "cycle");
35});
36
37test("columns cover every day, stack products, and add up when cumulative", () => {
38 const days = [
39 { day: "2026-10-01", product: "deployments", micros: 2_000_000 },
40 { day: "2026-10-01", product: "sandboxes", micros: 500_000 },
41 { day: "2026-10-03", product: "deployments", micros: 1_000_000 },
42 ];
43 const daily = columns(days, "2026-10-01", "2026-10-03");
44 assert.deepEqual(
45 daily.map((c) => c.total),
46 [2_500_000, 0, 1_000_000],
47 );
48 assert.equal(daily[0]!.parts.sandboxes, 500_000);
49 const running = columns(days, "2026-10-01", "2026-10-03", "day", true);
50 assert.deepEqual(
51 running.map((c) => c.total),
52 [2_500_000, 2_500_000, 3_500_000],
53 );
54 assert.equal(running[2]!.parts.deployments, 3_000_000);
55 // Weeks start on Monday: Oct 1 2026 is a Thursday.
56 const weekly = columns(days, "2026-10-01", "2026-10-08", "week");
57 assert.deepEqual(
58 weekly.map((c) => c.key),
59 ["2026-09-28", "2026-10-05"],
60 );
61 assert.equal(weekly[0]!.total, 3_500_000);
62 assert.equal(columns(days, "2026-09-15", "2026-10-03", "month").length, 2);
63 assert.equal(defaultGrain("2026-10-01", "2026-10-31"), "day");
64 assert.equal(defaultGrain("2026-07-01", "2026-10-01"), "week");
65});
66
67test("the money axis has clean ticks, never the same label twice", () => {
68 assert.deepEqual(ticks(0), [0]);
69 const t = ticks(3_320_000);
70 assert.equal(t[0], 0);
71 assert.ok(t[t.length - 1]! >= 3_320_000);
72 const labels = t.map(axisMoney);
73 assert.equal(new Set(labels).size, labels.length, labels.join(" "));
74 const small = ticks(4_000).map(axisMoney);
75 assert.equal(new Set(small).size, small.length, small.join(" "));
76 assert.equal(axisMoney(2_000_000), "$2");
77 assert.equal(axisMoney(1_500_000_000), "$1.5K");
78});
79
80test("quantities read in their units", () => {
81 assert.equal(quantity(1_234_567, "tokens"), "1.2M tokens");
82 assert.equal(quantity(3_725, "seconds"), "1h 2m");
83 assert.equal(quantity(1, "minutes"), "1 minute");
84 assert.equal(quantity(1_240, "minutes"), "1,240 minutes");
85 assert.equal(quantity(504_000_000, "bytes"), "504 MB");
86 assert.equal(quantity(1, "entries"), "1 entry");
87});
88
89test("the receipt shows usage at price, then what paid, then what is charged", () => {
90 const lines = receipt(
91 { priceMicros: 3_320_000, discountMicros: 3_320_000, includedMicros: 0, creditsMicros: 0, chargedMicros: 0, pendingMicros: 0, costMicros: 0 },
92 100,
93 );
94 assert.deepEqual(
95 lines.map((l) => l.label),
96 ["Usage at price", "Discount (100%)", "Charged"],
97 );
98});
99
100test("grouping by project adds a project's meters together", () => {
101 const meter = (label: string, parts: [string, number][]) => ({
102 key: label,
103 label,
104 product: "deployments",
105 unit: "entries",
106 quantity: 0,
107 micros: 0,
108 daily: [],
109 byProject: parts.map(([project, micros]) => ({ project, micros, quantity: 0 })),
110 });
111 const rows = byProject({
112 products: [
113 {
114 key: "deployments",
115 label: "Deployments",
116 micros: 0,
117 meters: [
118 meter("Requests", [
119 ["acme/web", 300],
120 ["acme/api", 500],
121 ]),
122 meter("Domains", [["acme/web", 400]]),
123 ],
124 },
125 ],
126 });
127 assert.deepEqual(
128 rows.map((r) => [r.project, r.micros]),
129 [
130 ["acme/web", 700],
131 ["acme/api", 500],
132 ],
133 );
134});
135
136test("the CSV has a row per day and meter with anything on it", () => {
137 const csv = usageCsv({
138 from: "2026-10-01",
139 until: "2026-10-02",
140 products: [
141 {
142 key: "sandboxes",
143 label: "Sandboxes",
144 micros: 0,
145 meters: [
146 { key: "sandbox_time", label: "Sandbox time", product: "sandboxes", unit: "seconds", quantity: 0, micros: 0, daily: [0, 1_500_000], byProject: [] },
147 ],
148 },
149 ],
150 });
151 assert.equal(csv, "day,product,meter,usd\n2026-10-02,Sandboxes,Sandbox time,1.500000\n");
152});
153
154test("test-mode hints are for g1t's own people", () => {
155 assert.ok(isStaff({ workspaces: [{ slug: "flagon-io" }] }));
156 assert.ok(!isStaff({ workspaces: [{ slug: "acme" }] }));
157 assert.ok(!isStaff(null));
158});
159
160test("the pending note says what the close will charge, with the fraction of a cent that explains a gap", () => {
161 // A comped workspace: $0.009 pending, nothing of it charged.
162 assert.equal(
163 pendingSentence({ pendingMicros: 9_000, pendingChargedMicros: 0 }),
164 "$0.009 of it is metered this month and not yet closed; nothing will be charged when the month closes, after your discount and included usage.",
165 );
166 assert.equal(
167 pendingSentence({ pendingMicros: 9_000, pendingChargedMicros: 6_300 }),
168 "$0.009 of it is metered this month and not yet closed; $0.0063 of it will be charged when the month closes, after your discount and included usage.",
169 );
170 assert.equal(
171 pendingSentence({ pendingMicros: 1_340_000, pendingChargedMicros: 1_340_000 }),
172 "$1.34 of it is metered this month and not yet closed; all of it will be charged when the month closes, after your discount and included usage.",
173 );
174 // An older billing, with no split yet.
175 assert.equal(pendingSentence({ pendingMicros: 9_000 }), "$0.009 of it is metered this month and not yet closed; it is charged when the month closes.");
176});
177
178test("products no longer offered still read in older usage", () => {
179 assert.equal(productStyle("agent").label, "Agent");
180 assert.equal(productStyle("sandboxes").label, "Sandboxes");
181});