@dudousxd/nestjs-durable-testing 0.6.0 → 0.7.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/index.d.ts +19 -2
- package/dist/index.js +282 -88
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,6 +5,170 @@ var __export = (target, all) => {
|
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
// src/admission-backend-conformance.ts
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
function runAdmissionBackendContract(name, makeBackend) {
|
|
11
|
+
describe(`AdmissionBackend contract: ${name}`, () => {
|
|
12
|
+
let now = 1e6;
|
|
13
|
+
const make = /* @__PURE__ */ __name((config) => {
|
|
14
|
+
now = 1e6;
|
|
15
|
+
const backend = makeBackend(() => now);
|
|
16
|
+
backend.register(config);
|
|
17
|
+
return backend;
|
|
18
|
+
}, "make");
|
|
19
|
+
it("caps concurrency: a second call blocks until the first releases", async () => {
|
|
20
|
+
const b = make({
|
|
21
|
+
name: "q",
|
|
22
|
+
concurrency: 1
|
|
23
|
+
});
|
|
24
|
+
expect((await b.tryAdmit("q", {
|
|
25
|
+
waiterId: "a"
|
|
26
|
+
})).ok).toBe(true);
|
|
27
|
+
expect((await b.tryAdmit("q", {
|
|
28
|
+
waiterId: "b"
|
|
29
|
+
})).ok).toBe(false);
|
|
30
|
+
await b.release("q", "a");
|
|
31
|
+
expect((await b.tryAdmit("q", {
|
|
32
|
+
waiterId: "b"
|
|
33
|
+
})).ok).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it("enforces a fixed-window rate limit", async () => {
|
|
36
|
+
const b = make({
|
|
37
|
+
name: "q",
|
|
38
|
+
rateLimit: {
|
|
39
|
+
limit: 2,
|
|
40
|
+
periodMs: 1e3
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
expect((await b.tryAdmit("q", {
|
|
44
|
+
waiterId: "1"
|
|
45
|
+
})).ok).toBe(true);
|
|
46
|
+
expect((await b.tryAdmit("q", {
|
|
47
|
+
waiterId: "2"
|
|
48
|
+
})).ok).toBe(true);
|
|
49
|
+
expect((await b.tryAdmit("q", {
|
|
50
|
+
waiterId: "3"
|
|
51
|
+
})).ok).toBe(false);
|
|
52
|
+
now += 1001;
|
|
53
|
+
expect((await b.tryAdmit("q", {
|
|
54
|
+
waiterId: "4"
|
|
55
|
+
})).ok).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it("admits the highest-priority waiter first when a slot frees", async () => {
|
|
58
|
+
const b = make({
|
|
59
|
+
name: "q",
|
|
60
|
+
concurrency: 1
|
|
61
|
+
});
|
|
62
|
+
expect((await b.tryAdmit("q", {
|
|
63
|
+
waiterId: "holder"
|
|
64
|
+
})).ok).toBe(true);
|
|
65
|
+
expect((await b.tryAdmit("q", {
|
|
66
|
+
waiterId: "low",
|
|
67
|
+
priority: 1
|
|
68
|
+
})).ok).toBe(false);
|
|
69
|
+
expect((await b.tryAdmit("q", {
|
|
70
|
+
waiterId: "high",
|
|
71
|
+
priority: 9
|
|
72
|
+
})).ok).toBe(false);
|
|
73
|
+
await b.release("q", "holder");
|
|
74
|
+
expect((await b.tryAdmit("q", {
|
|
75
|
+
waiterId: "low",
|
|
76
|
+
priority: 1
|
|
77
|
+
})).ok).toBe(false);
|
|
78
|
+
expect((await b.tryAdmit("q", {
|
|
79
|
+
waiterId: "high",
|
|
80
|
+
priority: 9
|
|
81
|
+
})).ok).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
it("orders plain-concurrency waiters by arrival (FIFO) \u2014 no priority/fairness configured", async () => {
|
|
84
|
+
const b = make({
|
|
85
|
+
name: "q",
|
|
86
|
+
concurrency: 1
|
|
87
|
+
});
|
|
88
|
+
expect((await b.tryAdmit("q", {
|
|
89
|
+
waiterId: "holder"
|
|
90
|
+
})).ok).toBe(true);
|
|
91
|
+
expect((await b.tryAdmit("q", {
|
|
92
|
+
waiterId: "first"
|
|
93
|
+
})).ok).toBe(false);
|
|
94
|
+
expect((await b.tryAdmit("q", {
|
|
95
|
+
waiterId: "second"
|
|
96
|
+
})).ok).toBe(false);
|
|
97
|
+
await b.release("q", "holder");
|
|
98
|
+
expect((await b.tryAdmit("q", {
|
|
99
|
+
waiterId: "second"
|
|
100
|
+
})).ok).toBe(false);
|
|
101
|
+
expect((await b.tryAdmit("q", {
|
|
102
|
+
waiterId: "first"
|
|
103
|
+
})).ok).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
it("admits the most-recent arrival first when order is LIFO", async () => {
|
|
106
|
+
const b = make({
|
|
107
|
+
name: "q",
|
|
108
|
+
concurrency: 1,
|
|
109
|
+
order: "lifo"
|
|
110
|
+
});
|
|
111
|
+
expect((await b.tryAdmit("q", {
|
|
112
|
+
waiterId: "holder"
|
|
113
|
+
})).ok).toBe(true);
|
|
114
|
+
expect((await b.tryAdmit("q", {
|
|
115
|
+
waiterId: "first"
|
|
116
|
+
})).ok).toBe(false);
|
|
117
|
+
expect((await b.tryAdmit("q", {
|
|
118
|
+
waiterId: "second"
|
|
119
|
+
})).ok).toBe(false);
|
|
120
|
+
await b.release("q", "holder");
|
|
121
|
+
expect((await b.tryAdmit("q", {
|
|
122
|
+
waiterId: "first"
|
|
123
|
+
})).ok).toBe(false);
|
|
124
|
+
expect((await b.tryAdmit("q", {
|
|
125
|
+
waiterId: "second"
|
|
126
|
+
})).ok).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
it("round-robins a contended slot across fairness keys", async () => {
|
|
129
|
+
const b = make({
|
|
130
|
+
name: "q",
|
|
131
|
+
concurrency: 1,
|
|
132
|
+
fairness: "key"
|
|
133
|
+
});
|
|
134
|
+
expect((await b.tryAdmit("q", {
|
|
135
|
+
waiterId: "a0",
|
|
136
|
+
key: "A"
|
|
137
|
+
})).ok).toBe(true);
|
|
138
|
+
expect((await b.tryAdmit("q", {
|
|
139
|
+
waiterId: "a1",
|
|
140
|
+
key: "A"
|
|
141
|
+
})).ok).toBe(false);
|
|
142
|
+
expect((await b.tryAdmit("q", {
|
|
143
|
+
waiterId: "b0",
|
|
144
|
+
key: "B"
|
|
145
|
+
})).ok).toBe(false);
|
|
146
|
+
await b.release("q", "a0");
|
|
147
|
+
expect((await b.tryAdmit("q", {
|
|
148
|
+
waiterId: "a1",
|
|
149
|
+
key: "A"
|
|
150
|
+
})).ok).toBe(false);
|
|
151
|
+
expect((await b.tryAdmit("q", {
|
|
152
|
+
waiterId: "b0",
|
|
153
|
+
key: "B"
|
|
154
|
+
})).ok).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
it("leaves an unregistered queue ungated", async () => {
|
|
157
|
+
const b = make({
|
|
158
|
+
name: "q",
|
|
159
|
+
concurrency: 1
|
|
160
|
+
});
|
|
161
|
+
expect((await b.tryAdmit("other", {
|
|
162
|
+
waiterId: "x"
|
|
163
|
+
})).ok).toBe(true);
|
|
164
|
+
expect((await b.tryAdmit("other", {
|
|
165
|
+
waiterId: "y"
|
|
166
|
+
})).ok).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
__name(runAdmissionBackendContract, "runAdmissionBackendContract");
|
|
171
|
+
|
|
8
172
|
// src/assertions.ts
|
|
9
173
|
async function requireRun(store, runId) {
|
|
10
174
|
const run2 = await store.getRun(runId);
|
|
@@ -200,7 +364,7 @@ __name(assertReplayable, "assertReplayable");
|
|
|
200
364
|
|
|
201
365
|
// src/state-store-conformance.ts
|
|
202
366
|
import { WorkflowEngine as WorkflowEngine3 } from "@dudousxd/nestjs-durable-core";
|
|
203
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
367
|
+
import { beforeEach, describe as describe2, expect as expect2, it as it2 } from "vitest";
|
|
204
368
|
var StateStoreUnavailableError = class extends Error {
|
|
205
369
|
static {
|
|
206
370
|
__name(this, "StateStoreUnavailableError");
|
|
@@ -245,7 +409,7 @@ var checkpoint = /* @__PURE__ */ __name((over = {}) => ({
|
|
|
245
409
|
...over
|
|
246
410
|
}), "checkpoint");
|
|
247
411
|
function runStateStoreContract(name, makeStore) {
|
|
248
|
-
|
|
412
|
+
describe2(`StateStore contract: ${name}`, () => {
|
|
249
413
|
let store;
|
|
250
414
|
let cleanup;
|
|
251
415
|
let supportsAsyncTransaction = true;
|
|
@@ -276,7 +440,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
276
440
|
};
|
|
277
441
|
});
|
|
278
442
|
const t = /* @__PURE__ */ __name((label, fn) => {
|
|
279
|
-
|
|
443
|
+
it2(label, async () => {
|
|
280
444
|
if (skipReason) return;
|
|
281
445
|
await fn();
|
|
282
446
|
});
|
|
@@ -284,13 +448,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
284
448
|
t("creates a run and reads it back with its JSON input", async () => {
|
|
285
449
|
await store.createRun(run());
|
|
286
450
|
const loaded = await store.getRun("r1");
|
|
287
|
-
|
|
288
|
-
|
|
451
|
+
expect2(loaded?.workflow).toBe("checkout");
|
|
452
|
+
expect2(loaded?.input).toEqual({
|
|
289
453
|
orderId: "o1"
|
|
290
454
|
});
|
|
291
455
|
});
|
|
292
456
|
t("returns null for a missing run", async () => {
|
|
293
|
-
|
|
457
|
+
expect2(await store.getRun("nope")).toBeNull();
|
|
294
458
|
});
|
|
295
459
|
t("updates a run (status/output) and round-trips recoveryAttempts + dead status", async () => {
|
|
296
460
|
await store.createRun(run({
|
|
@@ -304,18 +468,29 @@ function runStateStoreContract(name, makeStore) {
|
|
|
304
468
|
updatedAt: at
|
|
305
469
|
});
|
|
306
470
|
const done = await store.getRun("r1");
|
|
307
|
-
|
|
308
|
-
|
|
471
|
+
expect2(done?.status).toBe("completed");
|
|
472
|
+
expect2(done?.output).toEqual({
|
|
309
473
|
total: 42
|
|
310
474
|
});
|
|
311
|
-
|
|
475
|
+
expect2(done?.recoveryAttempts).toBe(3);
|
|
312
476
|
await store.updateRun("r1", {
|
|
313
477
|
status: "dead",
|
|
314
478
|
recoveryAttempts: 4
|
|
315
479
|
});
|
|
316
480
|
const dead = await store.getRun("r1");
|
|
317
|
-
|
|
318
|
-
|
|
481
|
+
expect2(dead?.status).toBe("dead");
|
|
482
|
+
expect2(dead?.recoveryAttempts).toBe(4);
|
|
483
|
+
});
|
|
484
|
+
t("round-trips a run dispatch priority", async () => {
|
|
485
|
+
await store.createRun(run({
|
|
486
|
+
id: "withprio",
|
|
487
|
+
priority: 7
|
|
488
|
+
}));
|
|
489
|
+
await store.createRun(run({
|
|
490
|
+
id: "noprio"
|
|
491
|
+
}));
|
|
492
|
+
expect2((await store.getRun("withprio"))?.priority).toBe(7);
|
|
493
|
+
expect2((await store.getRun("noprio"))?.priority).toBeUndefined();
|
|
319
494
|
});
|
|
320
495
|
t("deleteRun removes the run and all its rows (checkpoints, waiters, attributes)", async () => {
|
|
321
496
|
await store.createRun(run({
|
|
@@ -330,10 +505,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
330
505
|
seq: 0
|
|
331
506
|
});
|
|
332
507
|
await store.deleteRun("r1");
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
508
|
+
expect2(await store.getRun("r1")).toBeNull();
|
|
509
|
+
expect2(await store.listCheckpoints("r1")).toEqual([]);
|
|
510
|
+
expect2(await store.listSignalWaiters("approve-")).toEqual([]);
|
|
511
|
+
expect2(await store.listRuns({
|
|
337
512
|
attributes: [
|
|
338
513
|
{
|
|
339
514
|
key: "tier",
|
|
@@ -344,7 +519,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
344
519
|
})).toEqual([]);
|
|
345
520
|
});
|
|
346
521
|
t("deleteRun is a no-op for a missing run", async () => {
|
|
347
|
-
await
|
|
522
|
+
await expect2(store.deleteRun("nope")).resolves.toBeUndefined();
|
|
348
523
|
});
|
|
349
524
|
t("updateRun maps every patchable field \u2014 clears error and patches tags/lockedBy/input/timers", async () => {
|
|
350
525
|
await store.createRun(run({
|
|
@@ -376,21 +551,21 @@ function runStateStoreContract(name, makeStore) {
|
|
|
376
551
|
updatedAt: at
|
|
377
552
|
});
|
|
378
553
|
const r = await store.getRun("r1");
|
|
379
|
-
|
|
380
|
-
|
|
554
|
+
expect2(r?.status).toBe("completed");
|
|
555
|
+
expect2(r?.output).toEqual({
|
|
381
556
|
ok: true
|
|
382
557
|
});
|
|
383
|
-
|
|
384
|
-
|
|
558
|
+
expect2(r?.error).toBeUndefined();
|
|
559
|
+
expect2(r?.input).toEqual({
|
|
385
560
|
orderId: "o2"
|
|
386
561
|
});
|
|
387
|
-
|
|
562
|
+
expect2(r?.tags).toEqual([
|
|
388
563
|
"x",
|
|
389
564
|
"y"
|
|
390
565
|
]);
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
566
|
+
expect2(r?.lockedBy).toBe("owner-2");
|
|
567
|
+
expect2(r?.wakeAt).toBe(123456);
|
|
568
|
+
expect2(r?.lockedUntil).toBe(222222);
|
|
394
569
|
});
|
|
395
570
|
t("upserts checkpoints, reads them by (runId, seq), and lists them ordered by seq", async () => {
|
|
396
571
|
await store.createRun(run());
|
|
@@ -408,13 +583,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
408
583
|
again: true
|
|
409
584
|
}
|
|
410
585
|
}));
|
|
411
|
-
|
|
586
|
+
expect2((await store.getCheckpoint("r1", 0))?.output).toEqual({
|
|
412
587
|
ok: true,
|
|
413
588
|
again: true
|
|
414
589
|
});
|
|
415
|
-
|
|
590
|
+
expect2((await store.getCheckpoint("r1", 1))?.name).toBe("charge");
|
|
416
591
|
const list = await store.listCheckpoints("r1");
|
|
417
|
-
|
|
592
|
+
expect2(list.map((c) => c.seq)).toEqual([
|
|
418
593
|
0,
|
|
419
594
|
1
|
|
420
595
|
]);
|
|
@@ -450,14 +625,14 @@ function runStateStoreContract(name, makeStore) {
|
|
|
450
625
|
}
|
|
451
626
|
}));
|
|
452
627
|
const getLatestCheckpointByName = store.getLatestCheckpointByName;
|
|
453
|
-
|
|
628
|
+
expect2(getLatestCheckpointByName).toBeDefined();
|
|
454
629
|
if (!getLatestCheckpointByName) return;
|
|
455
630
|
const latest = await getLatestCheckpointByName.call(store, "r1", "event:progress");
|
|
456
|
-
|
|
457
|
-
|
|
631
|
+
expect2(latest?.seq).toBe(3);
|
|
632
|
+
expect2(latest?.output).toEqual({
|
|
458
633
|
pct: 90
|
|
459
634
|
});
|
|
460
|
-
|
|
635
|
+
expect2(await getLatestCheckpointByName.call(store, "r1", "event:missing")).toBeUndefined();
|
|
461
636
|
});
|
|
462
637
|
t("listCheckpointsByNamePrefix returns prefix matches ordered by seq (empty => none)", async () => {
|
|
463
638
|
await store.createRun(run());
|
|
@@ -484,18 +659,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
484
659
|
output: "child-b"
|
|
485
660
|
}));
|
|
486
661
|
const listCheckpointsByNamePrefix = store.listCheckpointsByNamePrefix;
|
|
487
|
-
|
|
662
|
+
expect2(listCheckpointsByNamePrefix).toBeDefined();
|
|
488
663
|
if (!listCheckpointsByNamePrefix) return;
|
|
489
664
|
const matches = await listCheckpointsByNamePrefix.call(store, "r1", [
|
|
490
665
|
"signal:child:",
|
|
491
666
|
"spawn:"
|
|
492
667
|
]);
|
|
493
|
-
|
|
668
|
+
expect2(matches.map((c) => c.name)).toEqual([
|
|
494
669
|
"spawn:0",
|
|
495
670
|
"signal:child:c2",
|
|
496
671
|
"spawn:1"
|
|
497
672
|
]);
|
|
498
|
-
|
|
673
|
+
expect2(await listCheckpointsByNamePrefix.call(store, "r1", [])).toEqual([]);
|
|
499
674
|
});
|
|
500
675
|
t("round-trips checkpoint events and reads back a missing checkpoint as null", async () => {
|
|
501
676
|
await store.createRun(run());
|
|
@@ -510,7 +685,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
510
685
|
}
|
|
511
686
|
]
|
|
512
687
|
}));
|
|
513
|
-
|
|
688
|
+
expect2((await store.getCheckpoint("r1", 0))?.events).toEqual([
|
|
514
689
|
{
|
|
515
690
|
at: 1,
|
|
516
691
|
level: "error",
|
|
@@ -519,7 +694,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
519
694
|
status: "failed"
|
|
520
695
|
}
|
|
521
696
|
]);
|
|
522
|
-
|
|
697
|
+
expect2(await store.getCheckpoint("r1", 99)).toBeNull();
|
|
523
698
|
});
|
|
524
699
|
t("lists incomplete runs, pending runs (FIFO), and due timers", async () => {
|
|
525
700
|
await store.createRun(run({
|
|
@@ -545,39 +720,39 @@ function runStateStoreContract(name, makeStore) {
|
|
|
545
720
|
status: "pending",
|
|
546
721
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:01.000Z")
|
|
547
722
|
}));
|
|
548
|
-
|
|
723
|
+
expect2((await store.listIncompleteRuns()).map((r) => r.id)).toEqual([
|
|
549
724
|
"running1"
|
|
550
725
|
]);
|
|
551
|
-
|
|
726
|
+
expect2((await store.listPendingRuns(10)).map((r) => r.id)).toEqual([
|
|
552
727
|
"pending1",
|
|
553
728
|
"pending2"
|
|
554
729
|
]);
|
|
555
|
-
|
|
730
|
+
expect2((await store.listPendingRuns(1)).map((r) => r.id)).toEqual([
|
|
556
731
|
"pending1"
|
|
557
732
|
]);
|
|
558
|
-
|
|
733
|
+
expect2((await store.listDueTimers(1e4)).map((r) => r.id)).toEqual([
|
|
559
734
|
"suspended1"
|
|
560
735
|
]);
|
|
561
|
-
|
|
736
|
+
expect2(await store.listDueTimers(1e3)).toHaveLength(0);
|
|
562
737
|
});
|
|
563
738
|
t("tryLockRun is atomic and respects lease expiry", async () => {
|
|
564
739
|
await store.createRun(run({
|
|
565
740
|
id: "r1"
|
|
566
741
|
}));
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
742
|
+
expect2(await store.tryLockRun("r1", "A", 2e3, 1e3)).toBe(true);
|
|
743
|
+
expect2(await store.tryLockRun("r1", "B", 3e3, 1500)).toBe(false);
|
|
744
|
+
expect2(await store.tryLockRun("r1", "B", 4e3, 2500)).toBe(true);
|
|
570
745
|
await store.releaseRunLock("r1");
|
|
571
|
-
|
|
746
|
+
expect2(await store.tryLockRun("r1", "C", 9e3, 2600)).toBe(true);
|
|
572
747
|
});
|
|
573
748
|
t("renewRunLock only succeeds for the current owner", async () => {
|
|
574
749
|
await store.createRun(run({
|
|
575
750
|
id: "r1"
|
|
576
751
|
}));
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
752
|
+
expect2(await store.tryLockRun("r1", "A", 2e3, 1e3)).toBe(true);
|
|
753
|
+
expect2(await store.renewRunLock("r1", "A", 5e3)).toBe(true);
|
|
754
|
+
expect2(await store.renewRunLock("r1", "B", 9e3)).toBe(false);
|
|
755
|
+
expect2(await store.tryLockRun("r1", "B", 6e3, 4e3)).toBe(false);
|
|
581
756
|
});
|
|
582
757
|
t("filters listRuns by workflow and by status", async () => {
|
|
583
758
|
await store.createRun(run({
|
|
@@ -595,19 +770,19 @@ function runStateStoreContract(name, makeStore) {
|
|
|
595
770
|
workflow: "checkout",
|
|
596
771
|
status: "completed"
|
|
597
772
|
}));
|
|
598
|
-
|
|
773
|
+
expect2((await store.listRuns({
|
|
599
774
|
workflow: "checkout"
|
|
600
775
|
})).map((r) => r.id).sort()).toEqual([
|
|
601
776
|
"a",
|
|
602
777
|
"c"
|
|
603
778
|
]);
|
|
604
|
-
|
|
779
|
+
expect2((await store.listRuns({
|
|
605
780
|
status: "running"
|
|
606
781
|
})).map((r) => r.id).sort()).toEqual([
|
|
607
782
|
"a",
|
|
608
783
|
"b"
|
|
609
784
|
]);
|
|
610
|
-
|
|
785
|
+
expect2((await store.listRuns({
|
|
611
786
|
workflow: "checkout",
|
|
612
787
|
status: "running"
|
|
613
788
|
})).map((r) => r.id)).toEqual([
|
|
@@ -631,7 +806,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
631
806
|
id: "d",
|
|
632
807
|
status: "pending"
|
|
633
808
|
}));
|
|
634
|
-
|
|
809
|
+
expect2((await store.listRuns({
|
|
635
810
|
statuses: [
|
|
636
811
|
"running",
|
|
637
812
|
"suspended"
|
|
@@ -640,7 +815,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
640
815
|
"a",
|
|
641
816
|
"b"
|
|
642
817
|
]);
|
|
643
|
-
|
|
818
|
+
expect2((await store.listRuns({
|
|
644
819
|
status: "running",
|
|
645
820
|
statuses: [
|
|
646
821
|
"running",
|
|
@@ -649,7 +824,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
649
824
|
})).map((r) => r.id)).toEqual([
|
|
650
825
|
"a"
|
|
651
826
|
]);
|
|
652
|
-
|
|
827
|
+
expect2(await store.listRuns({
|
|
653
828
|
statuses: []
|
|
654
829
|
})).toHaveLength(0);
|
|
655
830
|
});
|
|
@@ -671,21 +846,21 @@ function runStateStoreContract(name, makeStore) {
|
|
|
671
846
|
await store.createRun(run({
|
|
672
847
|
id: "c"
|
|
673
848
|
}));
|
|
674
|
-
|
|
849
|
+
expect2((await store.getRun("a"))?.tags).toEqual([
|
|
675
850
|
"etl",
|
|
676
851
|
"critical"
|
|
677
852
|
]);
|
|
678
|
-
|
|
853
|
+
expect2((await store.listRuns({
|
|
679
854
|
tag: "etl"
|
|
680
855
|
})).map((r) => r.id)).toEqual([
|
|
681
856
|
"a"
|
|
682
857
|
]);
|
|
683
|
-
|
|
858
|
+
expect2((await store.listRuns({
|
|
684
859
|
tag: "etl-foo"
|
|
685
860
|
})).map((r) => r.id)).toEqual([
|
|
686
861
|
"b"
|
|
687
862
|
]);
|
|
688
|
-
|
|
863
|
+
expect2(await store.listRuns({
|
|
689
864
|
tag: "nope"
|
|
690
865
|
})).toHaveLength(0);
|
|
691
866
|
});
|
|
@@ -702,18 +877,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
702
877
|
id: "new",
|
|
703
878
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:02.000Z")
|
|
704
879
|
}));
|
|
705
|
-
|
|
880
|
+
expect2((await store.listRuns({})).map((r) => r.id)).toEqual([
|
|
706
881
|
"new",
|
|
707
882
|
"mid",
|
|
708
883
|
"old"
|
|
709
884
|
]);
|
|
710
|
-
|
|
885
|
+
expect2((await store.listRuns({
|
|
711
886
|
limit: 2
|
|
712
887
|
})).map((r) => r.id)).toEqual([
|
|
713
888
|
"new",
|
|
714
889
|
"mid"
|
|
715
890
|
]);
|
|
716
|
-
|
|
891
|
+
expect2((await store.listRuns({
|
|
717
892
|
limit: 2,
|
|
718
893
|
offset: 1
|
|
719
894
|
})).map((r) => r.id)).toEqual([
|
|
@@ -743,11 +918,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
743
918
|
tier: "pro"
|
|
744
919
|
}
|
|
745
920
|
}));
|
|
746
|
-
|
|
921
|
+
expect2((await store.getRun("b"))?.searchAttributes).toEqual({
|
|
747
922
|
amount: 200,
|
|
748
923
|
tier: "pro"
|
|
749
924
|
});
|
|
750
|
-
|
|
925
|
+
expect2((await store.listRuns({
|
|
751
926
|
attributes: [
|
|
752
927
|
{
|
|
753
928
|
key: "amount",
|
|
@@ -759,7 +934,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
759
934
|
"b",
|
|
760
935
|
"c"
|
|
761
936
|
]);
|
|
762
|
-
|
|
937
|
+
expect2((await store.listRuns({
|
|
763
938
|
attributes: [
|
|
764
939
|
{
|
|
765
940
|
key: "tier",
|
|
@@ -775,7 +950,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
775
950
|
})).map((r) => r.id)).toEqual([
|
|
776
951
|
"b"
|
|
777
952
|
]);
|
|
778
|
-
|
|
953
|
+
expect2((await store.listRuns({
|
|
779
954
|
attributes: [
|
|
780
955
|
{
|
|
781
956
|
key: "tier",
|
|
@@ -807,7 +982,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
807
982
|
other: 1
|
|
808
983
|
}
|
|
809
984
|
}));
|
|
810
|
-
|
|
985
|
+
expect2((await store.listRuns({
|
|
811
986
|
attributes: [
|
|
812
987
|
{
|
|
813
988
|
key: "vip",
|
|
@@ -818,7 +993,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
818
993
|
})).map((r) => r.id)).toEqual([
|
|
819
994
|
"a"
|
|
820
995
|
]);
|
|
821
|
-
|
|
996
|
+
expect2((await store.listRuns({
|
|
822
997
|
attributes: [
|
|
823
998
|
{
|
|
824
999
|
key: "vip",
|
|
@@ -845,7 +1020,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
845
1020
|
"lt",
|
|
846
1021
|
"lte"
|
|
847
1022
|
]) {
|
|
848
|
-
|
|
1023
|
+
expect2(await store.listRuns({
|
|
849
1024
|
attributes: [
|
|
850
1025
|
{
|
|
851
1026
|
key: "missing",
|
|
@@ -864,7 +1039,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
864
1039
|
amount: 10
|
|
865
1040
|
}
|
|
866
1041
|
}));
|
|
867
|
-
|
|
1042
|
+
expect2((await store.listRuns({
|
|
868
1043
|
attributes: [
|
|
869
1044
|
{
|
|
870
1045
|
key: "tier",
|
|
@@ -880,7 +1055,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
880
1055
|
tier: "pro"
|
|
881
1056
|
}
|
|
882
1057
|
});
|
|
883
|
-
|
|
1058
|
+
expect2((await store.listRuns({
|
|
884
1059
|
attributes: [
|
|
885
1060
|
{
|
|
886
1061
|
key: "tier",
|
|
@@ -891,7 +1066,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
891
1066
|
})).map((r) => r.id)).toEqual([
|
|
892
1067
|
"a"
|
|
893
1068
|
]);
|
|
894
|
-
|
|
1069
|
+
expect2(await store.listRuns({
|
|
895
1070
|
attributes: [
|
|
896
1071
|
{
|
|
897
1072
|
key: "tier",
|
|
@@ -900,7 +1075,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
900
1075
|
}
|
|
901
1076
|
]
|
|
902
1077
|
})).toHaveLength(0);
|
|
903
|
-
|
|
1078
|
+
expect2(await store.listRuns({
|
|
904
1079
|
attributes: [
|
|
905
1080
|
{
|
|
906
1081
|
key: "amount",
|
|
@@ -943,7 +1118,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
943
1118
|
],
|
|
944
1119
|
limit: 10
|
|
945
1120
|
});
|
|
946
|
-
|
|
1121
|
+
expect2(res.map((r) => r.id)).toEqual([
|
|
947
1122
|
"a"
|
|
948
1123
|
]);
|
|
949
1124
|
});
|
|
@@ -963,12 +1138,30 @@ function runStateStoreContract(name, makeStore) {
|
|
|
963
1138
|
runId: "r3",
|
|
964
1139
|
seq: 5
|
|
965
1140
|
});
|
|
966
|
-
|
|
1141
|
+
expect2((await store.listSignalWaiters("approve-")).map((w) => w.token).sort()).toEqual([
|
|
967
1142
|
"approve-1",
|
|
968
1143
|
"approve-2"
|
|
969
1144
|
]);
|
|
970
|
-
|
|
971
|
-
|
|
1145
|
+
expect2((await store.takeSignalWaiter("approve-1"))?.seq).toBe(3);
|
|
1146
|
+
expect2(await store.takeSignalWaiter("approve-1")).toBeNull();
|
|
1147
|
+
});
|
|
1148
|
+
t("round-trips a signal waiter parallelGroup (child-fan tag) through put \u2192 take \u2192 list", async () => {
|
|
1149
|
+
await store.putSignalWaiter({
|
|
1150
|
+
token: "child:r.child.0",
|
|
1151
|
+
runId: "r",
|
|
1152
|
+
seq: 0,
|
|
1153
|
+
parallelGroup: "gather:0"
|
|
1154
|
+
});
|
|
1155
|
+
await store.putSignalWaiter({
|
|
1156
|
+
token: "child:r.child.9",
|
|
1157
|
+
runId: "r",
|
|
1158
|
+
seq: 9
|
|
1159
|
+
});
|
|
1160
|
+
const listed = await store.listSignalWaiters("child:");
|
|
1161
|
+
expect2(listed.find((w) => w.token === "child:r.child.0")?.parallelGroup).toBe("gather:0");
|
|
1162
|
+
expect2(listed.find((w) => w.token === "child:r.child.9")?.parallelGroup).toBeUndefined();
|
|
1163
|
+
expect2((await store.takeSignalWaiter("child:r.child.0"))?.parallelGroup).toBe("gather:0");
|
|
1164
|
+
expect2((await store.takeSignalWaiter("child:r.child.9"))?.parallelGroup).toBeUndefined();
|
|
972
1165
|
});
|
|
973
1166
|
t("buffers signals and takes them FIFO per token", async () => {
|
|
974
1167
|
await store.bufferSignal("sig", {
|
|
@@ -980,18 +1173,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
980
1173
|
await store.bufferSignal("other", {
|
|
981
1174
|
n: 9
|
|
982
1175
|
});
|
|
983
|
-
|
|
1176
|
+
expect2(await store.takeBufferedSignal("sig")).toEqual({
|
|
984
1177
|
payload: {
|
|
985
1178
|
n: 1
|
|
986
1179
|
}
|
|
987
1180
|
});
|
|
988
|
-
|
|
1181
|
+
expect2(await store.takeBufferedSignal("sig")).toEqual({
|
|
989
1182
|
payload: {
|
|
990
1183
|
n: 2
|
|
991
1184
|
}
|
|
992
1185
|
});
|
|
993
|
-
|
|
994
|
-
|
|
1186
|
+
expect2(await store.takeBufferedSignal("sig")).toBeNull();
|
|
1187
|
+
expect2(await store.takeBufferedSignal("other")).toEqual({
|
|
995
1188
|
payload: {
|
|
996
1189
|
n: 9
|
|
997
1190
|
}
|
|
@@ -1011,10 +1204,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1011
1204
|
}));
|
|
1012
1205
|
return "ok";
|
|
1013
1206
|
});
|
|
1014
|
-
|
|
1207
|
+
expect2(result).toBe("ok");
|
|
1015
1208
|
const cp = await store.getCheckpoint("r1", 7);
|
|
1016
|
-
|
|
1017
|
-
|
|
1209
|
+
expect2(cp?.name).toBe("tx-step");
|
|
1210
|
+
expect2(cp?.output).toEqual({
|
|
1018
1211
|
paid: true
|
|
1019
1212
|
});
|
|
1020
1213
|
});
|
|
@@ -1041,11 +1234,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1041
1234
|
await engine.start("wf", {
|
|
1042
1235
|
x: 1
|
|
1043
1236
|
}, "run1");
|
|
1044
|
-
|
|
1237
|
+
expect2((await engine.waitForRun("run1")).status).toBe("failed");
|
|
1045
1238
|
const resumed = await engine.resume("run1");
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1239
|
+
expect2(resumed.status).toBe("completed");
|
|
1240
|
+
expect2(resumed.output).toBe(15);
|
|
1241
|
+
expect2(aRuns).toBe(1);
|
|
1049
1242
|
});
|
|
1050
1243
|
});
|
|
1051
1244
|
}
|
|
@@ -5590,6 +5783,7 @@ export {
|
|
|
5590
5783
|
failTimes,
|
|
5591
5784
|
preserveColumnExpectation,
|
|
5592
5785
|
recordedSteps,
|
|
5786
|
+
runAdmissionBackendContract,
|
|
5593
5787
|
runStateStoreContract
|
|
5594
5788
|
};
|
|
5595
5789
|
//# sourceMappingURL=index.js.map
|