g1t/scripts/deploy/scheduler.test.mjs

63 lines3,011 bytesCodeBlame
1// The self-hosted scheduler's cron matching (deploy/self-host/scheduler.mjs).
2import assert from "node:assert/strict";
3import { test } from "node:test";
4
5import { due, fieldValues, matches, run } from "../../deploy/self-host/scheduler.mjs";
6
7const at = (iso) => new Date(iso);
8
9test("fields read numbers, ranges, lists and steps", () => {
10 assert.deepEqual([...fieldValues("*/15", { min: 0, max: 59 })], [0, 15, 30, 45]);
11 assert.deepEqual([...fieldValues("1-5", { min: 0, max: 59 })], [1, 2, 3, 4, 5]);
12 assert.deepEqual([...fieldValues("3,7,10-20/5", { min: 0, max: 59 })], [3, 7, 10, 15, 20]);
13 assert.equal(fieldValues("60", { min: 0, max: 59 }), null);
14 assert.equal(fieldValues("*/0", { min: 0, max: 59 }), null);
15 assert.equal(fieldValues("x", { min: 0, max: 59 }), null);
16});
17
18test("the services' crons match when hosted g1t's would fire", () => {
19 assert.ok(matches("37 * * * *", at("2026-10-06T12:37:00Z")));
20 assert.ok(!matches("37 * * * *", at("2026-10-06T12:38:00Z")));
21 assert.ok(matches("*/15 * * * *", at("2026-10-06T12:45:00Z")));
22 assert.ok(matches("41 3 * * *", at("2026-10-06T03:41:59Z")));
23 assert.ok(!matches("41 3 * * *", at("2026-10-06T04:41:00Z")));
24 assert.ok(matches("* * * * *", at("2026-10-06T00:00:00Z")));
25 // Sunday is 0 or 7; with both day fields given, either may match.
26 assert.ok(matches("0 0 * * 7", at("2026-10-04T00:00:00Z")));
27 assert.ok(matches("0 0 1 * 1", at("2026-10-05T00:00:00Z")));
28 assert.ok(!matches("bad cron", at("2026-10-06T00:00:00Z")));
29});
30
31test("what is due is each matching cron of each service", () => {
32 const schedules = [
33 { worker: "g1t-packages", crons: ["37 * * * *"] },
34 { worker: "g1t-webhooks", crons: ["* * * * *"] },
35 { worker: "g1t-identity", crons: ["*/15 * * * *"] },
36 ];
37 assert.deepEqual(due(schedules, at("2026-10-06T12:37:00Z")), [
38 { worker: "g1t-packages", cron: "37 * * * *" },
39 { worker: "g1t-webhooks", cron: "* * * * *" },
40 ]);
41});
42
43test("a run asks Wrangler for the handler, and a slow one is not doubled", async () => {
44 const asked = [];
45 let finish;
46 const slow = (url, init) => {
47 asked.push({ url, body: JSON.parse(init.body) });
48 return new Promise((resolve) => {
49 finish = () => resolve(new Response(JSON.stringify({ success: true, result: { outcome: "ok" } })));
50 });
51 };
52 const job = { worker: "g1t-webhooks", cron: "* * * * *" };
53 const first = run("http://127.0.0.1:8787", job, { fetch: slow });
54 // The minute after, it is still running: skipped, not started again.
55 assert.equal(await run("http://127.0.0.1:8787", job, { fetch: slow }), false);
56 finish();
57 assert.equal(await first, true);
58 assert.deepEqual(asked, [
59 { url: "http://127.0.0.1:8787/cdn-cgi/local/explorer/api/local/scheduled?worker=g1t-webhooks", body: { cron: "* * * * *" } },
60 ]);
61 const failing = async () => new Response(JSON.stringify({ success: true, result: { outcome: "exception" } }));
62 assert.equal(await run("http://127.0.0.1:8787", job, { fetch: failing }), false);
63});