@powerhousedao/reactor 5.0.0-staging.8 → 5.0.1-staging.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 (248) hide show
  1. package/dist/src/cache/buffer/ring-buffer.d.ts +37 -0
  2. package/dist/src/cache/buffer/ring-buffer.d.ts.map +1 -0
  3. package/dist/src/cache/buffer/ring-buffer.js +69 -0
  4. package/dist/src/cache/buffer/ring-buffer.js.map +1 -0
  5. package/dist/src/cache/kysely-write-cache.d.ts +133 -0
  6. package/dist/src/cache/kysely-write-cache.d.ts.map +1 -0
  7. package/dist/src/cache/kysely-write-cache.js +375 -0
  8. package/dist/src/cache/kysely-write-cache.js.map +1 -0
  9. package/dist/src/cache/lru/lru-tracker.d.ts +15 -0
  10. package/dist/src/cache/lru/lru-tracker.d.ts.map +1 -0
  11. package/dist/src/cache/lru/lru-tracker.js +96 -0
  12. package/dist/src/cache/lru/lru-tracker.js.map +1 -0
  13. package/dist/src/cache/types.d.ts +42 -0
  14. package/dist/src/cache/types.d.ts.map +1 -0
  15. package/dist/src/cache/types.js +2 -0
  16. package/dist/src/cache/types.js.map +1 -0
  17. package/dist/src/cache/write/interfaces.d.ts +83 -0
  18. package/dist/src/cache/write/interfaces.d.ts.map +1 -0
  19. package/dist/src/cache/write/interfaces.js +2 -0
  20. package/dist/src/cache/write/interfaces.js.map +1 -0
  21. package/dist/src/client/reactor-client.d.ts +103 -0
  22. package/dist/src/client/reactor-client.d.ts.map +1 -0
  23. package/dist/src/client/reactor-client.js +184 -0
  24. package/dist/src/client/reactor-client.js.map +1 -0
  25. package/dist/src/client/types.d.ts +213 -0
  26. package/dist/src/client/types.d.ts.map +1 -0
  27. package/dist/src/client/types.js +14 -0
  28. package/dist/src/client/types.js.map +1 -0
  29. package/dist/src/core/builder.d.ts +20 -0
  30. package/dist/src/core/builder.d.ts.map +1 -0
  31. package/dist/src/core/builder.js +47 -0
  32. package/dist/src/core/builder.js.map +1 -0
  33. package/dist/src/{reactor.d.ts → core/reactor.d.ts} +13 -30
  34. package/dist/src/core/reactor.d.ts.map +1 -0
  35. package/dist/src/{reactor.js → core/reactor.js} +147 -111
  36. package/dist/src/core/reactor.js.map +1 -0
  37. package/dist/src/{interfaces/reactor.d.ts → core/types.d.ts} +7 -7
  38. package/dist/src/core/types.d.ts.map +1 -0
  39. package/dist/src/core/types.js +2 -0
  40. package/dist/src/core/types.js.map +1 -0
  41. package/dist/src/{utils.d.ts → core/utils.d.ts} +2 -2
  42. package/dist/src/core/utils.d.ts.map +1 -0
  43. package/dist/src/core/utils.js.map +1 -0
  44. package/dist/src/events/event-bus.d.ts +2 -2
  45. package/dist/src/events/event-bus.d.ts.map +1 -1
  46. package/dist/src/events/event-bus.js +1 -1
  47. package/dist/src/events/event-bus.js.map +1 -1
  48. package/dist/src/events/interfaces.d.ts +1 -1
  49. package/dist/src/events/interfaces.d.ts.map +1 -1
  50. package/dist/src/events/types.d.ts +14 -0
  51. package/dist/src/events/types.d.ts.map +1 -1
  52. package/dist/src/events/types.js +6 -0
  53. package/dist/src/events/types.js.map +1 -1
  54. package/dist/src/executor/interfaces.d.ts +2 -2
  55. package/dist/src/executor/interfaces.d.ts.map +1 -1
  56. package/dist/src/executor/simple-job-executor-manager.d.ts +5 -1
  57. package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
  58. package/dist/src/executor/simple-job-executor-manager.js +98 -36
  59. package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
  60. package/dist/src/executor/simple-job-executor.d.ts +32 -3
  61. package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
  62. package/dist/src/executor/simple-job-executor.js +436 -17
  63. package/dist/src/executor/simple-job-executor.js.map +1 -1
  64. package/dist/src/executor/types.d.ts +10 -3
  65. package/dist/src/executor/types.d.ts.map +1 -1
  66. package/dist/src/executor/types.js.map +1 -1
  67. package/dist/src/executor/util.d.ts +47 -0
  68. package/dist/src/executor/util.d.ts.map +1 -0
  69. package/dist/src/executor/util.js +113 -0
  70. package/dist/src/executor/util.js.map +1 -0
  71. package/dist/src/index.d.ts +23 -3
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +30 -2
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/job-tracker/in-memory-job-tracker.d.ts +15 -0
  76. package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -0
  77. package/dist/src/job-tracker/in-memory-job-tracker.js +78 -0
  78. package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -0
  79. package/dist/src/job-tracker/index.d.ts +3 -0
  80. package/dist/src/job-tracker/index.d.ts.map +1 -0
  81. package/dist/src/job-tracker/index.js +2 -0
  82. package/dist/src/job-tracker/index.js.map +1 -0
  83. package/dist/src/job-tracker/interfaces.d.ts +41 -0
  84. package/dist/src/job-tracker/interfaces.d.ts.map +1 -0
  85. package/dist/src/job-tracker/interfaces.js +2 -0
  86. package/dist/src/job-tracker/interfaces.js.map +1 -0
  87. package/dist/src/queue/interfaces.d.ts +1 -1
  88. package/dist/src/queue/interfaces.d.ts.map +1 -1
  89. package/dist/src/queue/job-execution-handle.d.ts +2 -1
  90. package/dist/src/queue/job-execution-handle.d.ts.map +1 -1
  91. package/dist/src/queue/job-execution-handle.js.map +1 -1
  92. package/dist/src/queue/queue.d.ts +3 -3
  93. package/dist/src/queue/queue.d.ts.map +1 -1
  94. package/dist/src/queue/queue.js +47 -25
  95. package/dist/src/queue/queue.js.map +1 -1
  96. package/dist/src/queue/types.d.ts +7 -5
  97. package/dist/src/queue/types.d.ts.map +1 -1
  98. package/dist/src/queue/types.js.map +1 -1
  99. package/dist/src/read-models/coordinator.d.ts +38 -0
  100. package/dist/src/read-models/coordinator.d.ts.map +1 -0
  101. package/dist/src/read-models/coordinator.js +62 -0
  102. package/dist/src/read-models/coordinator.js.map +1 -0
  103. package/dist/src/read-models/document-view.d.ts +20 -0
  104. package/dist/src/read-models/document-view.d.ts.map +1 -0
  105. package/dist/src/read-models/document-view.js +365 -0
  106. package/dist/src/read-models/document-view.js.map +1 -0
  107. package/dist/src/read-models/interfaces.d.ts +29 -0
  108. package/dist/src/read-models/interfaces.d.ts.map +1 -0
  109. package/dist/src/read-models/interfaces.js +2 -0
  110. package/dist/src/read-models/interfaces.js.map +1 -0
  111. package/dist/src/read-models/types.d.ts +46 -0
  112. package/dist/src/read-models/types.d.ts.map +1 -0
  113. package/dist/src/read-models/types.js +2 -0
  114. package/dist/src/read-models/types.js.map +1 -0
  115. package/dist/src/registry/implementation.js +1 -1
  116. package/dist/src/registry/implementation.js.map +1 -1
  117. package/dist/src/shared/awaiter.d.ts +32 -0
  118. package/dist/src/shared/awaiter.d.ts.map +1 -0
  119. package/dist/src/shared/awaiter.js +132 -0
  120. package/dist/src/shared/awaiter.js.map +1 -0
  121. package/dist/src/shared/errors.d.ts +17 -0
  122. package/dist/src/shared/errors.d.ts.map +1 -0
  123. package/dist/src/shared/errors.js +33 -0
  124. package/dist/src/shared/errors.js.map +1 -0
  125. package/dist/src/shared/factories.d.ts +1 -1
  126. package/dist/src/shared/factories.d.ts.map +1 -1
  127. package/dist/src/shared/types.d.ts +8 -0
  128. package/dist/src/shared/types.d.ts.map +1 -1
  129. package/dist/src/shared/types.js +5 -0
  130. package/dist/src/shared/types.js.map +1 -1
  131. package/dist/src/signer/passthrough-signer.d.ts +6 -0
  132. package/dist/src/signer/passthrough-signer.d.ts.map +1 -0
  133. package/dist/src/signer/passthrough-signer.js +6 -0
  134. package/dist/src/signer/passthrough-signer.js.map +1 -0
  135. package/dist/src/signer/types.d.ts +15 -0
  136. package/dist/src/signer/types.d.ts.map +1 -0
  137. package/dist/src/signer/types.js +2 -0
  138. package/dist/src/signer/types.js.map +1 -0
  139. package/dist/src/storage/interfaces.d.ts +121 -0
  140. package/dist/src/storage/interfaces.d.ts.map +1 -0
  141. package/dist/src/storage/interfaces.js +19 -0
  142. package/dist/src/storage/interfaces.js.map +1 -0
  143. package/dist/src/storage/kysely/keyframe-store.d.ts +15 -0
  144. package/dist/src/storage/kysely/keyframe-store.d.ts.map +1 -0
  145. package/dist/src/storage/kysely/keyframe-store.js +71 -0
  146. package/dist/src/storage/kysely/keyframe-store.js.map +1 -0
  147. package/dist/src/storage/kysely/store.d.ts +15 -0
  148. package/dist/src/storage/kysely/store.d.ts.map +1 -0
  149. package/dist/src/storage/kysely/store.js +196 -0
  150. package/dist/src/storage/kysely/store.js.map +1 -0
  151. package/dist/src/storage/kysely/types.d.ts +39 -0
  152. package/dist/src/storage/kysely/types.d.ts.map +1 -0
  153. package/dist/src/storage/kysely/types.js +2 -0
  154. package/dist/src/storage/kysely/types.js.map +1 -0
  155. package/dist/src/storage/txn.d.ts +15 -0
  156. package/dist/src/storage/txn.d.ts.map +1 -0
  157. package/dist/src/storage/txn.js +41 -0
  158. package/dist/src/storage/txn.js.map +1 -0
  159. package/dist/src/subs/default-error-handler.d.ts +13 -0
  160. package/dist/src/subs/default-error-handler.d.ts.map +1 -0
  161. package/dist/src/subs/default-error-handler.js +27 -0
  162. package/dist/src/subs/default-error-handler.js.map +1 -0
  163. package/dist/src/subs/react-subscription-manager.d.ts +45 -0
  164. package/dist/src/subs/react-subscription-manager.d.ts.map +1 -0
  165. package/dist/src/subs/react-subscription-manager.js +185 -0
  166. package/dist/src/subs/react-subscription-manager.js.map +1 -0
  167. package/dist/src/subs/types.d.ts +64 -0
  168. package/dist/src/subs/types.d.ts.map +1 -0
  169. package/dist/src/subs/types.js +2 -0
  170. package/dist/src/subs/types.js.map +1 -0
  171. package/dist/src/utils/reshuffle.d.ts +30 -0
  172. package/dist/src/utils/reshuffle.d.ts.map +1 -0
  173. package/dist/src/utils/reshuffle.js +47 -0
  174. package/dist/src/utils/reshuffle.js.map +1 -0
  175. package/package.json +15 -9
  176. package/dist/bench/event-bus.bench.d.ts +0 -2
  177. package/dist/bench/event-bus.bench.d.ts.map +0 -1
  178. package/dist/bench/event-bus.bench.js +0 -228
  179. package/dist/bench/event-bus.bench.js.map +0 -1
  180. package/dist/bench/queue-only.bench.d.ts +0 -2
  181. package/dist/bench/queue-only.bench.d.ts.map +0 -1
  182. package/dist/bench/queue-only.bench.js +0 -46
  183. package/dist/bench/queue-only.bench.js.map +0 -1
  184. package/dist/bench/reactor-throughput.bench.d.ts +0 -2
  185. package/dist/bench/reactor-throughput.bench.d.ts.map +0 -1
  186. package/dist/bench/reactor-throughput.bench.js +0 -144
  187. package/dist/bench/reactor-throughput.bench.js.map +0 -1
  188. package/dist/src/interfaces/reactor.d.ts.map +0 -1
  189. package/dist/src/interfaces/reactor.js +0 -2
  190. package/dist/src/interfaces/reactor.js.map +0 -1
  191. package/dist/src/reactor.d.ts.map +0 -1
  192. package/dist/src/reactor.js.map +0 -1
  193. package/dist/src/utils.d.ts.map +0 -1
  194. package/dist/src/utils.js.map +0 -1
  195. package/dist/test/event-bus.test.d.ts +0 -2
  196. package/dist/test/event-bus.test.d.ts.map +0 -1
  197. package/dist/test/event-bus.test.js +0 -541
  198. package/dist/test/event-bus.test.js.map +0 -1
  199. package/dist/test/executor/executor-integration.test.d.ts +0 -2
  200. package/dist/test/executor/executor-integration.test.d.ts.map +0 -1
  201. package/dist/test/executor/executor-integration.test.js +0 -287
  202. package/dist/test/executor/executor-integration.test.js.map +0 -1
  203. package/dist/test/executor/job-execution-handle.test.d.ts +0 -2
  204. package/dist/test/executor/job-execution-handle.test.d.ts.map +0 -1
  205. package/dist/test/executor/job-execution-handle.test.js +0 -272
  206. package/dist/test/executor/job-execution-handle.test.js.map +0 -1
  207. package/dist/test/executor/simple-job-executor-manager.test.d.ts +0 -2
  208. package/dist/test/executor/simple-job-executor-manager.test.d.ts.map +0 -1
  209. package/dist/test/executor/simple-job-executor-manager.test.js +0 -132
  210. package/dist/test/executor/simple-job-executor-manager.test.js.map +0 -1
  211. package/dist/test/executor/simple-job-executor.test.d.ts +0 -2
  212. package/dist/test/executor/simple-job-executor.test.d.ts.map +0 -1
  213. package/dist/test/executor/simple-job-executor.test.js +0 -139
  214. package/dist/test/executor/simple-job-executor.test.js.map +0 -1
  215. package/dist/test/factories.d.ts +0 -122
  216. package/dist/test/factories.d.ts.map +0 -1
  217. package/dist/test/factories.js +0 -319
  218. package/dist/test/factories.js.map +0 -1
  219. package/dist/test/integration/document-drive-integration.test.d.ts +0 -2
  220. package/dist/test/integration/document-drive-integration.test.d.ts.map +0 -1
  221. package/dist/test/integration/document-drive-integration.test.js +0 -1102
  222. package/dist/test/integration/document-drive-integration.test.js.map +0 -1
  223. package/dist/test/integration/reactor-read.test.d.ts +0 -2
  224. package/dist/test/integration/reactor-read.test.d.ts.map +0 -1
  225. package/dist/test/integration/reactor-read.test.js +0 -300
  226. package/dist/test/integration/reactor-read.test.js.map +0 -1
  227. package/dist/test/queue/queue-integration.test.d.ts +0 -2
  228. package/dist/test/queue/queue-integration.test.d.ts.map +0 -1
  229. package/dist/test/queue/queue-integration.test.js +0 -322
  230. package/dist/test/queue/queue-integration.test.js.map +0 -1
  231. package/dist/test/queue/queue.test.d.ts +0 -2
  232. package/dist/test/queue/queue.test.d.ts.map +0 -1
  233. package/dist/test/queue/queue.test.js +0 -770
  234. package/dist/test/queue/queue.test.js.map +0 -1
  235. package/dist/test/registry/registry.test.d.ts +0 -2
  236. package/dist/test/registry/registry.test.d.ts.map +0 -1
  237. package/dist/test/registry/registry.test.js +0 -182
  238. package/dist/test/registry/registry.test.js.map +0 -1
  239. package/dist/test/utils.test.d.ts +0 -2
  240. package/dist/test/utils.test.d.ts.map +0 -1
  241. package/dist/test/utils.test.js +0 -66
  242. package/dist/test/utils.test.js.map +0 -1
  243. package/dist/tsconfig.tsbuildinfo +0 -1
  244. package/dist/vitest.config.d.ts +0 -3
  245. package/dist/vitest.config.d.ts.map +0 -1
  246. package/dist/vitest.config.js +0 -11
  247. package/dist/vitest.config.js.map +0 -1
  248. /package/dist/src/{utils.js → core/utils.js} +0 -0
@@ -1,770 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { EventBus } from "../../src/events/event-bus.js";
3
- import { InMemoryQueue } from "../../src/queue/queue.js";
4
- import { QueueEventTypes, } from "../../src/queue/types.js";
5
- import { createJobDependencyChain, createJobWithDependencies, createTestJob, createTestOperation, } from "../factories.js";
6
- describe("InMemoryQueue", () => {
7
- let queue;
8
- let eventBus;
9
- let mockEventBusEmit;
10
- beforeEach(() => {
11
- eventBus = new EventBus();
12
- mockEventBusEmit = vi.fn().mockResolvedValue(undefined);
13
- eventBus.emit = mockEventBusEmit;
14
- queue = new InMemoryQueue(eventBus);
15
- });
16
- describe("enqueue", () => {
17
- it("should add a job to the queue", async () => {
18
- const job = createTestJob();
19
- await queue.enqueue(job);
20
- const size = await queue.size(job.documentId, job.scope, job.branch);
21
- expect(size).toBe(1);
22
- });
23
- it("should emit a jobAvailable event when a job is enqueued", async () => {
24
- const job = createTestJob();
25
- await queue.enqueue(job);
26
- expect(mockEventBusEmit).toHaveBeenCalledWith(QueueEventTypes.JOB_AVAILABLE, {
27
- documentId: job.documentId,
28
- scope: job.scope,
29
- branch: job.branch,
30
- jobId: job.id,
31
- });
32
- });
33
- it("should organize jobs by documentId, scope, and branch", async () => {
34
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
35
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
36
- const job3 = createTestJob({
37
- id: "job-3",
38
- documentId: "doc-1",
39
- scope: "local",
40
- });
41
- await queue.enqueue(job1);
42
- await queue.enqueue(job2);
43
- await queue.enqueue(job3);
44
- expect(await queue.size("doc-1", "global", "main")).toBe(1);
45
- expect(await queue.size("doc-2", "global", "main")).toBe(1);
46
- expect(await queue.size("doc-1", "local", "main")).toBe(1);
47
- });
48
- it("should maintain FIFO order within the same queue", async () => {
49
- const job1 = createTestJob({ id: "job-1" });
50
- const job2 = createTestJob({ id: "job-2" });
51
- const job3 = createTestJob({ id: "job-3" });
52
- await queue.enqueue(job1);
53
- await queue.enqueue(job2);
54
- await queue.enqueue(job3);
55
- const dequeuedJob1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
56
- const dequeuedJob2 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
57
- const dequeuedJob3 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
58
- expect(dequeuedJob1?.job.id).toBe("job-1");
59
- expect(dequeuedJob2?.job.id).toBe("job-2");
60
- expect(dequeuedJob3?.job.id).toBe("job-3");
61
- });
62
- });
63
- describe("dequeue", () => {
64
- it("should return null when queue is empty", async () => {
65
- const job = await queue.dequeue("doc-1", "global", "main");
66
- expect(job).toBeNull();
67
- });
68
- it("should return and remove the first job from the queue", async () => {
69
- const testJob = createTestJob();
70
- await queue.enqueue(testJob);
71
- const dequeuedJob = await queue.dequeue(testJob.documentId, testJob.scope, testJob.branch);
72
- expect(dequeuedJob?.job).toEqual(testJob);
73
- expect(await queue.size(testJob.documentId, testJob.scope, testJob.branch)).toBe(0);
74
- });
75
- it("should only dequeue from the specified document/scope/branch", async () => {
76
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
77
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
78
- await queue.enqueue(job1);
79
- await queue.enqueue(job2);
80
- const dequeuedJob = await queue.dequeue("doc-1", "global", "main");
81
- expect(dequeuedJob?.job.id).toBe("job-1");
82
- expect(await queue.size("doc-2", "global", "main")).toBe(1);
83
- });
84
- it("should clean up empty queues after dequeuing", async () => {
85
- const job = createTestJob();
86
- await queue.enqueue(job);
87
- await queue.dequeue(job.documentId, job.scope, job.branch);
88
- // Verify queue is cleaned up by checking total size
89
- expect(await queue.totalSize()).toBe(0);
90
- });
91
- });
92
- describe("dequeueNext", () => {
93
- it("should return null when no jobs are available", async () => {
94
- const job = await queue.dequeueNext();
95
- expect(job).toBeNull();
96
- });
97
- it("should return a job from any available queue", async () => {
98
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
99
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
100
- await queue.enqueue(job1);
101
- await queue.enqueue(job2);
102
- const dequeuedJob = await queue.dequeueNext();
103
- expect(dequeuedJob).toBeDefined();
104
- expect([job1.id, job2.id]).toContain(dequeuedJob?.job.id);
105
- expect(await queue.totalSize()).toBe(1);
106
- });
107
- it("should clean up empty queues after dequeuing", async () => {
108
- const job = createTestJob();
109
- await queue.enqueue(job);
110
- await queue.dequeueNext();
111
- expect(await queue.totalSize()).toBe(0);
112
- });
113
- });
114
- describe("size", () => {
115
- it("should return 0 for non-existent queue", async () => {
116
- const size = await queue.size("doc-1", "global", "main");
117
- expect(size).toBe(0);
118
- });
119
- it("should return correct size for existing queue", async () => {
120
- const job1 = createTestJob({ id: "job-1" });
121
- const job2 = createTestJob({ id: "job-2" });
122
- await queue.enqueue(job1);
123
- await queue.enqueue(job2);
124
- const size = await queue.size(job1.documentId, job1.scope, job1.branch);
125
- expect(size).toBe(2);
126
- });
127
- it("should return size for specific queue only", async () => {
128
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
129
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
130
- await queue.enqueue(job1);
131
- await queue.enqueue(job2);
132
- expect(await queue.size("doc-1", "global", "main")).toBe(1);
133
- expect(await queue.size("doc-2", "global", "main")).toBe(1);
134
- });
135
- });
136
- describe("totalSize", () => {
137
- it("should return 0 when no jobs are queued", async () => {
138
- const totalSize = await queue.totalSize();
139
- expect(totalSize).toBe(0);
140
- });
141
- it("should return total count across all queues", async () => {
142
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
143
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
144
- const job3 = createTestJob({
145
- id: "job-3",
146
- documentId: "doc-1",
147
- scope: "local",
148
- });
149
- await queue.enqueue(job1);
150
- await queue.enqueue(job2);
151
- await queue.enqueue(job3);
152
- const totalSize = await queue.totalSize();
153
- expect(totalSize).toBe(3);
154
- });
155
- });
156
- describe("remove", () => {
157
- it("should return false when job does not exist", async () => {
158
- const removed = await queue.remove("non-existent-job");
159
- expect(removed).toBe(false);
160
- });
161
- it("should remove and return true when job exists", async () => {
162
- const job = createTestJob();
163
- await queue.enqueue(job);
164
- const removed = await queue.remove(job.id);
165
- expect(removed).toBe(true);
166
- expect(await queue.size(job.documentId, job.scope, job.branch)).toBe(0);
167
- });
168
- it("should remove job from middle of queue", async () => {
169
- const job1 = createTestJob({ id: "job-1" });
170
- const job2 = createTestJob({ id: "job-2" });
171
- const job3 = createTestJob({ id: "job-3" });
172
- await queue.enqueue(job1);
173
- await queue.enqueue(job2);
174
- await queue.enqueue(job3);
175
- const removed = await queue.remove(job2.id);
176
- expect(removed).toBe(true);
177
- expect(await queue.size(job1.documentId, job1.scope, job1.branch)).toBe(2);
178
- // Verify correct jobs remain
179
- const dequeuedJob1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
180
- const dequeuedJob2 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
181
- expect(dequeuedJob1?.job.id).toBe("job-1");
182
- expect(dequeuedJob2?.job.id).toBe("job-3");
183
- });
184
- it("should clean up empty queue after removing last job", async () => {
185
- const job = createTestJob();
186
- await queue.enqueue(job);
187
- await queue.remove(job.id);
188
- expect(await queue.totalSize()).toBe(0);
189
- });
190
- it("should handle orphaned job index entries gracefully", async () => {
191
- const job = createTestJob();
192
- await queue.enqueue(job);
193
- // Manually corrupt the state to simulate orphaned index
194
- const queueInstance = queue;
195
- // @ts-ignore - accessing private property for testing
196
- queueInstance.queues.clear();
197
- const removed = await queue.remove(job.id);
198
- expect(removed).toBe(false);
199
- });
200
- });
201
- describe("clear", () => {
202
- it("should clear specific queue", async () => {
203
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
204
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
205
- await queue.enqueue(job1);
206
- await queue.enqueue(job2);
207
- await queue.clear("doc-1", "global", "main");
208
- expect(await queue.size("doc-1", "global", "main")).toBe(0);
209
- expect(await queue.size("doc-2", "global", "main")).toBe(1);
210
- });
211
- it("should handle clearing non-existent queue", async () => {
212
- await expect(queue.clear("non-existent", "global", "main")).resolves.not.toThrow();
213
- });
214
- it("should clean up job indices when clearing", async () => {
215
- const job1 = createTestJob({ id: "job-1" });
216
- const job2 = createTestJob({ id: "job-2" });
217
- await queue.enqueue(job1);
218
- await queue.enqueue(job2);
219
- await queue.clear(job1.documentId, job1.scope, job1.branch);
220
- // Verify jobs can't be removed (they're no longer indexed)
221
- expect(await queue.remove(job1.id)).toBe(false);
222
- expect(await queue.remove(job2.id)).toBe(false);
223
- });
224
- });
225
- describe("clearAll", () => {
226
- it("should clear all queues", async () => {
227
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
228
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
229
- const job3 = createTestJob({
230
- id: "job-3",
231
- documentId: "doc-1",
232
- scope: "local",
233
- });
234
- await queue.enqueue(job1);
235
- await queue.enqueue(job2);
236
- await queue.enqueue(job3);
237
- await queue.clearAll();
238
- expect(await queue.totalSize()).toBe(0);
239
- expect(await queue.size("doc-1", "global", "main")).toBe(0);
240
- expect(await queue.size("doc-2", "global", "main")).toBe(0);
241
- expect(await queue.size("doc-1", "local", "main")).toBe(0);
242
- });
243
- it("should clean up all job indices", async () => {
244
- const job1 = createTestJob({ id: "job-1" });
245
- const job2 = createTestJob({ id: "job-2" });
246
- await queue.enqueue(job1);
247
- await queue.enqueue(job2);
248
- await queue.clearAll();
249
- // Verify jobs can't be removed (they're no longer indexed)
250
- expect(await queue.remove(job1.id)).toBe(false);
251
- expect(await queue.remove(job2.id)).toBe(false);
252
- });
253
- it("should handle clearing when already empty", async () => {
254
- await expect(queue.clearAll()).resolves.not.toThrow();
255
- expect(await queue.totalSize()).toBe(0);
256
- });
257
- });
258
- describe("event bus integration", () => {
259
- it("should emit events with correct data structure", async () => {
260
- const job = createTestJob({
261
- id: "test-job-id",
262
- documentId: "test-doc-id",
263
- scope: "test-scope",
264
- branch: "test-branch",
265
- });
266
- await queue.enqueue(job);
267
- expect(mockEventBusEmit).toHaveBeenCalledWith(QueueEventTypes.JOB_AVAILABLE, {
268
- documentId: "test-doc-id",
269
- scope: "test-scope",
270
- branch: "test-branch",
271
- jobId: "test-job-id",
272
- });
273
- });
274
- it("should handle event bus errors gracefully", async () => {
275
- mockEventBusEmit.mockRejectedValue(new Error("Event bus error"));
276
- const job = createTestJob();
277
- await expect(queue.enqueue(job)).rejects.toThrow("Event bus error");
278
- });
279
- it("should emit event for each job enqueued", async () => {
280
- const job1 = createTestJob({ id: "job-1" });
281
- const job2 = createTestJob({ id: "job-2" });
282
- await queue.enqueue(job1);
283
- await queue.enqueue(job2);
284
- expect(mockEventBusEmit).toHaveBeenCalledTimes(2);
285
- });
286
- });
287
- describe("queue key generation", () => {
288
- it("should create unique keys for different document/scope/branch combinations", async () => {
289
- const job1 = createTestJob({
290
- id: "job-1",
291
- documentId: "doc-1",
292
- scope: "global",
293
- branch: "main",
294
- });
295
- const job2 = createTestJob({
296
- id: "job-2",
297
- documentId: "doc-1",
298
- scope: "global",
299
- branch: "feature",
300
- });
301
- const job3 = createTestJob({
302
- id: "job-3",
303
- documentId: "doc-1",
304
- scope: "local",
305
- branch: "main",
306
- });
307
- const job4 = createTestJob({
308
- id: "job-4",
309
- documentId: "doc-2",
310
- scope: "global",
311
- branch: "main",
312
- });
313
- await queue.enqueue(job1);
314
- await queue.enqueue(job2);
315
- await queue.enqueue(job3);
316
- await queue.enqueue(job4);
317
- expect(await queue.size("doc-1", "global", "main")).toBe(1);
318
- expect(await queue.size("doc-1", "global", "feature")).toBe(1);
319
- expect(await queue.size("doc-1", "local", "main")).toBe(1);
320
- expect(await queue.size("doc-2", "global", "main")).toBe(1);
321
- });
322
- it("should handle special characters in identifiers", async () => {
323
- const job = createTestJob({
324
- documentId: "doc:with:colons",
325
- scope: "scope-with-dashes",
326
- branch: "branch_with_underscores",
327
- });
328
- await queue.enqueue(job);
329
- expect(await queue.size("doc:with:colons", "scope-with-dashes", "branch_with_underscores")).toBe(1);
330
- });
331
- });
332
- describe("dependency management", () => {
333
- it("should dequeue job with no dependencies immediately", async () => {
334
- const job = createTestJob({ id: "job-1", queueHint: [] });
335
- await queue.enqueue(job);
336
- const dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
337
- expect(dequeuedJob?.job.id).toBe("job-1");
338
- });
339
- it("should block job with unmet dependencies", async () => {
340
- const job1 = createTestJob({ id: "job-1", queueHint: ["job-0"] });
341
- await queue.enqueue(job1);
342
- const dequeuedJob = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
343
- expect(dequeuedJob).toBeNull();
344
- expect(await queue.size(job1.documentId, job1.scope, job1.branch)).toBe(1);
345
- });
346
- it("should dequeue job after dependencies are completed", async () => {
347
- const job1 = createTestJob({ id: "job-1", queueHint: [] });
348
- const job2 = createTestJob({ id: "job-2", queueHint: ["job-1"] });
349
- await queue.enqueue(job1);
350
- await queue.enqueue(job2);
351
- // First job should be dequeued
352
- const dequeuedJob1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
353
- expect(dequeuedJob1?.job.id).toBe("job-1");
354
- // Second job should be blocked
355
- const blockedJob = await queue.dequeue(job2.documentId, job2.scope, job2.branch);
356
- expect(blockedJob).toBeNull();
357
- // Complete first job
358
- await queue.completeJob("job-1");
359
- // Now second job should be available
360
- const dequeuedJob2 = await queue.dequeue(job2.documentId, job2.scope, job2.branch);
361
- expect(dequeuedJob2?.job.id).toBe("job-2");
362
- });
363
- it("should handle multiple dependencies", async () => {
364
- const job1 = createTestJob({ id: "job-1", queueHint: [] });
365
- const job2 = createTestJob({ id: "job-2", queueHint: [] });
366
- const job3 = createTestJob({
367
- id: "job-3",
368
- queueHint: ["job-1", "job-2"],
369
- });
370
- await queue.enqueue(job1);
371
- await queue.enqueue(job2);
372
- await queue.enqueue(job3);
373
- // Dequeue and complete first job
374
- const dequeuedJob1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
375
- expect(dequeuedJob1?.job.id).toBe("job-1");
376
- await queue.completeJob("job-1");
377
- // Next dequeue should return job-2 (not blocked), not job-3 (still blocked)
378
- const nextJob = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
379
- expect(nextJob?.job.id).toBe("job-2");
380
- await queue.completeJob("job-2");
381
- // Now job 3 should be available
382
- const dequeuedJob3 = await queue.dequeue(job3.documentId, job3.scope, job3.branch);
383
- expect(dequeuedJob3?.job.id).toBe("job-3");
384
- });
385
- it("should handle dependency chains", async () => {
386
- const jobs = createJobDependencyChain(4);
387
- for (const job of jobs) {
388
- await queue.enqueue(job);
389
- }
390
- // Process in order
391
- const d1 = await queue.dequeue(jobs[0].documentId, jobs[0].scope, jobs[0].branch);
392
- expect(d1?.job.id).toBe("job-1");
393
- // Others should be blocked
394
- expect(await queue.dequeue(jobs[1].documentId, jobs[1].scope, jobs[1].branch)).toBeNull();
395
- expect(await queue.dequeue(jobs[2].documentId, jobs[2].scope, jobs[2].branch)).toBeNull();
396
- expect(await queue.dequeue(jobs[3].documentId, jobs[3].scope, jobs[3].branch)).toBeNull();
397
- await queue.completeJob("job-1");
398
- const d2 = await queue.dequeue(jobs[1].documentId, jobs[1].scope, jobs[1].branch);
399
- expect(d2?.job.id).toBe("job-2");
400
- // job-3 and job-4 still blocked
401
- expect(await queue.dequeue(jobs[2].documentId, jobs[2].scope, jobs[2].branch)).toBeNull();
402
- expect(await queue.dequeue(jobs[3].documentId, jobs[3].scope, jobs[3].branch)).toBeNull();
403
- await queue.completeJob("job-2");
404
- const d3 = await queue.dequeue(jobs[2].documentId, jobs[2].scope, jobs[2].branch);
405
- expect(d3?.job.id).toBe("job-3");
406
- // job-4 still blocked
407
- expect(await queue.dequeue(jobs[3].documentId, jobs[3].scope, jobs[3].branch)).toBeNull();
408
- await queue.completeJob("job-3");
409
- const d4 = await queue.dequeue(jobs[3].documentId, jobs[3].scope, jobs[3].branch);
410
- expect(d4?.job.id).toBe("job-4");
411
- });
412
- it("should dequeue jobs out of order based on dependencies", async () => {
413
- const job1 = createTestJob({ id: "job-1", queueHint: ["job-0"] });
414
- const job2 = createTestJob({ id: "job-2", queueHint: [] });
415
- const job3 = createTestJob({ id: "job-3", queueHint: [] });
416
- await queue.enqueue(job1);
417
- await queue.enqueue(job2);
418
- await queue.enqueue(job3);
419
- // Job 1 is blocked, so job 2 should be dequeued
420
- const dequeuedJob = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
421
- expect(dequeuedJob?.job.id).toBe("job-2");
422
- });
423
- it("should handle dependencies across different queues", async () => {
424
- const job1 = createTestJob({
425
- id: "job-1",
426
- documentId: "doc-1",
427
- queueHint: [],
428
- });
429
- const job2 = createTestJob({
430
- id: "job-2",
431
- documentId: "doc-2",
432
- queueHint: ["job-1"],
433
- });
434
- await queue.enqueue(job1);
435
- await queue.enqueue(job2);
436
- // Job 2 should be blocked even though it's in a different queue
437
- const blockedJob = await queue.dequeue("doc-2", "global", "main");
438
- expect(blockedJob).toBeNull();
439
- // Dequeue and complete job 1
440
- const dequeuedJob1 = await queue.dequeue("doc-1", "global", "main");
441
- expect(dequeuedJob1?.job.id).toBe("job-1");
442
- await queue.completeJob("job-1");
443
- // Now job 2 should be available
444
- const dequeuedJob2 = await queue.dequeue("doc-2", "global", "main");
445
- expect(dequeuedJob2?.job.id).toBe("job-2");
446
- });
447
- it("should work with dequeueNext respecting dependencies", async () => {
448
- const job1 = createTestJob({
449
- id: "job-1",
450
- documentId: "doc-1",
451
- queueHint: ["job-0"],
452
- });
453
- const job2 = createTestJob({
454
- id: "job-2",
455
- documentId: "doc-2",
456
- queueHint: [],
457
- });
458
- await queue.enqueue(job1);
459
- await queue.enqueue(job2);
460
- // Should dequeue job-2 since job-1 is blocked
461
- const dequeuedJob = await queue.dequeueNext();
462
- expect(dequeuedJob?.job.id).toBe("job-2");
463
- });
464
- it("should handle circular dependencies by blocking all involved jobs", async () => {
465
- const job1 = createJobWithDependencies("job-1", ["job-2"]);
466
- const job2 = createJobWithDependencies("job-2", ["job-1"]);
467
- await queue.enqueue(job1);
468
- await queue.enqueue(job2);
469
- // Both jobs should be blocked
470
- const dequeuedJob1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
471
- expect(dequeuedJob1).toBeNull();
472
- const dequeuedJob2 = await queue.dequeue(job2.documentId, job2.scope, job2.branch);
473
- expect(dequeuedJob2).toBeNull();
474
- });
475
- it("should handle self-dependencies by blocking the job", async () => {
476
- const job = createJobWithDependencies("job-1", ["job-1"]);
477
- await queue.enqueue(job);
478
- const dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
479
- expect(dequeuedJob).toBeNull();
480
- });
481
- it("should clear completed jobs tracking when clearAll is called", async () => {
482
- const job1 = createTestJob({ id: "job-1", queueHint: [] });
483
- const job2 = createTestJob({ id: "job-2", queueHint: ["job-1"] });
484
- await queue.enqueue(job1);
485
- // Complete job-1
486
- await queue.dequeue(job1.documentId, job1.scope, job1.branch);
487
- await queue.completeJob("job-1");
488
- // Clear all
489
- await queue.clearAll();
490
- // Re-enqueue job-2 - it should be blocked again since completed jobs were cleared
491
- await queue.enqueue(job2);
492
- const dequeuedJob = await queue.dequeue(job2.documentId, job2.scope, job2.branch);
493
- expect(dequeuedJob).toBeNull();
494
- });
495
- it("should handle already completed dependencies", async () => {
496
- // Complete a job that doesn't exist in queue
497
- await queue.completeJob("job-0");
498
- // Enqueue a job that depends on it
499
- const job1 = createJobWithDependencies("job-1", ["job-0"]);
500
- await queue.enqueue(job1);
501
- // Should be able to dequeue since dependency is already complete
502
- const dequeuedJob = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
503
- expect(dequeuedJob?.job.id).toBe("job-1");
504
- });
505
- it("should handle mixed dependencies (some met, some unmet)", async () => {
506
- // Complete one dependency
507
- await queue.completeJob("job-0");
508
- // Enqueue a job with mixed dependencies
509
- const job = createTestJob({
510
- id: "job-3",
511
- queueHint: ["job-0", "job-1", "job-2"],
512
- });
513
- await queue.enqueue(job);
514
- // Should be blocked (job-1 and job-2 not complete)
515
- let dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
516
- expect(dequeuedJob).toBeNull();
517
- // Complete remaining dependencies
518
- await queue.completeJob("job-1");
519
- await queue.completeJob("job-2");
520
- // Now should be available
521
- dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
522
- expect(dequeuedJob?.job.id).toBe("job-3");
523
- });
524
- it("should handle failJob without marking as completed", async () => {
525
- const job1 = createTestJob({ id: "job-1", queueHint: [] });
526
- const job2 = createTestJob({ id: "job-2", queueHint: ["job-1"] });
527
- await queue.enqueue(job1);
528
- await queue.enqueue(job2);
529
- // Dequeue and fail job 1
530
- const d1 = await queue.dequeue(job1.documentId, job1.scope, job1.branch);
531
- expect(d1?.job.id).toBe("job-1");
532
- await queue.failJob("job-1", "Test error");
533
- // Job 2 should still be blocked since job 1 wasn't completed
534
- const dequeuedJob = await queue.dequeue(job2.documentId, job2.scope, job2.branch);
535
- expect(dequeuedJob).toBeNull();
536
- });
537
- });
538
- describe("isDrained", () => {
539
- it("should return true when queue is empty", () => {
540
- expect(queue.isDrained).toBe(true);
541
- });
542
- it("should return false when jobs are pending", async () => {
543
- const job = createTestJob();
544
- await queue.enqueue(job);
545
- expect(queue.isDrained).toBe(false);
546
- });
547
- it("should return false when jobs are executing", async () => {
548
- const job = createTestJob();
549
- await queue.enqueue(job);
550
- // Dequeue the job (marks it as executing)
551
- await queue.dequeueNext();
552
- expect(queue.isDrained).toBe(false);
553
- });
554
- it("should return true after all jobs are completed", async () => {
555
- const job = createTestJob();
556
- await queue.enqueue(job);
557
- const dequeuedJob = await queue.dequeueNext();
558
- expect(queue.isDrained).toBe(false);
559
- await queue.completeJob(dequeuedJob.job.id);
560
- expect(queue.isDrained).toBe(true);
561
- });
562
- it("should return true after all jobs are failed", async () => {
563
- const job = createTestJob();
564
- await queue.enqueue(job);
565
- const dequeuedJob = await queue.dequeueNext();
566
- expect(queue.isDrained).toBe(false);
567
- await queue.failJob(dequeuedJob.job.id, "Test failure");
568
- expect(queue.isDrained).toBe(true);
569
- });
570
- it("should handle multiple queues", async () => {
571
- const job1 = createTestJob({ id: "job-1", documentId: "doc-1" });
572
- const job2 = createTestJob({ id: "job-2", documentId: "doc-2" });
573
- await queue.enqueue(job1);
574
- await queue.enqueue(job2);
575
- expect(queue.isDrained).toBe(false);
576
- const dequeued1 = await queue.dequeueNext();
577
- await queue.completeJob(dequeued1.job.id);
578
- expect(queue.isDrained).toBe(false);
579
- const dequeued2 = await queue.dequeueNext();
580
- await queue.completeJob(dequeued2.job.id);
581
- expect(queue.isDrained).toBe(true);
582
- });
583
- });
584
- describe("block", () => {
585
- it("should prevent new jobs from being enqueued", async () => {
586
- queue.block();
587
- const job = createTestJob();
588
- await expect(queue.enqueue(job)).rejects.toThrow("Queue is blocked");
589
- });
590
- it("should allow existing jobs to be processed", async () => {
591
- const job = createTestJob();
592
- await queue.enqueue(job);
593
- queue.block();
594
- const dequeuedJob = await queue.dequeueNext();
595
- expect(dequeuedJob).toBeDefined();
596
- expect(dequeuedJob?.job.id).toBe(job.id);
597
- });
598
- it("should call onDrained callback when queue becomes drained", async () => {
599
- const onDrained = vi.fn();
600
- const job = createTestJob();
601
- await queue.enqueue(job);
602
- queue.block(onDrained);
603
- expect(onDrained).not.toHaveBeenCalled();
604
- const dequeuedJob = await queue.dequeueNext();
605
- await queue.completeJob(dequeuedJob.job.id);
606
- expect(onDrained).toHaveBeenCalledTimes(1);
607
- });
608
- it("should call onDrained immediately if already drained", () => {
609
- const onDrained = vi.fn();
610
- queue.block(onDrained);
611
- expect(onDrained).toHaveBeenCalledTimes(1);
612
- });
613
- it("should not call onDrained if unblocked before draining", async () => {
614
- const onDrained = vi.fn();
615
- const job = createTestJob();
616
- await queue.enqueue(job);
617
- queue.block(onDrained);
618
- queue.unblock();
619
- const dequeuedJob = await queue.dequeueNext();
620
- await queue.completeJob(dequeuedJob.job.id);
621
- expect(onDrained).not.toHaveBeenCalled();
622
- });
623
- it("should handle multiple block calls", () => {
624
- const onDrained1 = vi.fn();
625
- const onDrained2 = vi.fn();
626
- queue.block(onDrained1);
627
- queue.block(onDrained2);
628
- // Only the second callback should be registered
629
- expect(onDrained1).toHaveBeenCalledTimes(1);
630
- expect(onDrained2).toHaveBeenCalledTimes(1);
631
- });
632
- it("should handle block without callback", async () => {
633
- queue.block();
634
- const job = createTestJob();
635
- await expect(queue.enqueue(job)).rejects.toThrow("Queue is blocked");
636
- });
637
- });
638
- describe("unblock", () => {
639
- it("should allow new jobs to be enqueued after unblocking", async () => {
640
- queue.block();
641
- const job1 = createTestJob({ id: "job-1" });
642
- await expect(queue.enqueue(job1)).rejects.toThrow("Queue is blocked");
643
- queue.unblock();
644
- const job2 = createTestJob({ id: "job-2" });
645
- await expect(queue.enqueue(job2)).resolves.not.toThrow();
646
- const dequeuedJob = await queue.dequeueNext();
647
- expect(dequeuedJob?.job.id).toBe("job-2");
648
- });
649
- it("should clear onDrained callback", async () => {
650
- const onDrained = vi.fn();
651
- const job = createTestJob();
652
- await queue.enqueue(job);
653
- queue.block(onDrained);
654
- queue.unblock();
655
- // Complete the job after unblocking
656
- const dequeuedJob = await queue.dequeueNext();
657
- await queue.completeJob(dequeuedJob.job.id);
658
- // Callback should not be called since we unblocked
659
- expect(onDrained).not.toHaveBeenCalled();
660
- });
661
- it("should handle unblock when not blocked", async () => {
662
- expect(() => queue.unblock()).not.toThrow();
663
- const job = createTestJob();
664
- await expect(queue.enqueue(job)).resolves.not.toThrow();
665
- });
666
- });
667
- describe("block/unblock integration", () => {
668
- it("should handle complex block/unblock scenarios", async () => {
669
- const onDrained1 = vi.fn();
670
- const onDrained2 = vi.fn();
671
- // Add some jobs
672
- const job1 = createTestJob({ id: "job-1" });
673
- const job2 = createTestJob({ id: "job-2" });
674
- await queue.enqueue(job1);
675
- await queue.enqueue(job2);
676
- // Block with callback
677
- queue.block(onDrained1);
678
- // Process one job
679
- const dequeued1 = await queue.dequeueNext();
680
- await queue.completeJob(dequeued1.job.id);
681
- expect(onDrained1).not.toHaveBeenCalled();
682
- // Unblock and re-block with new callback
683
- queue.unblock();
684
- queue.block(onDrained2);
685
- // Process last job
686
- const dequeued2 = await queue.dequeueNext();
687
- await queue.completeJob(dequeued2.job.id);
688
- // Only second callback should be called
689
- expect(onDrained1).not.toHaveBeenCalled();
690
- expect(onDrained2).toHaveBeenCalledTimes(1);
691
- });
692
- it("should handle retry while blocked", async () => {
693
- const job = createTestJob({ id: "job-1", maxRetries: 3 });
694
- await queue.enqueue(job);
695
- queue.block();
696
- const dequeuedJob = await queue.dequeueNext();
697
- // Retry should fail because queue is blocked
698
- await expect(queue.retryJob(dequeuedJob.job.id, "Test error")).rejects.toThrow("Queue is blocked");
699
- });
700
- it("should track drain state correctly with dependencies", async () => {
701
- const job1 = createTestJob({ id: "job-1", queueHint: [] });
702
- const job2 = createTestJob({ id: "job-2", queueHint: ["job-1"] });
703
- await queue.enqueue(job1);
704
- await queue.enqueue(job2);
705
- expect(queue.isDrained).toBe(false);
706
- // Process first job
707
- const dequeued1 = await queue.dequeueNext();
708
- expect(dequeued1?.job.id).toBe("job-1");
709
- expect(queue.isDrained).toBe(false);
710
- await queue.completeJob("job-1");
711
- expect(queue.isDrained).toBe(false); // job-2 still pending
712
- // Process second job
713
- const dequeued2 = await queue.dequeueNext();
714
- expect(dequeued2?.job.id).toBe("job-2");
715
- expect(queue.isDrained).toBe(false);
716
- await queue.completeJob("job-2");
717
- expect(queue.isDrained).toBe(true);
718
- });
719
- });
720
- describe("job properties", () => {
721
- it("should preserve all job properties", async () => {
722
- const operation = createTestOperation({
723
- index: 42,
724
- timestampUtcMs: "2023-01-01T00:00:00.000Z",
725
- hash: "custom-hash",
726
- skip: 5,
727
- action: {
728
- id: "action-1",
729
- type: "custom-operation",
730
- timestampUtcMs: "2023-01-01T00:00:00.000Z",
731
- input: { custom: "input" },
732
- scope: "global",
733
- },
734
- error: "test error",
735
- id: "custom-op-id",
736
- });
737
- const job = createTestJob({
738
- id: "custom-job-id",
739
- documentId: "custom-doc-id",
740
- scope: "custom-scope",
741
- branch: "custom-branch",
742
- operation,
743
- createdAt: "2023-01-01T00:00:00.000Z",
744
- retryCount: 2,
745
- maxRetries: 5,
746
- });
747
- await queue.enqueue(job);
748
- const dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
749
- expect(dequeuedJob?.job).toEqual(job);
750
- });
751
- it("should handle jobs with optional properties", async () => {
752
- const job = {
753
- id: "minimal-job",
754
- documentId: "doc-1",
755
- scope: "global",
756
- branch: "main",
757
- operation: createTestOperation(),
758
- createdAt: new Date().toISOString(),
759
- queueHint: [],
760
- // retryCount and maxRetries are optional
761
- };
762
- await queue.enqueue(job);
763
- const dequeuedJob = await queue.dequeue(job.documentId, job.scope, job.branch);
764
- expect(dequeuedJob?.job).toEqual(job);
765
- expect(dequeuedJob?.job.retryCount).toBeUndefined();
766
- expect(dequeuedJob?.job.maxRetries).toBeUndefined();
767
- });
768
- });
769
- });
770
- //# sourceMappingURL=queue.test.js.map