@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.
- package/dist/src/cache/buffer/ring-buffer.d.ts +37 -0
- package/dist/src/cache/buffer/ring-buffer.d.ts.map +1 -0
- package/dist/src/cache/buffer/ring-buffer.js +69 -0
- package/dist/src/cache/buffer/ring-buffer.js.map +1 -0
- package/dist/src/cache/kysely-write-cache.d.ts +133 -0
- package/dist/src/cache/kysely-write-cache.d.ts.map +1 -0
- package/dist/src/cache/kysely-write-cache.js +375 -0
- package/dist/src/cache/kysely-write-cache.js.map +1 -0
- package/dist/src/cache/lru/lru-tracker.d.ts +15 -0
- package/dist/src/cache/lru/lru-tracker.d.ts.map +1 -0
- package/dist/src/cache/lru/lru-tracker.js +96 -0
- package/dist/src/cache/lru/lru-tracker.js.map +1 -0
- package/dist/src/cache/types.d.ts +42 -0
- package/dist/src/cache/types.d.ts.map +1 -0
- package/dist/src/cache/types.js +2 -0
- package/dist/src/cache/types.js.map +1 -0
- package/dist/src/cache/write/interfaces.d.ts +83 -0
- package/dist/src/cache/write/interfaces.d.ts.map +1 -0
- package/dist/src/cache/write/interfaces.js +2 -0
- package/dist/src/cache/write/interfaces.js.map +1 -0
- package/dist/src/client/reactor-client.d.ts +103 -0
- package/dist/src/client/reactor-client.d.ts.map +1 -0
- package/dist/src/client/reactor-client.js +184 -0
- package/dist/src/client/reactor-client.js.map +1 -0
- package/dist/src/client/types.d.ts +213 -0
- package/dist/src/client/types.d.ts.map +1 -0
- package/dist/src/client/types.js +14 -0
- package/dist/src/client/types.js.map +1 -0
- package/dist/src/core/builder.d.ts +20 -0
- package/dist/src/core/builder.d.ts.map +1 -0
- package/dist/src/core/builder.js +47 -0
- package/dist/src/core/builder.js.map +1 -0
- package/dist/src/{reactor.d.ts → core/reactor.d.ts} +13 -30
- package/dist/src/core/reactor.d.ts.map +1 -0
- package/dist/src/{reactor.js → core/reactor.js} +147 -111
- package/dist/src/core/reactor.js.map +1 -0
- package/dist/src/{interfaces/reactor.d.ts → core/types.d.ts} +7 -7
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +2 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/{utils.d.ts → core/utils.d.ts} +2 -2
- package/dist/src/core/utils.d.ts.map +1 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/events/event-bus.d.ts +2 -2
- package/dist/src/events/event-bus.d.ts.map +1 -1
- package/dist/src/events/event-bus.js +1 -1
- package/dist/src/events/event-bus.js.map +1 -1
- package/dist/src/events/interfaces.d.ts +1 -1
- package/dist/src/events/interfaces.d.ts.map +1 -1
- package/dist/src/events/types.d.ts +14 -0
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js +6 -0
- package/dist/src/events/types.js.map +1 -1
- package/dist/src/executor/interfaces.d.ts +2 -2
- package/dist/src/executor/interfaces.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor-manager.d.ts +5 -1
- package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor-manager.js +98 -36
- package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
- package/dist/src/executor/simple-job-executor.d.ts +32 -3
- package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor.js +436 -17
- package/dist/src/executor/simple-job-executor.js.map +1 -1
- package/dist/src/executor/types.d.ts +10 -3
- package/dist/src/executor/types.d.ts.map +1 -1
- package/dist/src/executor/types.js.map +1 -1
- package/dist/src/executor/util.d.ts +47 -0
- package/dist/src/executor/util.d.ts.map +1 -0
- package/dist/src/executor/util.js +113 -0
- package/dist/src/executor/util.js.map +1 -0
- package/dist/src/index.d.ts +23 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +30 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts +15 -0
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -0
- package/dist/src/job-tracker/in-memory-job-tracker.js +78 -0
- package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -0
- package/dist/src/job-tracker/index.d.ts +3 -0
- package/dist/src/job-tracker/index.d.ts.map +1 -0
- package/dist/src/job-tracker/index.js +2 -0
- package/dist/src/job-tracker/index.js.map +1 -0
- package/dist/src/job-tracker/interfaces.d.ts +41 -0
- package/dist/src/job-tracker/interfaces.d.ts.map +1 -0
- package/dist/src/job-tracker/interfaces.js +2 -0
- package/dist/src/job-tracker/interfaces.js.map +1 -0
- package/dist/src/queue/interfaces.d.ts +1 -1
- package/dist/src/queue/interfaces.d.ts.map +1 -1
- package/dist/src/queue/job-execution-handle.d.ts +2 -1
- package/dist/src/queue/job-execution-handle.d.ts.map +1 -1
- package/dist/src/queue/job-execution-handle.js.map +1 -1
- package/dist/src/queue/queue.d.ts +3 -3
- package/dist/src/queue/queue.d.ts.map +1 -1
- package/dist/src/queue/queue.js +47 -25
- package/dist/src/queue/queue.js.map +1 -1
- package/dist/src/queue/types.d.ts +7 -5
- package/dist/src/queue/types.d.ts.map +1 -1
- package/dist/src/queue/types.js.map +1 -1
- package/dist/src/read-models/coordinator.d.ts +38 -0
- package/dist/src/read-models/coordinator.d.ts.map +1 -0
- package/dist/src/read-models/coordinator.js +62 -0
- package/dist/src/read-models/coordinator.js.map +1 -0
- package/dist/src/read-models/document-view.d.ts +20 -0
- package/dist/src/read-models/document-view.d.ts.map +1 -0
- package/dist/src/read-models/document-view.js +365 -0
- package/dist/src/read-models/document-view.js.map +1 -0
- package/dist/src/read-models/interfaces.d.ts +29 -0
- package/dist/src/read-models/interfaces.d.ts.map +1 -0
- package/dist/src/read-models/interfaces.js +2 -0
- package/dist/src/read-models/interfaces.js.map +1 -0
- package/dist/src/read-models/types.d.ts +46 -0
- package/dist/src/read-models/types.d.ts.map +1 -0
- package/dist/src/read-models/types.js +2 -0
- package/dist/src/read-models/types.js.map +1 -0
- package/dist/src/registry/implementation.js +1 -1
- package/dist/src/registry/implementation.js.map +1 -1
- package/dist/src/shared/awaiter.d.ts +32 -0
- package/dist/src/shared/awaiter.d.ts.map +1 -0
- package/dist/src/shared/awaiter.js +132 -0
- package/dist/src/shared/awaiter.js.map +1 -0
- package/dist/src/shared/errors.d.ts +17 -0
- package/dist/src/shared/errors.d.ts.map +1 -0
- package/dist/src/shared/errors.js +33 -0
- package/dist/src/shared/errors.js.map +1 -0
- package/dist/src/shared/factories.d.ts +1 -1
- package/dist/src/shared/factories.d.ts.map +1 -1
- package/dist/src/shared/types.d.ts +8 -0
- package/dist/src/shared/types.d.ts.map +1 -1
- package/dist/src/shared/types.js +5 -0
- package/dist/src/shared/types.js.map +1 -1
- package/dist/src/signer/passthrough-signer.d.ts +6 -0
- package/dist/src/signer/passthrough-signer.d.ts.map +1 -0
- package/dist/src/signer/passthrough-signer.js +6 -0
- package/dist/src/signer/passthrough-signer.js.map +1 -0
- package/dist/src/signer/types.d.ts +15 -0
- package/dist/src/signer/types.d.ts.map +1 -0
- package/dist/src/signer/types.js +2 -0
- package/dist/src/signer/types.js.map +1 -0
- package/dist/src/storage/interfaces.d.ts +121 -0
- package/dist/src/storage/interfaces.d.ts.map +1 -0
- package/dist/src/storage/interfaces.js +19 -0
- package/dist/src/storage/interfaces.js.map +1 -0
- package/dist/src/storage/kysely/keyframe-store.d.ts +15 -0
- package/dist/src/storage/kysely/keyframe-store.d.ts.map +1 -0
- package/dist/src/storage/kysely/keyframe-store.js +71 -0
- package/dist/src/storage/kysely/keyframe-store.js.map +1 -0
- package/dist/src/storage/kysely/store.d.ts +15 -0
- package/dist/src/storage/kysely/store.d.ts.map +1 -0
- package/dist/src/storage/kysely/store.js +196 -0
- package/dist/src/storage/kysely/store.js.map +1 -0
- package/dist/src/storage/kysely/types.d.ts +39 -0
- package/dist/src/storage/kysely/types.d.ts.map +1 -0
- package/dist/src/storage/kysely/types.js +2 -0
- package/dist/src/storage/kysely/types.js.map +1 -0
- package/dist/src/storage/txn.d.ts +15 -0
- package/dist/src/storage/txn.d.ts.map +1 -0
- package/dist/src/storage/txn.js +41 -0
- package/dist/src/storage/txn.js.map +1 -0
- package/dist/src/subs/default-error-handler.d.ts +13 -0
- package/dist/src/subs/default-error-handler.d.ts.map +1 -0
- package/dist/src/subs/default-error-handler.js +27 -0
- package/dist/src/subs/default-error-handler.js.map +1 -0
- package/dist/src/subs/react-subscription-manager.d.ts +45 -0
- package/dist/src/subs/react-subscription-manager.d.ts.map +1 -0
- package/dist/src/subs/react-subscription-manager.js +185 -0
- package/dist/src/subs/react-subscription-manager.js.map +1 -0
- package/dist/src/subs/types.d.ts +64 -0
- package/dist/src/subs/types.d.ts.map +1 -0
- package/dist/src/subs/types.js +2 -0
- package/dist/src/subs/types.js.map +1 -0
- package/dist/src/utils/reshuffle.d.ts +30 -0
- package/dist/src/utils/reshuffle.d.ts.map +1 -0
- package/dist/src/utils/reshuffle.js +47 -0
- package/dist/src/utils/reshuffle.js.map +1 -0
- package/package.json +15 -9
- package/dist/bench/event-bus.bench.d.ts +0 -2
- package/dist/bench/event-bus.bench.d.ts.map +0 -1
- package/dist/bench/event-bus.bench.js +0 -228
- package/dist/bench/event-bus.bench.js.map +0 -1
- package/dist/bench/queue-only.bench.d.ts +0 -2
- package/dist/bench/queue-only.bench.d.ts.map +0 -1
- package/dist/bench/queue-only.bench.js +0 -46
- package/dist/bench/queue-only.bench.js.map +0 -1
- package/dist/bench/reactor-throughput.bench.d.ts +0 -2
- package/dist/bench/reactor-throughput.bench.d.ts.map +0 -1
- package/dist/bench/reactor-throughput.bench.js +0 -144
- package/dist/bench/reactor-throughput.bench.js.map +0 -1
- package/dist/src/interfaces/reactor.d.ts.map +0 -1
- package/dist/src/interfaces/reactor.js +0 -2
- package/dist/src/interfaces/reactor.js.map +0 -1
- package/dist/src/reactor.d.ts.map +0 -1
- package/dist/src/reactor.js.map +0 -1
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.js.map +0 -1
- package/dist/test/event-bus.test.d.ts +0 -2
- package/dist/test/event-bus.test.d.ts.map +0 -1
- package/dist/test/event-bus.test.js +0 -541
- package/dist/test/event-bus.test.js.map +0 -1
- package/dist/test/executor/executor-integration.test.d.ts +0 -2
- package/dist/test/executor/executor-integration.test.d.ts.map +0 -1
- package/dist/test/executor/executor-integration.test.js +0 -287
- package/dist/test/executor/executor-integration.test.js.map +0 -1
- package/dist/test/executor/job-execution-handle.test.d.ts +0 -2
- package/dist/test/executor/job-execution-handle.test.d.ts.map +0 -1
- package/dist/test/executor/job-execution-handle.test.js +0 -272
- package/dist/test/executor/job-execution-handle.test.js.map +0 -1
- package/dist/test/executor/simple-job-executor-manager.test.d.ts +0 -2
- package/dist/test/executor/simple-job-executor-manager.test.d.ts.map +0 -1
- package/dist/test/executor/simple-job-executor-manager.test.js +0 -132
- package/dist/test/executor/simple-job-executor-manager.test.js.map +0 -1
- package/dist/test/executor/simple-job-executor.test.d.ts +0 -2
- package/dist/test/executor/simple-job-executor.test.d.ts.map +0 -1
- package/dist/test/executor/simple-job-executor.test.js +0 -139
- package/dist/test/executor/simple-job-executor.test.js.map +0 -1
- package/dist/test/factories.d.ts +0 -122
- package/dist/test/factories.d.ts.map +0 -1
- package/dist/test/factories.js +0 -319
- package/dist/test/factories.js.map +0 -1
- package/dist/test/integration/document-drive-integration.test.d.ts +0 -2
- package/dist/test/integration/document-drive-integration.test.d.ts.map +0 -1
- package/dist/test/integration/document-drive-integration.test.js +0 -1102
- package/dist/test/integration/document-drive-integration.test.js.map +0 -1
- package/dist/test/integration/reactor-read.test.d.ts +0 -2
- package/dist/test/integration/reactor-read.test.d.ts.map +0 -1
- package/dist/test/integration/reactor-read.test.js +0 -300
- package/dist/test/integration/reactor-read.test.js.map +0 -1
- package/dist/test/queue/queue-integration.test.d.ts +0 -2
- package/dist/test/queue/queue-integration.test.d.ts.map +0 -1
- package/dist/test/queue/queue-integration.test.js +0 -322
- package/dist/test/queue/queue-integration.test.js.map +0 -1
- package/dist/test/queue/queue.test.d.ts +0 -2
- package/dist/test/queue/queue.test.d.ts.map +0 -1
- package/dist/test/queue/queue.test.js +0 -770
- package/dist/test/queue/queue.test.js.map +0 -1
- package/dist/test/registry/registry.test.d.ts +0 -2
- package/dist/test/registry/registry.test.d.ts.map +0 -1
- package/dist/test/registry/registry.test.js +0 -182
- package/dist/test/registry/registry.test.js.map +0 -1
- package/dist/test/utils.test.d.ts +0 -2
- package/dist/test/utils.test.d.ts.map +0 -1
- package/dist/test/utils.test.js +0 -66
- package/dist/test/utils.test.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -11
- package/dist/vitest.config.js.map +0 -1
- /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
|