@queuebase/cli 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/__tests__/generate.test.d.ts +2 -0
  2. package/dist/__tests__/generate.test.d.ts.map +1 -0
  3. package/dist/__tests__/generate.test.js +204 -0
  4. package/dist/__tests__/generate.test.js.map +1 -0
  5. package/dist/__tests__/manifest.test.d.ts +2 -0
  6. package/dist/__tests__/manifest.test.d.ts.map +1 -0
  7. package/dist/__tests__/manifest.test.js +41 -0
  8. package/dist/__tests__/manifest.test.js.map +1 -0
  9. package/dist/__tests__/schedule-ticker.test.d.ts +2 -0
  10. package/dist/__tests__/schedule-ticker.test.d.ts.map +1 -0
  11. package/dist/__tests__/schedule-ticker.test.js +185 -0
  12. package/dist/__tests__/schedule-ticker.test.js.map +1 -0
  13. package/dist/__tests__/sync-schedules.test.d.ts +2 -0
  14. package/dist/__tests__/sync-schedules.test.d.ts.map +1 -0
  15. package/dist/__tests__/sync-schedules.test.js +171 -0
  16. package/dist/__tests__/sync-schedules.test.js.map +1 -0
  17. package/dist/bin.js +7 -1
  18. package/dist/bin.js.map +1 -1
  19. package/dist/commands/dev.d.ts.map +1 -1
  20. package/dist/commands/dev.js +3 -1
  21. package/dist/commands/dev.js.map +1 -1
  22. package/dist/commands/generate.d.ts +2 -0
  23. package/dist/commands/generate.d.ts.map +1 -0
  24. package/dist/commands/generate.js +10 -0
  25. package/dist/commands/generate.js.map +1 -0
  26. package/dist/db/migrations.d.ts.map +1 -1
  27. package/dist/db/migrations.js +36 -0
  28. package/dist/db/migrations.js.map +1 -1
  29. package/dist/db/queries.d.ts +2 -0
  30. package/dist/db/queries.d.ts.map +1 -1
  31. package/dist/db/queries.js +4 -4
  32. package/dist/db/queries.js.map +1 -1
  33. package/dist/db/schedule-queries.d.ts +59 -0
  34. package/dist/db/schedule-queries.d.ts.map +1 -0
  35. package/dist/db/schedule-queries.js +78 -0
  36. package/dist/db/schedule-queries.js.map +1 -0
  37. package/dist/generate.d.ts +12 -0
  38. package/dist/generate.d.ts.map +1 -0
  39. package/dist/generate.js +103 -0
  40. package/dist/generate.js.map +1 -0
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/manifest.d.ts +12 -0
  46. package/dist/manifest.d.ts.map +1 -0
  47. package/dist/manifest.js +15 -0
  48. package/dist/manifest.js.map +1 -0
  49. package/dist/schedule-ticker.d.ts +16 -0
  50. package/dist/schedule-ticker.d.ts.map +1 -0
  51. package/dist/schedule-ticker.js +63 -0
  52. package/dist/schedule-ticker.js.map +1 -0
  53. package/dist/server.d.ts +1 -0
  54. package/dist/server.d.ts.map +1 -1
  55. package/dist/server.js +13 -0
  56. package/dist/server.js.map +1 -1
  57. package/dist/sync-schedules.d.ts +4 -0
  58. package/dist/sync-schedules.d.ts.map +1 -0
  59. package/dist/sync-schedules.js +29 -0
  60. package/dist/sync-schedules.js.map +1 -0
  61. package/package.json +4 -3
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=generate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/generate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,204 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { buildManifest, discoverRouterPath, generate, loadRouter, writeManifest, } from "../generate.js";
6
+ import { getManifestPath, readManifest } from "../manifest.js";
7
+ describe("discoverRouterPath", () => {
8
+ let tmpDir;
9
+ beforeEach(() => {
10
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "qb-generate-"));
11
+ });
12
+ afterEach(() => {
13
+ fs.rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ it("finds router at src/jobs/index.ts", async () => {
16
+ const routerDir = path.join(tmpDir, "src", "jobs");
17
+ fs.mkdirSync(routerDir, { recursive: true });
18
+ fs.writeFileSync(path.join(routerDir, "index.ts"), "export default {}");
19
+ const result = await discoverRouterPath(tmpDir);
20
+ expect(result).toBe(path.join(tmpDir, "src", "jobs", "index.ts"));
21
+ });
22
+ it("finds router at jobs/index.ts when src/jobs does not exist", async () => {
23
+ const routerDir = path.join(tmpDir, "jobs");
24
+ fs.mkdirSync(routerDir, { recursive: true });
25
+ fs.writeFileSync(path.join(routerDir, "index.ts"), "export default {}");
26
+ const result = await discoverRouterPath(tmpDir);
27
+ expect(result).toBe(path.join(tmpDir, "jobs", "index.ts"));
28
+ });
29
+ it("prefers src/jobs/index.ts over jobs/index.ts", async () => {
30
+ for (const dir of ["src/jobs", "jobs"]) {
31
+ const routerDir = path.join(tmpDir, dir);
32
+ fs.mkdirSync(routerDir, { recursive: true });
33
+ fs.writeFileSync(path.join(routerDir, "index.ts"), "export default {}");
34
+ }
35
+ const result = await discoverRouterPath(tmpDir);
36
+ expect(result).toBe(path.join(tmpDir, "src", "jobs", "index.ts"));
37
+ });
38
+ it("finds .js files", async () => {
39
+ const routerDir = path.join(tmpDir, "src", "jobs");
40
+ fs.mkdirSync(routerDir, { recursive: true });
41
+ fs.writeFileSync(path.join(routerDir, "index.js"), "export default {}");
42
+ const result = await discoverRouterPath(tmpDir);
43
+ expect(result).toBe(path.join(tmpDir, "src", "jobs", "index.js"));
44
+ });
45
+ it("throws when no router is found", async () => {
46
+ await expect(discoverRouterPath(tmpDir)).rejects.toThrow(/Could not find job router/);
47
+ });
48
+ it("reads routerPath from queuebase.config.js", async () => {
49
+ const customDir = path.join(tmpDir, "custom");
50
+ fs.mkdirSync(customDir, { recursive: true });
51
+ fs.writeFileSync(path.join(customDir, "router.ts"), "export default {}");
52
+ fs.writeFileSync(path.join(tmpDir, "queuebase.config.js"), "export default { routerPath: './custom/router.ts' }");
53
+ const result = await discoverRouterPath(tmpDir);
54
+ expect(result).toBe(path.join(tmpDir, "custom", "router.ts"));
55
+ });
56
+ it("throws when config exists but routerPath is missing", async () => {
57
+ fs.writeFileSync(path.join(tmpDir, "queuebase.config.js"), "export default {}");
58
+ await expect(discoverRouterPath(tmpDir)).rejects.toThrow(/missing routerPath/);
59
+ });
60
+ it("throws when config routerPath points to nonexistent file", async () => {
61
+ fs.writeFileSync(path.join(tmpDir, "queuebase.config.js"), "export default { routerPath: './nonexistent.ts' }");
62
+ await expect(discoverRouterPath(tmpDir)).rejects.toThrow(/does not exist/);
63
+ });
64
+ });
65
+ describe("buildManifest", () => {
66
+ it("builds manifest with all jobs, scheduled and unscheduled", () => {
67
+ const router = {
68
+ sendEmail: {
69
+ input: {},
70
+ handler: async () => { },
71
+ },
72
+ dailyReport: {
73
+ input: {},
74
+ handler: async () => { },
75
+ schedule: "every day at 9am",
76
+ },
77
+ };
78
+ const manifest = buildManifest(router);
79
+ expect(manifest.version).toBe(1);
80
+ expect(manifest.generatedAt).toBeDefined();
81
+ expect(Object.keys(manifest.jobs)).toEqual(["sendEmail", "dailyReport"]);
82
+ expect(manifest.jobs.sendEmail).toEqual({});
83
+ expect(manifest.jobs.dailyReport).toEqual({
84
+ schedule: {
85
+ cronExpression: "0 9 * * *",
86
+ timezone: "UTC",
87
+ enabled: true,
88
+ overlap: "skip",
89
+ timeoutMs: null,
90
+ },
91
+ });
92
+ });
93
+ it("builds manifest with no jobs", () => {
94
+ const router = {};
95
+ const manifest = buildManifest(router);
96
+ expect(manifest.version).toBe(1);
97
+ expect(manifest.jobs).toEqual({});
98
+ });
99
+ it("resolves schedule config objects", () => {
100
+ const router = {
101
+ weeklyCleanup: {
102
+ input: {},
103
+ handler: async () => { },
104
+ schedule: {
105
+ cron: "every monday at 9am",
106
+ timezone: "America/New_York",
107
+ enabled: false,
108
+ overlap: "allow",
109
+ timeout: "5m",
110
+ },
111
+ },
112
+ };
113
+ const manifest = buildManifest(router);
114
+ expect(manifest.jobs.weeklyCleanup).toEqual({
115
+ schedule: {
116
+ cronExpression: "0 9 * * 1",
117
+ timezone: "America/New_York",
118
+ enabled: false,
119
+ overlap: "allow",
120
+ timeoutMs: 300000,
121
+ },
122
+ });
123
+ });
124
+ });
125
+ describe("writeManifest", () => {
126
+ let tmpDir;
127
+ beforeEach(() => {
128
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "qb-write-"));
129
+ });
130
+ afterEach(() => {
131
+ fs.rmSync(tmpDir, { recursive: true, force: true });
132
+ });
133
+ it("writes manifest JSON to .queuebase directory", () => {
134
+ const manifest = {
135
+ version: 1,
136
+ generatedAt: "2026-03-14T12:00:00.000Z",
137
+ jobs: { sendEmail: {} },
138
+ };
139
+ writeManifest(manifest, tmpDir);
140
+ const manifestPath = getManifestPath(tmpDir);
141
+ expect(fs.existsSync(manifestPath)).toBe(true);
142
+ const written = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
143
+ expect(written).toEqual(manifest);
144
+ });
145
+ it("creates .queuebase directory if it does not exist", () => {
146
+ const manifest = {
147
+ version: 1,
148
+ generatedAt: "2026-03-14T12:00:00.000Z",
149
+ jobs: {},
150
+ };
151
+ writeManifest(manifest, tmpDir);
152
+ expect(fs.existsSync(path.join(tmpDir, ".queuebase"))).toBe(true);
153
+ });
154
+ });
155
+ describe("loadRouter", () => {
156
+ let tmpDir;
157
+ beforeEach(() => {
158
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "qb-load-"));
159
+ });
160
+ afterEach(() => {
161
+ fs.rmSync(tmpDir, { recursive: true, force: true });
162
+ });
163
+ it("loads a router from a JS file with default export", async () => {
164
+ const routerFile = path.join(tmpDir, "router.js");
165
+ fs.writeFileSync(routerFile, "export default { sendEmail: { input: {}, handler: async () => {} } }");
166
+ const router = await loadRouter(routerFile);
167
+ expect(router).toHaveProperty("sendEmail");
168
+ });
169
+ it("throws when file has no default export", async () => {
170
+ const routerFile = path.join(tmpDir, "router.js");
171
+ fs.writeFileSync(routerFile, 'export const foo = "bar"');
172
+ await expect(loadRouter(routerFile)).rejects.toThrow(/Expected default export/);
173
+ });
174
+ });
175
+ describe("generate", () => {
176
+ let tmpDir;
177
+ beforeEach(() => {
178
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "qb-integration-"));
179
+ });
180
+ afterEach(() => {
181
+ fs.rmSync(tmpDir, { recursive: true, force: true });
182
+ });
183
+ it("discovers router, builds manifest, and writes to disk", async () => {
184
+ const routerDir = path.join(tmpDir, "src", "jobs");
185
+ fs.mkdirSync(routerDir, { recursive: true });
186
+ fs.writeFileSync(path.join(routerDir, "index.js"), `export default {
187
+ sendEmail: { input: {}, handler: async () => {} },
188
+ dailyReport: { input: {}, handler: async () => {}, schedule: "every day at 9am" },
189
+ }`);
190
+ const result = await generate(tmpDir);
191
+ expect(result.routerPath).toBe(path.join(tmpDir, "src", "jobs", "index.js"));
192
+ expect(result.manifest.version).toBe(1);
193
+ expect(Object.keys(result.manifest.jobs)).toEqual(["sendEmail", "dailyReport"]);
194
+ const dailyReport = result.manifest.jobs.dailyReport;
195
+ expect(dailyReport?.schedule?.cronExpression).toBe("0 9 * * *");
196
+ expect(result.manifest.jobs.sendEmail).toEqual({});
197
+ const fromDisk = readManifest(tmpDir);
198
+ expect(fromDisk).toEqual(result.manifest);
199
+ });
200
+ it("throws when no router file exists", async () => {
201
+ await expect(generate(tmpDir)).rejects.toThrow(/Could not find job router/);
202
+ });
203
+ });
204
+ //# sourceMappingURL=generate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.test.js","sourceRoot":"","sources":["../../src/__tests__/generate.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EACN,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,UAAU,EACV,aAAa,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE/D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IACnC,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACnD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC7D,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;YACxC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACzC,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACnD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACvD,2BAA2B,CAC3B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACzE,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,EACxC,qDAAqD,CACrD,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACpE,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,EACxC,mBAAmB,CACnB,CAAC;QACF,MAAM,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACvD,oBAAoB,CACpB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACzE,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,EACxC,mDAAmD,CACnD,CAAC;QACF,MAAM,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACvD,gBAAgB,CAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QACnE,MAAM,MAAM,GAAG;YACd,SAAS,EAAE;gBACV,KAAK,EAAE,EAAS;gBAChB,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;aACvB;YACD,WAAW,EAAE;gBACZ,KAAK,EAAE,EAAS;gBAChB,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;gBACvB,QAAQ,EAAE,kBAAkB;aAC5B;SACD,CAAC;QAEF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAEvC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;QACzE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC;YACzC,QAAQ,EAAE;gBACT,cAAc,EAAE,WAAW;gBAC3B,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,MAAM;gBACf,SAAS,EAAE,IAAI;aACf;SACD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG;YACd,aAAa,EAAE;gBACd,KAAK,EAAE,EAAS;gBAChB,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;gBACvB,QAAQ,EAAE;oBACT,IAAI,EAAE,qBAAqB;oBAC3B,QAAQ,EAAE,kBAAkB;oBAC5B,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,OAAgB;oBACzB,OAAO,EAAE,IAAI;iBACb;aACD;SACD,CAAC;QAEF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC;YAC3C,QAAQ,EAAE;gBACT,cAAc,EAAE,WAAW;gBAC3B,QAAQ,EAAE,kBAAkB;gBAC5B,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,OAAO;gBAChB,SAAS,EAAE,MAAM;aACjB;SACD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC9B,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACvD,MAAM,QAAQ,GAAG;YAChB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,0BAA0B;YACvC,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;SACvB,CAAC;QAEF,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEhC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC5D,MAAM,QAAQ,GAAG;YAChB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,0BAA0B;YACvC,IAAI,EAAE,EAAE;SACR,CAAC;QAEF,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC3B,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAClD,EAAE,CAAC,aAAa,CACf,UAAU,EACV,sEAAsE,CACtE,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAClD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,0BAA0B,CAAC,CAAC;QACzD,MAAM,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACnD,yBAAyB,CACzB,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACzB,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACnD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAChC;;;KAGE,CACF,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEtC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7E,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;QAChF,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC;QACrD,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=manifest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/manifest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { readManifest } from "../manifest.js";
6
+ describe("readManifest", () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "qb-manifest-"));
10
+ });
11
+ afterEach(() => {
12
+ fs.rmSync(tmpDir, { recursive: true, force: true });
13
+ });
14
+ it("reads and parses a valid manifest file", () => {
15
+ const manifest = {
16
+ version: 1,
17
+ generatedAt: "2026-03-14T12:00:00.000Z",
18
+ jobs: {
19
+ sendEmail: {},
20
+ dailyReport: {
21
+ schedule: {
22
+ cronExpression: "0 9 * * *",
23
+ timezone: "UTC",
24
+ enabled: true,
25
+ overlap: "skip",
26
+ timeoutMs: 300000,
27
+ },
28
+ },
29
+ },
30
+ };
31
+ const qbDir = path.join(tmpDir, ".queuebase");
32
+ fs.mkdirSync(qbDir);
33
+ fs.writeFileSync(path.join(qbDir, "queuebase.manifest.json"), JSON.stringify(manifest, null, 2));
34
+ const result = readManifest(tmpDir);
35
+ expect(result).toEqual(manifest);
36
+ });
37
+ it("throws when manifest file does not exist", () => {
38
+ expect(() => readManifest(tmpDir)).toThrow(/Run `queuebase generate` first/);
39
+ });
40
+ });
41
+ //# sourceMappingURL=manifest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.test.js","sourceRoot":"","sources":["../../src/__tests__/manifest.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC7B,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QACjD,MAAM,QAAQ,GAAa;YAC1B,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,0BAA0B;YACvC,IAAI,EAAE;gBACL,SAAS,EAAE,EAAE;gBACb,WAAW,EAAE;oBACZ,QAAQ,EAAE;wBACT,cAAc,EAAE,WAAW;wBAC3B,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE,MAAM;wBACf,SAAS,EAAE,MAAM;qBACjB;iBACD;aACD;SACD,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC9C,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACpB,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,yBAAyB,CAAC,EAC3C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CACjC,CAAC;QAEF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CACzC,gCAAgC,CAChC,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schedule-ticker.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-ticker.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schedule-ticker.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,185 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ vi.mock("@queuebase/core", async (importOriginal) => {
4
+ const actual = await importOriginal();
5
+ return {
6
+ ...actual,
7
+ nextRun: vi.fn((expression, timezone) => {
8
+ return actual.nextRun(expression, timezone);
9
+ }),
10
+ };
11
+ });
12
+ import { nextRun } from "@queuebase/core";
13
+ import { tick } from "../schedule-ticker.js";
14
+ function createTestDb() {
15
+ const db = new DatabaseSync(":memory:");
16
+ db.exec(`
17
+ CREATE TABLE schedules (
18
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19
+ public_id TEXT NOT NULL UNIQUE,
20
+ job_name TEXT NOT NULL UNIQUE,
21
+ cron_expr TEXT NOT NULL,
22
+ timezone TEXT NOT NULL DEFAULT 'UTC',
23
+ enabled INTEGER NOT NULL DEFAULT 1,
24
+ dashboard_enabled INTEGER NOT NULL DEFAULT 1,
25
+ overlap TEXT NOT NULL DEFAULT 'skip',
26
+ timeout_ms INTEGER,
27
+ last_run_at INTEGER,
28
+ next_run_at INTEGER,
29
+ deleted_at INTEGER,
30
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
31
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
32
+ )
33
+ `);
34
+ db.exec(`
35
+ CREATE TABLE jobs (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ public_id TEXT NOT NULL UNIQUE,
38
+ name TEXT NOT NULL,
39
+ payload TEXT NOT NULL,
40
+ status TEXT NOT NULL DEFAULT 'pending',
41
+ attempt INTEGER NOT NULL DEFAULT 1,
42
+ max_attempts INTEGER NOT NULL DEFAULT 1,
43
+ run_at INTEGER NOT NULL,
44
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
45
+ started_at INTEGER,
46
+ completed_at INTEGER,
47
+ result TEXT,
48
+ error TEXT,
49
+ backoff_strategy TEXT NOT NULL DEFAULT 'exponential',
50
+ backoff_delay INTEGER NOT NULL DEFAULT 1000,
51
+ callback_url TEXT NOT NULL,
52
+ schedule_id INTEGER REFERENCES schedules(id) ON DELETE SET NULL
53
+ )
54
+ `);
55
+ db.exec("CREATE INDEX idx_jobs_schedule_id ON jobs(schedule_id)");
56
+ db.exec(`
57
+ CREATE TABLE job_logs (
58
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
59
+ job_id INTEGER NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
60
+ event TEXT NOT NULL,
61
+ source TEXT NOT NULL DEFAULT 'system',
62
+ metadata TEXT,
63
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
64
+ )
65
+ `);
66
+ return db;
67
+ }
68
+ function insertDueSchedule(db, overrides = {}) {
69
+ const now = Date.now();
70
+ const defaults = {
71
+ jobName: "testJob",
72
+ cronExpr: "* * * * *",
73
+ timezone: "UTC",
74
+ enabled: 1,
75
+ dashboardEnabled: 1,
76
+ overlap: "skip",
77
+ nextRunAt: now - 60_000,
78
+ };
79
+ const p = { ...defaults, ...overrides };
80
+ db.prepare(`
81
+ INSERT INTO schedules (public_id, job_name, cron_expr, timezone, enabled, dashboard_enabled, overlap, next_run_at, created_at, updated_at)
82
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
83
+ `).run(`sched_${p.jobName}`, p.jobName, p.cronExpr, p.timezone, p.enabled, p.dashboardEnabled, p.overlap, p.nextRunAt, now, now);
84
+ }
85
+ function getJobs(db) {
86
+ return db
87
+ .prepare("SELECT * FROM jobs")
88
+ .all();
89
+ }
90
+ function getSchedule(db, jobName) {
91
+ return db
92
+ .prepare("SELECT * FROM schedules WHERE job_name = ?")
93
+ .get(jobName);
94
+ }
95
+ const CALLBACK_URL = "http://localhost:3000/api/queuebase";
96
+ describe("schedule ticker", () => {
97
+ let db;
98
+ beforeEach(() => {
99
+ db = createTestDb();
100
+ vi.clearAllMocks();
101
+ });
102
+ afterEach(() => {
103
+ db.close();
104
+ });
105
+ it("enqueues a job for a due schedule with overlap 'allow'", () => {
106
+ insertDueSchedule(db, { jobName: "allowJob", overlap: "allow" });
107
+ tick(db, CALLBACK_URL);
108
+ const jobs = getJobs(db);
109
+ expect(jobs).toHaveLength(1);
110
+ expect(jobs[0].name).toBe("allowJob");
111
+ expect(jobs[0].schedule_id).toBeTypeOf("number");
112
+ expect(jobs[0].callback_url).toBe(CALLBACK_URL);
113
+ const schedule = getSchedule(db, "allowJob");
114
+ expect(schedule.next_run_at).not.toBe(null);
115
+ expect(schedule.next_run_at > Date.now() - 1000).toBe(true);
116
+ });
117
+ it("enqueues a job for a due schedule with overlap 'skip' and no active jobs", () => {
118
+ insertDueSchedule(db, { jobName: "skipJob", overlap: "skip" });
119
+ tick(db, CALLBACK_URL);
120
+ const jobs = getJobs(db);
121
+ expect(jobs).toHaveLength(1);
122
+ expect(jobs[0].name).toBe("skipJob");
123
+ });
124
+ it("skips enqueue when overlap is 'skip' and a running job exists", () => {
125
+ insertDueSchedule(db, { jobName: "busyJob", overlap: "skip" });
126
+ const scheduleRow = getSchedule(db, "busyJob");
127
+ db.prepare(`
128
+ INSERT INTO jobs (public_id, name, payload, status, run_at, callback_url, schedule_id)
129
+ VALUES (?, ?, ?, 'running', ?, ?, ?)
130
+ `).run("job_existing", "busyJob", "{}", Date.now(), CALLBACK_URL, scheduleRow.id);
131
+ const prevNextRun = scheduleRow.next_run_at;
132
+ tick(db, CALLBACK_URL);
133
+ const jobs = getJobs(db);
134
+ expect(jobs).toHaveLength(1);
135
+ expect(jobs[0].public_id).toBe("job_existing");
136
+ const updated = getSchedule(db, "busyJob");
137
+ expect(updated.next_run_at).not.toBe(prevNextRun);
138
+ });
139
+ it("skips enqueue when overlap is 'skip' and a pending job exists", () => {
140
+ insertDueSchedule(db, { jobName: "pendingJob", overlap: "skip" });
141
+ const scheduleRow = getSchedule(db, "pendingJob");
142
+ db.prepare(`
143
+ INSERT INTO jobs (public_id, name, payload, status, run_at, callback_url, schedule_id)
144
+ VALUES (?, ?, ?, 'pending', ?, ?, ?)
145
+ `).run("job_pending", "pendingJob", "{}", Date.now(), CALLBACK_URL, scheduleRow.id);
146
+ tick(db, CALLBACK_URL);
147
+ const jobs = getJobs(db);
148
+ expect(jobs).toHaveLength(1);
149
+ expect(jobs[0].public_id).toBe("job_pending");
150
+ });
151
+ it("ignores schedules that are not yet due", () => {
152
+ insertDueSchedule(db, {
153
+ jobName: "futureJob",
154
+ nextRunAt: Date.now() + 60_000,
155
+ });
156
+ tick(db, CALLBACK_URL);
157
+ expect(getJobs(db)).toHaveLength(0);
158
+ });
159
+ it("ignores disabled schedules", () => {
160
+ insertDueSchedule(db, { jobName: "disabledJob", enabled: 0 });
161
+ tick(db, CALLBACK_URL);
162
+ expect(getJobs(db)).toHaveLength(0);
163
+ });
164
+ it("ignores schedules with dashboard_enabled = 0", () => {
165
+ insertDueSchedule(db, { jobName: "hiddenJob", dashboardEnabled: 0 });
166
+ tick(db, CALLBACK_URL);
167
+ expect(getJobs(db)).toHaveLength(0);
168
+ });
169
+ it("does not double-enqueue on consecutive ticks", () => {
170
+ insertDueSchedule(db, { jobName: "onceJob", overlap: "allow" });
171
+ tick(db, CALLBACK_URL);
172
+ tick(db, CALLBACK_URL);
173
+ expect(getJobs(db)).toHaveLength(1);
174
+ });
175
+ it("skips schedule when nextRun returns null", () => {
176
+ insertDueSchedule(db, { jobName: "nullNextJob" });
177
+ const mockedNextRun = vi.mocked(nextRun);
178
+ mockedNextRun.mockReturnValueOnce(null);
179
+ tick(db, CALLBACK_URL);
180
+ expect(getJobs(db)).toHaveLength(0);
181
+ const schedule = getSchedule(db, "nullNextJob");
182
+ expect(schedule.next_run_at).toBeTypeOf("number");
183
+ });
184
+ });
185
+ //# sourceMappingURL=schedule-ticker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-ticker.test.js","sourceRoot":"","sources":["../../src/__tests__/schedule-ticker.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sync-schedules.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-schedules.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sync-schedules.test.ts"],"names":[],"mappings":""}