Skip to content
113 linesCodeBlameRaw
1// The platform usage report, from GraphQL answers as Cloudflare gives them,
2// without the network.
3
4import assert from "node:assert/strict";
5import { test } from "node:test";
6
7import { DATASETS, assemble, buildQuery, format, logsDataset, parseArgs, parseGroups, snake, windows } from "./platform-usage.mjs";
8
9const NOW = new Date("2026-10-08T15:30:00.000Z");
10const answer = (rows) => ({ data: { viewer: { accounts: [{ rows }] } } });
11const variantOf = (key, at = 0) => DATASETS.find((spec) => spec.key === key).variants[at];
12
13test("the windows are the UTC month so far and the last 24 hours", () => {
14 const [month, day] = windows(NOW);
15 assert.deepEqual(month, { key: "month_to_date", label: "Month to date (UTC)", start: "2026-10-01T00:00:00.000Z", end: "2026-10-08T15:30:00.000Z" });
16 assert.equal(day.key, "last_24h");
17 assert.equal(day.start, "2026-10-07T15:30:00.000Z");
18 assert.equal(day.end, NOW.toISOString());
19 // Just after midnight on the 1st, the month has only begun.
20 assert.equal(windows(new Date("2026-11-01T00:05:00Z"))[0].start, "2026-11-01T00:00:00.000Z");
21});
22
23test("a query asks for the variant's sums and dimensions under one alias", () => {
24 const query = buildQuery("kvOperationsAdaptiveGroups", variantOf("kv"));
25 assert.match(query, /rows: kvOperationsAdaptiveGroups\(limit: 10000, filter: \{ datetime_geq: \$start, datetime_leq: \$end \}\)/);
26 assert.match(query, /sum \{ requests \}/);
27 assert.match(query, /dimensions \{ namespaceId actionType \}/);
28 assert.match(query, /accounts\(filter: \{ accountTag: \$accountTag \}\)/);
29 const artifacts = buildQuery("artifactsEventsAdaptiveGroups", variantOf("artifacts"));
30 assert.match(artifacts, /\bcount\b/);
31 const minute = buildQuery("durableObjectsPeriodicGroups", variantOf("durable_objects_periodic", 2));
32 assert.match(minute, /datetimeMinute_geq: \$start, datetimeMinute_leq: \$end/);
33});
34
35test("Workers Logs are read from whichever dataset the schema has, or skipped", () => {
36 assert.equal(logsDataset(["workersInvocationsAdaptive", "workersObservabilityEventsAdaptiveGroups"]), "workersObservabilityEventsAdaptiveGroups");
37 assert.equal(logsDataset(["workersLogsSomethingGroups"]), "workersLogsSomethingGroups");
38 assert.equal(logsDataset(["workersInvocationsAdaptive", "kvOperationsAdaptiveGroups"]), null);
39});
40
41test("groups are summed per name, named from the listing, sorted largest first, with totals", () => {
42 const body = answer([
43 { sum: { rowsRead: 10, rowsWritten: 1, readQueries: 2, writeQueries: 1 }, dimensions: { databaseId: "aaa" } },
44 { sum: { rowsRead: 500, rowsWritten: 20, readQueries: 9, writeQueries: 3 }, dimensions: { databaseId: "bbb" } },
45 { sum: { rowsRead: 5, rowsWritten: 0, readQueries: 1, writeQueries: 0 }, dimensions: { databaseId: "aaa" } },
46 ]);
47 const parsed = parseGroups(body, variantOf("d1"), { bbb: "g1t-repos" });
48 assert.deepEqual(parsed.metrics, ["rows_read", "rows_written", "read_queries", "write_queries"]);
49 assert.deepEqual(parsed.rows.map((row) => [row.name, row.rows_read]), [["g1t-repos", 500], ["aaa", 15]]);
50 assert.deepEqual(parsed.totals, { rows_read: 515, rows_written: 21, read_queries: 12, write_queries: 4 });
51 const kv = parseGroups(
52 answer([
53 { sum: { requests: 3 }, dimensions: { namespaceId: "n1", actionType: "write" } },
54 { sum: { requests: 40 }, dimensions: { namespaceId: "n1", actionType: "read" } },
55 ]),
56 variantOf("kv"),
57 { n1: "SESSIONS" },
58 );
59 assert.deepEqual(kv.rows.map((row) => row.name), ["SESSIONS / read", "SESSIONS / write"]);
60 assert.throws(() => parseGroups({ errors: [{ message: "unknown field cpuTimeUs" }] }, variantOf("workers")), /cpuTimeUs/);
61 assert.equal(snake("billableOperations"), "billable_operations");
62});
63
64test("a dataset that errors or is missing is a note, and the others still report", () => {
65 const windowList = windows(NOW);
66 const outcomes = {
67 month_to_date: {
68 workers: { body: answer([{ sum: { requests: 7, errors: 0, cpuTimeUs: 1200 }, dimensions: { scriptName: "web" } }]), variant: variantOf("workers"), dataset: "workersInvocationsAdaptive" },
69 d1: { body: { errors: [{ message: "unknown field \"readQueries\"" }] }, variant: variantOf("d1"), dataset: "d1AnalyticsAdaptiveGroups" },
70 queues: { skipped: "queueMessageOperationsAdaptiveGroups is not in this account's schema" },
71 kv: { error: new Error("fetch failed") },
72 artifacts: { body: answer([{ count: 4, sum: { durationMs: 80 }, dimensions: { repositoryNamespace: "g1t", eventType: "pull" } }]), variant: variantOf("artifacts"), dataset: "artifactsEventsAdaptiveGroups" },
73 },
74 last_24h: {},
75 };
76 const report = assemble({ account: "acct", now: NOW, windowList, outcomes });
77 const month = report.windows[0];
78 assert.deepEqual(month.datasets.map((set) => set.key), ["workers", "artifacts"]);
79 assert.equal(month.notes.length, 3);
80 assert.match(month.notes.join("\n"), /D1 rows.*readQueries/);
81 assert.match(month.notes.join("\n"), /not in this account's schema/);
82 assert.match(month.notes.join("\n"), /fetch failed/);
83 assert.deepEqual(report.windows[1].datasets, []);
84 const text = format(report, 10);
85 assert.match(text, /Workers invocations, by script/);
86 assert.match(text, /web\s+7\s+0\s+1,200/);
87 assert.match(text, /Notes:/);
88});
89
90test("the JSON report is one snake_case object with every row", () => {
91 const windowList = windows(NOW);
92 const rows = Array.from({ length: 15 }, (_, at) => ({ sum: { requests: at + 1 }, dimensions: { scriptName: `s${at}` } }));
93 const outcomes = { month_to_date: { durable_objects: { body: answer(rows), variant: variantOf("durable_objects", 1), dataset: "durableObjectsInvocationsAdaptiveGroups" } }, last_24h: {} };
94 const report = JSON.parse(JSON.stringify(assemble({ account: "acct", now: NOW, windowList, outcomes })));
95 assert.deepEqual(Object.keys(report), ["account", "generated_at", "windows"]);
96 assert.deepEqual(Object.keys(report.windows[0]), ["key", "label", "start", "end", "datasets", "notes"]);
97 const set = report.windows[0].datasets[0];
98 assert.deepEqual(Object.keys(set), ["key", "label", "dataset", "metrics", "rows", "totals"]);
99 assert.equal(set.rows.length, 15);
100 assert.equal(set.rows[0].name, "s14");
101 assert.equal(set.totals.requests, 120);
102 const keys = JSON.stringify(report).match(/"([^"]+)":/g).map((key) => key.slice(1, -2));
103 for (const key of keys) assert.match(key, /^[a-z0-9_]+$/, key);
104 // The tables show only the top.
105 assert.match(format(assemble({ account: "acct", now: NOW, windowList, outcomes }), 10), /\.\.\. and 5 more/);
106});
107
108test("the command line reads --json, --top and the account", () => {
109 assert.deepEqual(parseArgs(["--json", "--top", "3", "--account", "abc"], {}), { help: false, json: true, top: 3, account: "abc" });
110 assert.equal(parseArgs([], { CLOUDFLARE_ACCOUNT_ID: "env" }).account, "env");
111 assert.equal(parseArgs(["--help"], {}).help, true);
112 assert.equal(parseArgs([], {}).top, 10);
113});