@dudousxd/nestjs-durable-testing 0.17.0 → 0.18.0
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 +16 -1
- package/dist/index.js +862 -327
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -187,7 +187,7 @@ async function assertOutput(store, runId, expected) {
|
|
|
187
187
|
}
|
|
188
188
|
__name(assertOutput, "assertOutput");
|
|
189
189
|
async function recordedSteps(store, runId) {
|
|
190
|
-
return (await store.listCheckpoints(runId)).map((
|
|
190
|
+
return (await store.listCheckpoints(runId)).map((cp2) => cp2.name);
|
|
191
191
|
}
|
|
192
192
|
__name(recordedSteps, "recordedSteps");
|
|
193
193
|
async function assertStepsRan(store, runId, names) {
|
|
@@ -200,10 +200,10 @@ async function assertStepsRan(store, runId, names) {
|
|
|
200
200
|
}
|
|
201
201
|
__name(assertStepsRan, "assertStepsRan");
|
|
202
202
|
async function assertStepAttempts(store, runId, name, attempts) {
|
|
203
|
-
const
|
|
204
|
-
if (!
|
|
205
|
-
if (
|
|
206
|
-
throw new Error(`expected step "${name}" to take ${attempts} attempt(s), took ${
|
|
203
|
+
const cp2 = (await store.listCheckpoints(runId)).find((c) => c.name === name);
|
|
204
|
+
if (!cp2) throw new Error(`expected step "${name}" to have run, but it did not`);
|
|
205
|
+
if (cp2.attempts !== attempts) {
|
|
206
|
+
throw new Error(`expected step "${name}" to take ${attempts} attempt(s), took ${cp2.attempts}`);
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
__name(assertStepAttempts, "assertStepAttempts");
|
|
@@ -341,11 +341,11 @@ var MutableClock = class {
|
|
|
341
341
|
this.current = current;
|
|
342
342
|
}
|
|
343
343
|
now = /* @__PURE__ */ __name(() => this.current, "now");
|
|
344
|
-
set(
|
|
345
|
-
this.current =
|
|
344
|
+
set(ms2) {
|
|
345
|
+
this.current = ms2;
|
|
346
346
|
}
|
|
347
|
-
advance(
|
|
348
|
-
this.current +=
|
|
347
|
+
advance(ms2) {
|
|
348
|
+
this.current += ms2;
|
|
349
349
|
}
|
|
350
350
|
};
|
|
351
351
|
function createTestEngine() {
|
|
@@ -362,8 +362,8 @@ function createTestEngine() {
|
|
|
362
362
|
store,
|
|
363
363
|
transport,
|
|
364
364
|
clock,
|
|
365
|
-
async tick(
|
|
366
|
-
clock.advance(
|
|
365
|
+
async tick(ms2) {
|
|
366
|
+
clock.advance(ms2);
|
|
367
367
|
return engine.resumeDueTimers(clock.now());
|
|
368
368
|
},
|
|
369
369
|
async run(workflow, input, runId, opts) {
|
|
@@ -384,7 +384,7 @@ async function assertReplayable(register, history) {
|
|
|
384
384
|
lockedBy: void 0,
|
|
385
385
|
lockedUntil: void 0
|
|
386
386
|
});
|
|
387
|
-
for (const
|
|
387
|
+
for (const cp2 of history.checkpoints) await store.saveCheckpoint(cp2);
|
|
388
388
|
const engine = new WorkflowEngine2({
|
|
389
389
|
store
|
|
390
390
|
});
|
|
@@ -400,7 +400,352 @@ __name(assertReplayable, "assertReplayable");
|
|
|
400
400
|
|
|
401
401
|
// src/state-store-conformance.ts
|
|
402
402
|
import { SINGLETON_ADMITTED_TAG, WorkflowEngine as WorkflowEngine3 } from "@dudousxd/nestjs-durable-core";
|
|
403
|
-
import { beforeEach, describe as describe2, expect as
|
|
403
|
+
import { beforeEach, describe as describe2, expect as expect3, it as it2 } from "vitest";
|
|
404
|
+
|
|
405
|
+
// src/checkpoint-aggregation-contract.ts
|
|
406
|
+
import { SUMMARY_CHILD_LIMIT, aggregatesCheckpoints, checkpointStatsOf, childIdsOf, pageFan, pageFanFromStore, queryCheckpointsOf, summarizeRunFromStore, summarizeTimeline } from "@dudousxd/nestjs-durable-core";
|
|
407
|
+
import { expect as expect2 } from "vitest";
|
|
408
|
+
var base = Date.parse("2026-06-11T00:00:00.000Z");
|
|
409
|
+
var ms = /* @__PURE__ */ __name((n) => new Date(base + n), "ms");
|
|
410
|
+
function cp(seq, over = {}) {
|
|
411
|
+
return {
|
|
412
|
+
runId: "r1",
|
|
413
|
+
seq,
|
|
414
|
+
name: "Lines.process",
|
|
415
|
+
kind: "remote",
|
|
416
|
+
stepId: `r1:${seq}`,
|
|
417
|
+
status: "completed",
|
|
418
|
+
output: {
|
|
419
|
+
n: seq
|
|
420
|
+
},
|
|
421
|
+
attempts: 1,
|
|
422
|
+
enqueuedAt: ms(seq * 10),
|
|
423
|
+
startedAt: ms(seq * 10 + 3),
|
|
424
|
+
finishedAt: ms(seq * 10 + 7),
|
|
425
|
+
...over
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
__name(cp, "cp");
|
|
429
|
+
var child = /* @__PURE__ */ __name((seq, group, over = {}) => cp(seq, {
|
|
430
|
+
name: `signal:child:r1.all.${group.split(":")[1]}.${seq}`,
|
|
431
|
+
kind: "signal",
|
|
432
|
+
parallelGroup: group,
|
|
433
|
+
output: {
|
|
434
|
+
ok: true,
|
|
435
|
+
value: seq
|
|
436
|
+
},
|
|
437
|
+
...over
|
|
438
|
+
}), "child");
|
|
439
|
+
var folded = /* @__PURE__ */ __name((seq, group, wave, from, n) => cp(seq, {
|
|
440
|
+
name: `fan:r1.all.${group.split(":")[1]}.w${wave}.`,
|
|
441
|
+
kind: "signal",
|
|
442
|
+
parallelGroup: group,
|
|
443
|
+
output: {
|
|
444
|
+
from,
|
|
445
|
+
values: Array.from({
|
|
446
|
+
length: n
|
|
447
|
+
}, (_, i) => i)
|
|
448
|
+
}
|
|
449
|
+
}), "folded");
|
|
450
|
+
function aggregationFixture() {
|
|
451
|
+
const rows = [
|
|
452
|
+
cp(0, {
|
|
453
|
+
name: "Lines.load",
|
|
454
|
+
kind: "local"
|
|
455
|
+
})
|
|
456
|
+
];
|
|
457
|
+
rows.push(cp(1, {
|
|
458
|
+
name: "spawn:audit-1",
|
|
459
|
+
kind: "local",
|
|
460
|
+
output: "audit-1"
|
|
461
|
+
}));
|
|
462
|
+
for (let i = 0; i < 120; i++) {
|
|
463
|
+
const seq = 2 + i;
|
|
464
|
+
rows.push(cp(seq, {
|
|
465
|
+
parallelGroup: "steps:2:120",
|
|
466
|
+
...i % 4 === 0 && i < 100 ? {
|
|
467
|
+
status: "failed",
|
|
468
|
+
attempts: 3,
|
|
469
|
+
error: {
|
|
470
|
+
message: `bad ${i}`
|
|
471
|
+
}
|
|
472
|
+
} : i >= 100 && i < 120 && i !== 105 && i !== 106 ? {
|
|
473
|
+
status: "pending",
|
|
474
|
+
output: void 0
|
|
475
|
+
} : i === 105 || i === 106 ? {
|
|
476
|
+
status: "running",
|
|
477
|
+
output: void 0
|
|
478
|
+
} : {}
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
for (let i = 0; i < 5; i++) rows.push(cp(122 + i, {
|
|
482
|
+
parallelGroup: "steps:122:5"
|
|
483
|
+
}));
|
|
484
|
+
for (let i = 0; i < 30; i++) {
|
|
485
|
+
rows.push(cp(127 + i, {
|
|
486
|
+
parallelGroup: "steps:127:40000",
|
|
487
|
+
name: "Rows.score",
|
|
488
|
+
...i >= 12 ? {
|
|
489
|
+
status: "pending",
|
|
490
|
+
output: void 0
|
|
491
|
+
} : {}
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
for (let i = 0; i < 30; i++) {
|
|
495
|
+
rows.push(cp(157 + i, {
|
|
496
|
+
parallelGroup: "steps:157:60:failFast",
|
|
497
|
+
name: i % 2 === 0 ? "Check.a" : "Check.b",
|
|
498
|
+
...i === 7 ? {
|
|
499
|
+
status: "failed",
|
|
500
|
+
error: {
|
|
501
|
+
message: "stop"
|
|
502
|
+
}
|
|
503
|
+
} : {}
|
|
504
|
+
}));
|
|
505
|
+
}
|
|
506
|
+
for (let i = 0; i < 60; i++) {
|
|
507
|
+
const seq = 187 + i;
|
|
508
|
+
rows.push(child(seq, "all:187:n80", {
|
|
509
|
+
...i >= 50 ? {
|
|
510
|
+
status: "running",
|
|
511
|
+
output: void 0
|
|
512
|
+
} : i % 3 === 0 ? {
|
|
513
|
+
output: {
|
|
514
|
+
ok: false,
|
|
515
|
+
error: `child ${i}`
|
|
516
|
+
}
|
|
517
|
+
} : i % 7 === 1 ? {
|
|
518
|
+
status: "failed",
|
|
519
|
+
error: {
|
|
520
|
+
message: "lost"
|
|
521
|
+
}
|
|
522
|
+
} : {}
|
|
523
|
+
}));
|
|
524
|
+
}
|
|
525
|
+
rows.push(folded(247, "all:187:n80", 9, 60, 20));
|
|
526
|
+
rows.push(folded(300, "all:300:n3000:failFast", 0, 0, 1e3));
|
|
527
|
+
rows.push(folded(301, "all:300:n3000:failFast", 1, 1e3, 1e3));
|
|
528
|
+
rows.push(folded(302, "all:300:n3000:failFast", 2, 2e3, 0));
|
|
529
|
+
for (let i = 0; i < 4; i++) {
|
|
530
|
+
rows.push(child(303 + i, "all:300:n3000:failFast", {
|
|
531
|
+
status: "running",
|
|
532
|
+
output: void 0
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
rows.push(child(400, "all:400:n3"));
|
|
536
|
+
rows.push(child(401, "all:400:n3", {
|
|
537
|
+
output: {
|
|
538
|
+
ok: false,
|
|
539
|
+
error: "narrow"
|
|
540
|
+
}
|
|
541
|
+
}));
|
|
542
|
+
rows.push(child(402, "all:400:n3", {
|
|
543
|
+
status: "running",
|
|
544
|
+
output: void 0
|
|
545
|
+
}));
|
|
546
|
+
for (let i = 0; i < 55; i++) {
|
|
547
|
+
rows.push(child(450 + i, "all:450", i === 54 ? {
|
|
548
|
+
output: {
|
|
549
|
+
ok: false
|
|
550
|
+
}
|
|
551
|
+
} : {}));
|
|
552
|
+
}
|
|
553
|
+
rows.push(cp(600, {
|
|
554
|
+
name: "Lines.report",
|
|
555
|
+
kind: "local",
|
|
556
|
+
status: "failed"
|
|
557
|
+
}));
|
|
558
|
+
return rows;
|
|
559
|
+
}
|
|
560
|
+
__name(aggregationFixture, "aggregationFixture");
|
|
561
|
+
var GROUPS = [
|
|
562
|
+
"steps:2:120",
|
|
563
|
+
"steps:122:5",
|
|
564
|
+
"steps:127:40000",
|
|
565
|
+
"steps:157:60:failFast",
|
|
566
|
+
"all:187:n80",
|
|
567
|
+
"all:300:n3000:failFast",
|
|
568
|
+
"all:400:n3",
|
|
569
|
+
"all:450",
|
|
570
|
+
"absent"
|
|
571
|
+
];
|
|
572
|
+
var QUERIES = [
|
|
573
|
+
{},
|
|
574
|
+
{
|
|
575
|
+
offset: 10,
|
|
576
|
+
limit: 7
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
offset: 0,
|
|
580
|
+
limit: 500
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
offset: 118,
|
|
584
|
+
limit: 50
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
offset: 5e3
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
failedOnly: true
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
failedOnly: true,
|
|
594
|
+
offset: 3,
|
|
595
|
+
limit: 4
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
failedOnly: true,
|
|
599
|
+
offset: 15,
|
|
600
|
+
limit: 10
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
failedOnly: true,
|
|
604
|
+
offset: 1e3
|
|
605
|
+
}
|
|
606
|
+
];
|
|
607
|
+
function comparable(stats) {
|
|
608
|
+
return stats.map(({ minName, maxName, ...rest }) => ({
|
|
609
|
+
...rest,
|
|
610
|
+
uniform: minName === maxName
|
|
611
|
+
})).sort((a, b) => `${a.group}|${a.status}|${a.nameKind}`.localeCompare(`${b.group}|${b.status}|${b.nameKind}`));
|
|
612
|
+
}
|
|
613
|
+
__name(comparable, "comparable");
|
|
614
|
+
async function seed(store, run2) {
|
|
615
|
+
await store.createRun(run2());
|
|
616
|
+
for (const row of aggregationFixture()) await store.saveCheckpoint(row);
|
|
617
|
+
await store.createRun(run2({
|
|
618
|
+
id: "r2"
|
|
619
|
+
}));
|
|
620
|
+
for (let i = 0; i < 3; i++) {
|
|
621
|
+
await store.saveCheckpoint(cp(2 + i, {
|
|
622
|
+
runId: "r2",
|
|
623
|
+
stepId: `r2:${i}`,
|
|
624
|
+
parallelGroup: "steps:2:120",
|
|
625
|
+
status: "failed"
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
__name(seed, "seed");
|
|
630
|
+
function registerCheckpointAggregationCases(t, getStore, run2) {
|
|
631
|
+
t("checkpointStats and queryCheckpoints agree with the in-memory reference", async () => {
|
|
632
|
+
const store = getStore();
|
|
633
|
+
expect2(store.checkpointStats).toBeDefined();
|
|
634
|
+
expect2(store.queryCheckpoints).toBeDefined();
|
|
635
|
+
if (!aggregatesCheckpoints(store)) return;
|
|
636
|
+
await seed(store, run2);
|
|
637
|
+
const full = await store.listCheckpoints("r1");
|
|
638
|
+
expect2(comparable(await store.checkpointStats("r1"))).toEqual(comparable(checkpointStatsOf(full)));
|
|
639
|
+
for (const group of GROUPS) {
|
|
640
|
+
expect2(comparable(await store.checkpointStats("r1", {
|
|
641
|
+
group
|
|
642
|
+
}))).toEqual(comparable(checkpointStatsOf(full, {
|
|
643
|
+
group
|
|
644
|
+
})));
|
|
645
|
+
}
|
|
646
|
+
expect2(await store.checkpointStats("nope")).toEqual([]);
|
|
647
|
+
const queries = [
|
|
648
|
+
{},
|
|
649
|
+
{
|
|
650
|
+
group: "steps:2:120"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
group: "steps:2:120",
|
|
654
|
+
statuses: [
|
|
655
|
+
"failed"
|
|
656
|
+
],
|
|
657
|
+
limit: 20
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
group: "steps:2:120",
|
|
661
|
+
offset: 30,
|
|
662
|
+
limit: 5
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
group: "all:187:n80",
|
|
666
|
+
statuses: [
|
|
667
|
+
"completed"
|
|
668
|
+
],
|
|
669
|
+
namePrefixes: [
|
|
670
|
+
"signal:child:"
|
|
671
|
+
]
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
excludeGroups: [
|
|
675
|
+
"steps:2:120",
|
|
676
|
+
"all:187:n80",
|
|
677
|
+
"all:450"
|
|
678
|
+
]
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
excludeGroups: []
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
namePrefixes: [
|
|
685
|
+
"signal:child:",
|
|
686
|
+
"fan:",
|
|
687
|
+
"spawn:"
|
|
688
|
+
],
|
|
689
|
+
offset: 200,
|
|
690
|
+
limit: 200
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
namePrefixes: [
|
|
694
|
+
"fan:"
|
|
695
|
+
]
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
statuses: []
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
namePrefixes: []
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
statuses: [
|
|
705
|
+
"pending",
|
|
706
|
+
"running"
|
|
707
|
+
],
|
|
708
|
+
offset: 3
|
|
709
|
+
}
|
|
710
|
+
];
|
|
711
|
+
for (const query of queries) {
|
|
712
|
+
expect2(await store.queryCheckpoints("r1", query)).toEqual(queryCheckpointsOf(full, query));
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
t("a run detail read through the aggregates is the one summarizeTimeline builds", async () => {
|
|
716
|
+
const store = getStore();
|
|
717
|
+
if (!aggregatesCheckpoints(store)) return;
|
|
718
|
+
await seed(store, run2);
|
|
719
|
+
const full = await store.listCheckpoints("r1");
|
|
720
|
+
for (const over of [
|
|
721
|
+
50,
|
|
722
|
+
0,
|
|
723
|
+
1e3
|
|
724
|
+
]) {
|
|
725
|
+
const ids = childIdsOf(full);
|
|
726
|
+
const expected = {
|
|
727
|
+
...summarizeTimeline(full, over),
|
|
728
|
+
children: ids.slice(0, SUMMARY_CHILD_LIMIT),
|
|
729
|
+
childCount: ids.length
|
|
730
|
+
};
|
|
731
|
+
expect2(await summarizeRunFromStore(store, "r1", over)).toEqual(expected);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
t("a fan page read through the aggregates is the one pageFan builds", async () => {
|
|
735
|
+
const store = getStore();
|
|
736
|
+
if (!aggregatesCheckpoints(store)) return;
|
|
737
|
+
await seed(store, run2);
|
|
738
|
+
const full = await store.listCheckpoints("r1");
|
|
739
|
+
for (const group of GROUPS) {
|
|
740
|
+
for (const query of QUERIES) {
|
|
741
|
+
expect2(await pageFanFromStore(store, "r1", group, query)).toEqual(pageFan(full, group, query));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
__name(registerCheckpointAggregationCases, "registerCheckpointAggregationCases");
|
|
747
|
+
|
|
748
|
+
// src/state-store-conformance.ts
|
|
404
749
|
var StateStoreUnavailableError = class extends Error {
|
|
405
750
|
static {
|
|
406
751
|
__name(this, "StateStoreUnavailableError");
|
|
@@ -486,7 +831,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
486
831
|
});
|
|
487
832
|
}, "t");
|
|
488
833
|
t("atomically admits holders without displacement by later same-millisecond IDs", async () => {
|
|
489
|
-
|
|
834
|
+
expect3(store.tryAdmitSingleton).toBeTypeOf("function");
|
|
490
835
|
const claim = /* @__PURE__ */ __name((id, limit = 1) => store.tryAdmitSingleton?.(id, "singleton:k", "checkout", limit), "claim");
|
|
491
836
|
await store.createRun(run({
|
|
492
837
|
id: "z-holder",
|
|
@@ -495,12 +840,12 @@ function runStateStoreContract(name, makeStore) {
|
|
|
495
840
|
"user-tag"
|
|
496
841
|
]
|
|
497
842
|
}));
|
|
498
|
-
|
|
843
|
+
expect3(await claim("z-holder")).toBe(true);
|
|
499
844
|
await store.updateRun("z-holder", {
|
|
500
845
|
status: "suspended",
|
|
501
846
|
wakeAt: 1e4
|
|
502
847
|
});
|
|
503
|
-
|
|
848
|
+
expect3(await claim("z-holder")).toBe(true);
|
|
504
849
|
await store.updateRun("z-holder", {
|
|
505
850
|
status: "blocked",
|
|
506
851
|
lockedBy: "expired",
|
|
@@ -512,25 +857,25 @@ function runStateStoreContract(name, makeStore) {
|
|
|
512
857
|
"singleton:k"
|
|
513
858
|
]
|
|
514
859
|
}));
|
|
515
|
-
|
|
516
|
-
|
|
860
|
+
expect3(await claim("a-waiter")).toBe(false);
|
|
861
|
+
expect3(await claim("z-holder")).toBe(true);
|
|
517
862
|
await store.updateRun("z-holder", {
|
|
518
863
|
status: "cancelling"
|
|
519
864
|
});
|
|
520
|
-
|
|
865
|
+
expect3(await claim("a-waiter")).toBe(false);
|
|
521
866
|
await store.updateRun("z-holder", {
|
|
522
867
|
status: "completed"
|
|
523
868
|
});
|
|
524
|
-
|
|
869
|
+
expect3((await store.getRun("z-holder"))?.tags).toEqual([
|
|
525
870
|
"singleton:k",
|
|
526
871
|
"user-tag"
|
|
527
872
|
]);
|
|
528
|
-
|
|
873
|
+
expect3(await claim("a-waiter")).toBe(true);
|
|
529
874
|
await store.updateRun("z-holder", {
|
|
530
875
|
status: "running"
|
|
531
876
|
});
|
|
532
|
-
|
|
533
|
-
|
|
877
|
+
expect3(await claim("z-holder")).toBe(false);
|
|
878
|
+
expect3(await claim("missing")).toBe(false);
|
|
534
879
|
});
|
|
535
880
|
t("singleton denial atomically parks waiters and preserves cancelling/terminal state", async () => {
|
|
536
881
|
await store.createRun(run({
|
|
@@ -539,15 +884,15 @@ function runStateStoreContract(name, makeStore) {
|
|
|
539
884
|
"singleton:k"
|
|
540
885
|
]
|
|
541
886
|
}));
|
|
542
|
-
|
|
887
|
+
expect3(await store.tryAdmitSingleton?.("holder", "singleton:k", "checkout", 1)).toBe(true);
|
|
543
888
|
await store.createRun(run({
|
|
544
889
|
id: "waiter",
|
|
545
890
|
tags: [
|
|
546
891
|
"singleton:k"
|
|
547
892
|
]
|
|
548
893
|
}));
|
|
549
|
-
|
|
550
|
-
|
|
894
|
+
expect3(await peerStore.tryAdmitSingleton?.("waiter", "singleton:k", "checkout", 1, 12e3)).toBe(false);
|
|
895
|
+
expect3(await store.getRun("waiter")).toMatchObject({
|
|
551
896
|
status: "suspended",
|
|
552
897
|
wakeAt: 12e3
|
|
553
898
|
});
|
|
@@ -555,16 +900,16 @@ function runStateStoreContract(name, makeStore) {
|
|
|
555
900
|
status: "cancelling",
|
|
556
901
|
wakeAt: void 0
|
|
557
902
|
});
|
|
558
|
-
|
|
559
|
-
|
|
903
|
+
expect3(await peerStore.tryAdmitSingleton?.("waiter", "singleton:k", "checkout", 1, 14e3)).toBe(false);
|
|
904
|
+
expect3(await store.getRun("waiter")).toMatchObject({
|
|
560
905
|
status: "cancelling"
|
|
561
906
|
});
|
|
562
|
-
|
|
907
|
+
expect3((await store.getRun("waiter"))?.wakeAt).toBeUndefined();
|
|
563
908
|
await store.updateRun("waiter", {
|
|
564
909
|
status: "cancelled"
|
|
565
910
|
});
|
|
566
|
-
|
|
567
|
-
|
|
911
|
+
expect3(await peerStore.tryAdmitSingleton?.("waiter", "singleton:k", "checkout", 1, 16e3)).toBe(false);
|
|
912
|
+
expect3((await store.getRun("waiter"))?.status).toBe("cancelled");
|
|
568
913
|
await store.updateRun("holder", {
|
|
569
914
|
status: "completed"
|
|
570
915
|
});
|
|
@@ -575,8 +920,8 @@ function runStateStoreContract(name, makeStore) {
|
|
|
575
920
|
"singleton:k"
|
|
576
921
|
]
|
|
577
922
|
}));
|
|
578
|
-
|
|
579
|
-
|
|
923
|
+
expect3(await store.tryAdmitSingleton?.("cancelling", "singleton:k", "checkout", 1, 18e3)).toBe(false);
|
|
924
|
+
expect3((await store.getRun("cancelling"))?.tags).not.toContain(SINGLETON_ADMITTED_TAG);
|
|
580
925
|
});
|
|
581
926
|
t("competing store instances atomically claim an empty singleton slot", async () => {
|
|
582
927
|
for (const id of [
|
|
@@ -596,13 +941,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
596
941
|
store.tryAdmitSingleton?.("a-first", "singleton:k", "checkout", 1),
|
|
597
942
|
peerStore.tryAdmitSingleton?.("c-third", "singleton:k", "checkout", 1)
|
|
598
943
|
]);
|
|
599
|
-
|
|
944
|
+
expect3(results).toEqual([
|
|
600
945
|
false,
|
|
601
946
|
true,
|
|
602
947
|
false
|
|
603
948
|
]);
|
|
604
|
-
|
|
605
|
-
|
|
949
|
+
expect3(await peerStore.tryAdmitSingleton?.("a-first", "singleton:k", "checkout", 1)).toBe(true);
|
|
950
|
+
expect3((await store.getRun("a-first"))?.tags).toContain(SINGLETON_ADMITTED_TAG);
|
|
606
951
|
});
|
|
607
952
|
t("concurrent singleton claims honor limits and independent keys/workflows", async () => {
|
|
608
953
|
const claim = /* @__PURE__ */ __name((id, tag = "singleton:k", workflow = "checkout") => store.tryAdmitSingleton?.(id, tag, workflow, 2), "claim");
|
|
@@ -616,7 +961,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
616
961
|
"singleton:k"
|
|
617
962
|
]
|
|
618
963
|
}));
|
|
619
|
-
|
|
964
|
+
expect3(await claim(id)).toBe(true);
|
|
620
965
|
}
|
|
621
966
|
for (const id of [
|
|
622
967
|
"a-waiter",
|
|
@@ -631,7 +976,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
631
976
|
]
|
|
632
977
|
}));
|
|
633
978
|
}
|
|
634
|
-
|
|
979
|
+
expect3(await Promise.all([
|
|
635
980
|
"a-waiter",
|
|
636
981
|
"b-waiter",
|
|
637
982
|
"c-waiter"
|
|
@@ -643,7 +988,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
643
988
|
await store.updateRun("z-holder", {
|
|
644
989
|
status: "failed"
|
|
645
990
|
});
|
|
646
|
-
|
|
991
|
+
expect3(await Promise.all([
|
|
647
992
|
"c-waiter",
|
|
648
993
|
"b-waiter",
|
|
649
994
|
"a-waiter"
|
|
@@ -659,14 +1004,14 @@ function runStateStoreContract(name, makeStore) {
|
|
|
659
1004
|
"pending"
|
|
660
1005
|
]
|
|
661
1006
|
});
|
|
662
|
-
|
|
1007
|
+
expect3(active.filter((r) => r.tags?.includes(SINGLETON_ADMITTED_TAG))).toHaveLength(2);
|
|
663
1008
|
await store.createRun(run({
|
|
664
1009
|
id: "other-key",
|
|
665
1010
|
tags: [
|
|
666
1011
|
"singleton:other"
|
|
667
1012
|
]
|
|
668
1013
|
}));
|
|
669
|
-
|
|
1014
|
+
expect3(await claim("other-key", "singleton:other")).toBe(true);
|
|
670
1015
|
await store.createRun(run({
|
|
671
1016
|
id: "other-workflow",
|
|
672
1017
|
workflow: "other",
|
|
@@ -674,18 +1019,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
674
1019
|
"singleton:k"
|
|
675
1020
|
]
|
|
676
1021
|
}));
|
|
677
|
-
|
|
1022
|
+
expect3(await claim("other-workflow", "singleton:k", "other")).toBe(true);
|
|
678
1023
|
});
|
|
679
1024
|
t("creates a run and reads it back with its JSON input", async () => {
|
|
680
1025
|
await store.createRun(run());
|
|
681
1026
|
const loaded = await store.getRun("r1");
|
|
682
|
-
|
|
683
|
-
|
|
1027
|
+
expect3(loaded?.workflow).toBe("checkout");
|
|
1028
|
+
expect3(loaded?.input).toEqual({
|
|
684
1029
|
orderId: "o1"
|
|
685
1030
|
});
|
|
686
1031
|
});
|
|
687
1032
|
t("returns null for a missing run", async () => {
|
|
688
|
-
|
|
1033
|
+
expect3(await store.getRun("nope")).toBeNull();
|
|
689
1034
|
});
|
|
690
1035
|
t("updates a run (status/output) and round-trips recoveryAttempts + dead status", async () => {
|
|
691
1036
|
await store.createRun(run({
|
|
@@ -699,18 +1044,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
699
1044
|
updatedAt: at
|
|
700
1045
|
});
|
|
701
1046
|
const done = await store.getRun("r1");
|
|
702
|
-
|
|
703
|
-
|
|
1047
|
+
expect3(done?.status).toBe("completed");
|
|
1048
|
+
expect3(done?.output).toEqual({
|
|
704
1049
|
total: 42
|
|
705
1050
|
});
|
|
706
|
-
|
|
1051
|
+
expect3(done?.recoveryAttempts).toBe(3);
|
|
707
1052
|
await store.updateRun("r1", {
|
|
708
1053
|
status: "dead",
|
|
709
1054
|
recoveryAttempts: 4
|
|
710
1055
|
});
|
|
711
1056
|
const dead = await store.getRun("r1");
|
|
712
|
-
|
|
713
|
-
|
|
1057
|
+
expect3(dead?.status).toBe("dead");
|
|
1058
|
+
expect3(dead?.recoveryAttempts).toBe(4);
|
|
714
1059
|
});
|
|
715
1060
|
t("round-trips a run dispatch priority", async () => {
|
|
716
1061
|
await store.createRun(run({
|
|
@@ -720,8 +1065,8 @@ function runStateStoreContract(name, makeStore) {
|
|
|
720
1065
|
await store.createRun(run({
|
|
721
1066
|
id: "noprio"
|
|
722
1067
|
}));
|
|
723
|
-
|
|
724
|
-
|
|
1068
|
+
expect3((await store.getRun("withprio"))?.priority).toBe(7);
|
|
1069
|
+
expect3((await store.getRun("noprio"))?.priority).toBeUndefined();
|
|
725
1070
|
});
|
|
726
1071
|
t("deleteRun removes the run and all its rows (checkpoints, waiters, attributes)", async () => {
|
|
727
1072
|
await store.createRun(run({
|
|
@@ -736,10 +1081,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
736
1081
|
seq: 0
|
|
737
1082
|
});
|
|
738
1083
|
await store.deleteRun("r1");
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1084
|
+
expect3(await store.getRun("r1")).toBeNull();
|
|
1085
|
+
expect3(await store.listCheckpoints("r1")).toEqual([]);
|
|
1086
|
+
expect3(await store.listSignalWaiters("approve-")).toEqual([]);
|
|
1087
|
+
expect3(await store.listRuns({
|
|
743
1088
|
attributes: [
|
|
744
1089
|
{
|
|
745
1090
|
key: "tier",
|
|
@@ -750,7 +1095,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
750
1095
|
})).toEqual([]);
|
|
751
1096
|
});
|
|
752
1097
|
t("deleteRun is a no-op for a missing run", async () => {
|
|
753
|
-
await
|
|
1098
|
+
await expect3(store.deleteRun("nope")).resolves.toBeUndefined();
|
|
754
1099
|
});
|
|
755
1100
|
t("deleteRuns removes every listed run with its rows and ignores missing ids", async () => {
|
|
756
1101
|
await store.createRun(run({
|
|
@@ -774,18 +1119,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
774
1119
|
runId: "d2",
|
|
775
1120
|
seq: 0
|
|
776
1121
|
});
|
|
777
|
-
|
|
1122
|
+
expect3(typeof store.deleteRuns).toBe("function");
|
|
778
1123
|
await store.deleteRuns?.([
|
|
779
1124
|
"d1",
|
|
780
1125
|
"d2",
|
|
781
1126
|
"ghost"
|
|
782
1127
|
]);
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1128
|
+
expect3(await store.getRun("d1")).toBeNull();
|
|
1129
|
+
expect3(await store.getRun("d2")).toBeNull();
|
|
1130
|
+
expect3(await store.getRun("keep")).not.toBeNull();
|
|
1131
|
+
expect3(await store.listCheckpoints("d1")).toEqual([]);
|
|
1132
|
+
expect3(await store.listSignalWaiters("approve-")).toEqual([]);
|
|
1133
|
+
expect3(await store.listRuns({
|
|
789
1134
|
attributes: [
|
|
790
1135
|
{
|
|
791
1136
|
key: "tier",
|
|
@@ -794,7 +1139,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
794
1139
|
}
|
|
795
1140
|
]
|
|
796
1141
|
})).toEqual([]);
|
|
797
|
-
await
|
|
1142
|
+
await expect3(store.deleteRuns?.([])).resolves.toBeUndefined();
|
|
798
1143
|
});
|
|
799
1144
|
const aged = /* @__PURE__ */ __name((id, ageDays, over = {}) => run({
|
|
800
1145
|
id,
|
|
@@ -817,7 +1162,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
817
1162
|
runId: "old-done",
|
|
818
1163
|
stepId: "old-done:0"
|
|
819
1164
|
}));
|
|
820
|
-
|
|
1165
|
+
expect3(typeof store.pruneTerminalRuns).toBe("function");
|
|
821
1166
|
const deleted = await store.pruneTerminalRuns?.({
|
|
822
1167
|
statuses: [
|
|
823
1168
|
"completed",
|
|
@@ -826,12 +1171,12 @@ function runStateStoreContract(name, makeStore) {
|
|
|
826
1171
|
],
|
|
827
1172
|
maxAge: "7d"
|
|
828
1173
|
}, NOW, 100);
|
|
829
|
-
|
|
830
|
-
|
|
1174
|
+
expect3(deleted).toBe(2);
|
|
1175
|
+
expect3(await ids()).toEqual([
|
|
831
1176
|
"fresh-done",
|
|
832
1177
|
"old-live"
|
|
833
1178
|
]);
|
|
834
|
-
|
|
1179
|
+
expect3(await store.listCheckpoints("old-done")).toEqual([]);
|
|
835
1180
|
});
|
|
836
1181
|
t("pruneTerminalRuns keeps only the newest maxCount and drains in batches of limit", async () => {
|
|
837
1182
|
for (let i = 1; i <= 5; i++) await store.createRun(aged(`c${i}`, i));
|
|
@@ -841,10 +1186,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
841
1186
|
],
|
|
842
1187
|
maxCount: 2
|
|
843
1188
|
};
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1189
|
+
expect3(await store.pruneTerminalRuns?.(policy, NOW, 2)).toBe(2);
|
|
1190
|
+
expect3(await store.pruneTerminalRuns?.(policy, NOW, 2)).toBe(1);
|
|
1191
|
+
expect3(await store.pruneTerminalRuns?.(policy, NOW, 2)).toBe(0);
|
|
1192
|
+
expect3(await ids()).toEqual([
|
|
848
1193
|
"c1",
|
|
849
1194
|
"c2"
|
|
850
1195
|
]);
|
|
@@ -871,7 +1216,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
871
1216
|
namespace: "acme"
|
|
872
1217
|
}
|
|
873
1218
|
}, NOW, 100);
|
|
874
|
-
|
|
1219
|
+
expect3(await ids()).toEqual([
|
|
875
1220
|
"acme-new",
|
|
876
1221
|
"beta-old",
|
|
877
1222
|
"report-old"
|
|
@@ -887,7 +1232,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
887
1232
|
]
|
|
888
1233
|
}
|
|
889
1234
|
}, NOW, 100);
|
|
890
|
-
|
|
1235
|
+
expect3(await ids()).toEqual([
|
|
891
1236
|
"acme-new",
|
|
892
1237
|
"beta-old"
|
|
893
1238
|
]);
|
|
@@ -903,7 +1248,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
903
1248
|
namespace: "acme"
|
|
904
1249
|
}
|
|
905
1250
|
}, NOW, 100);
|
|
906
|
-
|
|
1251
|
+
expect3(await ids()).toEqual([
|
|
907
1252
|
"acme-new",
|
|
908
1253
|
"beta-old"
|
|
909
1254
|
]);
|
|
@@ -942,7 +1287,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
942
1287
|
]
|
|
943
1288
|
}
|
|
944
1289
|
}, NOW, 100);
|
|
945
|
-
|
|
1290
|
+
expect3(await ids()).toEqual([
|
|
946
1291
|
"attr-old",
|
|
947
1292
|
"attr-other",
|
|
948
1293
|
"chatty-old"
|
|
@@ -962,7 +1307,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
962
1307
|
]
|
|
963
1308
|
}
|
|
964
1309
|
}, NOW, 100);
|
|
965
|
-
|
|
1310
|
+
expect3(await ids()).toEqual([
|
|
966
1311
|
"attr-other",
|
|
967
1312
|
"chatty-old"
|
|
968
1313
|
]);
|
|
@@ -979,21 +1324,21 @@ function runStateStoreContract(name, makeStore) {
|
|
|
979
1324
|
engine.register("p-other", "1", async () => "done");
|
|
980
1325
|
await engine.start("p-root", {}, "pr");
|
|
981
1326
|
await engine.start("p-other", {}, "po");
|
|
982
|
-
|
|
1327
|
+
expect3((await engine.waitForRun("pc", {
|
|
983
1328
|
timeoutMs: 2e4
|
|
984
1329
|
})).status).toBe("suspended");
|
|
985
|
-
|
|
1330
|
+
expect3((await engine.waitForRun("po", {
|
|
986
1331
|
timeoutMs: 2e4
|
|
987
1332
|
})).status).toBe("completed");
|
|
988
|
-
await
|
|
989
|
-
|
|
1333
|
+
await expect3(engine.purgeRuns({})).rejects.toThrow(/empty scope/);
|
|
1334
|
+
expect3(await engine.purgeRuns({
|
|
990
1335
|
workflows: [
|
|
991
1336
|
"p-root"
|
|
992
1337
|
]
|
|
993
1338
|
})).toBe(2);
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1339
|
+
expect3(await store.getRun("pr")).toBeNull();
|
|
1340
|
+
expect3(await store.getRun("pc")).toBeNull();
|
|
1341
|
+
expect3(await store.getRun("po")).not.toBeNull();
|
|
997
1342
|
});
|
|
998
1343
|
t("updateRun maps every patchable field \u2014 clears error and patches tags/lockedBy/input/timers", async () => {
|
|
999
1344
|
await store.createRun(run({
|
|
@@ -1025,29 +1370,29 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1025
1370
|
updatedAt: at
|
|
1026
1371
|
});
|
|
1027
1372
|
const r = await store.getRun("r1");
|
|
1028
|
-
|
|
1029
|
-
|
|
1373
|
+
expect3(r?.status).toBe("completed");
|
|
1374
|
+
expect3(r?.output).toEqual({
|
|
1030
1375
|
ok: true
|
|
1031
1376
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1377
|
+
expect3(r?.error).toBeUndefined();
|
|
1378
|
+
expect3(r?.input).toEqual({
|
|
1034
1379
|
orderId: "o2"
|
|
1035
1380
|
});
|
|
1036
|
-
|
|
1381
|
+
expect3(r?.tags).toEqual([
|
|
1037
1382
|
"x",
|
|
1038
1383
|
"y"
|
|
1039
1384
|
]);
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1385
|
+
expect3(r?.lockedBy).toBe("owner-2");
|
|
1386
|
+
expect3(r?.wakeAt).toBe(123456);
|
|
1387
|
+
expect3(r?.lockedUntil).toBe(222222);
|
|
1043
1388
|
await store.updateRun("r1", {
|
|
1044
1389
|
awaitingDecisionTaskId: "turn-7"
|
|
1045
1390
|
});
|
|
1046
|
-
|
|
1391
|
+
expect3((await store.getRun("r1"))?.awaitingDecisionTaskId).toBe("turn-7");
|
|
1047
1392
|
await store.updateRun("r1", {
|
|
1048
1393
|
awaitingDecisionTaskId: void 0
|
|
1049
1394
|
});
|
|
1050
|
-
|
|
1395
|
+
expect3((await store.getRun("r1"))?.awaitingDecisionTaskId).toBeUndefined();
|
|
1051
1396
|
});
|
|
1052
1397
|
t("listDueTimers returns the most overdue first, capped at limit", async () => {
|
|
1053
1398
|
for (const [id, wakeAt] of [
|
|
@@ -1074,18 +1419,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1074
1419
|
wakeAt
|
|
1075
1420
|
}));
|
|
1076
1421
|
}
|
|
1077
|
-
|
|
1422
|
+
expect3((await store.listDueTimers(5e3)).map((r) => r.id)).toEqual([
|
|
1078
1423
|
"earliest",
|
|
1079
1424
|
"middle",
|
|
1080
1425
|
"late"
|
|
1081
1426
|
]);
|
|
1082
|
-
|
|
1427
|
+
expect3((await store.listDueTimers(5e3, void 0, 2)).map((r) => r.id)).toEqual([
|
|
1083
1428
|
"earliest",
|
|
1084
1429
|
"middle"
|
|
1085
1430
|
]);
|
|
1086
1431
|
});
|
|
1087
1432
|
t("countCheckpoints counts a seq range by status", async () => {
|
|
1088
|
-
|
|
1433
|
+
expect3(store.countCheckpoints).toBeDefined();
|
|
1089
1434
|
if (!store.countCheckpoints) return;
|
|
1090
1435
|
await store.createRun(run());
|
|
1091
1436
|
for (const [seq, status] of [
|
|
@@ -1125,32 +1470,33 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1125
1470
|
status: "pending",
|
|
1126
1471
|
stepId: "other:2"
|
|
1127
1472
|
}));
|
|
1128
|
-
|
|
1473
|
+
expect3(await store.countCheckpoints("r1", 1, 3, [
|
|
1129
1474
|
"pending"
|
|
1130
1475
|
])).toBe(2);
|
|
1131
|
-
|
|
1476
|
+
expect3(await store.countCheckpoints("r1", 0, 4, [
|
|
1132
1477
|
"pending",
|
|
1133
1478
|
"failed"
|
|
1134
1479
|
])).toBe(4);
|
|
1135
|
-
|
|
1480
|
+
expect3(await store.countCheckpoints("r1", 2, 2, [
|
|
1136
1481
|
"pending"
|
|
1137
1482
|
])).toBe(1);
|
|
1138
|
-
|
|
1483
|
+
expect3(await store.countCheckpoints("r1", 1, 4, [
|
|
1139
1484
|
"pending"
|
|
1140
1485
|
])).toBe(3);
|
|
1141
|
-
|
|
1486
|
+
expect3(await store.countCheckpoints("r1", 0, 0, [
|
|
1142
1487
|
"completed"
|
|
1143
1488
|
])).toBe(1);
|
|
1144
|
-
|
|
1489
|
+
expect3(await store.countCheckpoints("other", 0, 4, [
|
|
1145
1490
|
"pending"
|
|
1146
1491
|
])).toBe(1);
|
|
1147
|
-
|
|
1492
|
+
expect3(await store.countCheckpoints("r1", 5, 9, [
|
|
1148
1493
|
"pending"
|
|
1149
1494
|
])).toBe(0);
|
|
1150
|
-
|
|
1495
|
+
expect3(await store.countCheckpoints("r1", 0, 4, [])).toBe(0);
|
|
1151
1496
|
});
|
|
1497
|
+
registerCheckpointAggregationCases(t, () => store, run);
|
|
1152
1498
|
t("saveCheckpointIfPending writes over a pending row only", async () => {
|
|
1153
|
-
|
|
1499
|
+
expect3(store.saveCheckpointIfPending).toBeDefined();
|
|
1154
1500
|
if (!store.saveCheckpointIfPending) return;
|
|
1155
1501
|
await store.createRun(run());
|
|
1156
1502
|
const pending = checkpoint({
|
|
@@ -1158,11 +1504,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1158
1504
|
output: void 0
|
|
1159
1505
|
});
|
|
1160
1506
|
await store.saveCheckpoint(pending);
|
|
1161
|
-
|
|
1507
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1162
1508
|
...pending,
|
|
1163
1509
|
wakeAt: 5e3
|
|
1164
1510
|
})).toBe(true);
|
|
1165
|
-
|
|
1511
|
+
expect3((await store.getCheckpoint("r1", 0))?.wakeAt).toBe(5e3);
|
|
1166
1512
|
const done = {
|
|
1167
1513
|
...pending,
|
|
1168
1514
|
status: "completed",
|
|
@@ -1170,33 +1516,190 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1170
1516
|
ok: true
|
|
1171
1517
|
}
|
|
1172
1518
|
};
|
|
1173
|
-
|
|
1174
|
-
|
|
1519
|
+
expect3(await store.saveCheckpointIfPending(done)).toBe(true);
|
|
1520
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1175
1521
|
...pending,
|
|
1176
1522
|
wakeAt: 9e3
|
|
1177
1523
|
})).toBe(false);
|
|
1178
1524
|
const after = await store.getCheckpoint("r1", 0);
|
|
1179
|
-
|
|
1180
|
-
|
|
1525
|
+
expect3(after?.status).toBe("completed");
|
|
1526
|
+
expect3(after?.output).toEqual({
|
|
1181
1527
|
ok: true
|
|
1182
1528
|
});
|
|
1183
|
-
|
|
1529
|
+
expect3(after?.wakeAt).toBeUndefined();
|
|
1184
1530
|
await store.saveCheckpoint(checkpoint({
|
|
1185
1531
|
seq: 1,
|
|
1186
1532
|
status: "failed",
|
|
1187
1533
|
stepId: "r1:1"
|
|
1188
1534
|
}));
|
|
1189
|
-
|
|
1535
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1190
1536
|
...pending,
|
|
1191
1537
|
seq: 1,
|
|
1192
1538
|
stepId: "r1:1"
|
|
1193
1539
|
})).toBe(false);
|
|
1194
|
-
|
|
1195
|
-
|
|
1540
|
+
expect3((await store.getCheckpoint("r1", 1))?.status).toBe("failed");
|
|
1541
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1196
1542
|
...pending,
|
|
1197
1543
|
seq: 9
|
|
1198
1544
|
})).toBe(false);
|
|
1199
|
-
|
|
1545
|
+
expect3(await store.getCheckpoint("r1", 9)).toBeNull();
|
|
1546
|
+
});
|
|
1547
|
+
t("saveCheckpointsIfPending settles a batch row by row, in order", async () => {
|
|
1548
|
+
expect3(store.saveCheckpointsIfPending).toBeDefined();
|
|
1549
|
+
if (!store.saveCheckpointsIfPending) return;
|
|
1550
|
+
expect3(await store.saveCheckpointsIfPending([])).toEqual([]);
|
|
1551
|
+
await store.createRun(run());
|
|
1552
|
+
await store.createRun(run({
|
|
1553
|
+
id: "r2"
|
|
1554
|
+
}));
|
|
1555
|
+
const pending = /* @__PURE__ */ __name((runId, seq) => checkpoint({
|
|
1556
|
+
runId,
|
|
1557
|
+
seq,
|
|
1558
|
+
stepId: `${runId}:${seq}`,
|
|
1559
|
+
status: "pending",
|
|
1560
|
+
output: void 0,
|
|
1561
|
+
wakeAt: 5e3
|
|
1562
|
+
}), "pending");
|
|
1563
|
+
for (const seq of [
|
|
1564
|
+
0,
|
|
1565
|
+
1,
|
|
1566
|
+
2,
|
|
1567
|
+
3
|
|
1568
|
+
]) await store.saveCheckpoint(pending("r1", seq));
|
|
1569
|
+
await store.saveCheckpoint(pending("r2", 0));
|
|
1570
|
+
await store.saveCheckpoint({
|
|
1571
|
+
...pending("r1", 1),
|
|
1572
|
+
status: "completed",
|
|
1573
|
+
output: {
|
|
1574
|
+
first: 1
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
const done = /* @__PURE__ */ __name((runId, seq, output) => ({
|
|
1578
|
+
...pending(runId, seq),
|
|
1579
|
+
status: "completed",
|
|
1580
|
+
output,
|
|
1581
|
+
wakeAt: void 0
|
|
1582
|
+
}), "done");
|
|
1583
|
+
const wrote = await store.saveCheckpointsIfPending([
|
|
1584
|
+
done("r1", 0, {
|
|
1585
|
+
n: 0
|
|
1586
|
+
}),
|
|
1587
|
+
done("r1", 1, {
|
|
1588
|
+
n: 1
|
|
1589
|
+
}),
|
|
1590
|
+
// A second copy of the same row: compared against the first copy's write, so refused.
|
|
1591
|
+
done("r1", 0, {
|
|
1592
|
+
n: "dup"
|
|
1593
|
+
}),
|
|
1594
|
+
{
|
|
1595
|
+
...pending("r1", 2),
|
|
1596
|
+
status: "failed",
|
|
1597
|
+
error: {
|
|
1598
|
+
message: "boom"
|
|
1599
|
+
},
|
|
1600
|
+
wakeAt: void 0
|
|
1601
|
+
},
|
|
1602
|
+
// No such row: nothing is written.
|
|
1603
|
+
done("r1", 9, {
|
|
1604
|
+
n: 9
|
|
1605
|
+
}),
|
|
1606
|
+
// Another run's row, same seq as one above.
|
|
1607
|
+
done("r2", 0, {
|
|
1608
|
+
n: "r2"
|
|
1609
|
+
})
|
|
1610
|
+
]);
|
|
1611
|
+
expect3(wrote).toEqual([
|
|
1612
|
+
true,
|
|
1613
|
+
false,
|
|
1614
|
+
false,
|
|
1615
|
+
true,
|
|
1616
|
+
false,
|
|
1617
|
+
true
|
|
1618
|
+
]);
|
|
1619
|
+
const r1 = await store.listCheckpoints("r1");
|
|
1620
|
+
expect3(r1.map((c) => [
|
|
1621
|
+
c.seq,
|
|
1622
|
+
c.status,
|
|
1623
|
+
c.output
|
|
1624
|
+
])).toEqual([
|
|
1625
|
+
[
|
|
1626
|
+
0,
|
|
1627
|
+
"completed",
|
|
1628
|
+
{
|
|
1629
|
+
n: 0
|
|
1630
|
+
}
|
|
1631
|
+
],
|
|
1632
|
+
[
|
|
1633
|
+
1,
|
|
1634
|
+
"completed",
|
|
1635
|
+
{
|
|
1636
|
+
first: 1
|
|
1637
|
+
}
|
|
1638
|
+
],
|
|
1639
|
+
[
|
|
1640
|
+
2,
|
|
1641
|
+
"failed",
|
|
1642
|
+
void 0
|
|
1643
|
+
],
|
|
1644
|
+
[
|
|
1645
|
+
3,
|
|
1646
|
+
"pending",
|
|
1647
|
+
void 0
|
|
1648
|
+
]
|
|
1649
|
+
]);
|
|
1650
|
+
expect3(r1[2]?.error).toEqual({
|
|
1651
|
+
message: "boom"
|
|
1652
|
+
});
|
|
1653
|
+
expect3(r1[0]?.wakeAt).toBeUndefined();
|
|
1654
|
+
expect3(r1[3]?.wakeAt).toBe(5e3);
|
|
1655
|
+
expect3(await store.getCheckpoint("r1", 9)).toBeNull();
|
|
1656
|
+
expect3((await store.getCheckpoint("r2", 0))?.output).toEqual({
|
|
1657
|
+
n: "r2"
|
|
1658
|
+
});
|
|
1659
|
+
expect3(await store.saveCheckpointsIfPending([
|
|
1660
|
+
{
|
|
1661
|
+
...pending("r1", 3),
|
|
1662
|
+
wakeAt: 9e3
|
|
1663
|
+
},
|
|
1664
|
+
done("r1", 3, {
|
|
1665
|
+
n: 3
|
|
1666
|
+
})
|
|
1667
|
+
])).toEqual([
|
|
1668
|
+
true,
|
|
1669
|
+
true
|
|
1670
|
+
]);
|
|
1671
|
+
expect3((await store.getCheckpoint("r1", 3))?.output).toEqual({
|
|
1672
|
+
n: 3
|
|
1673
|
+
});
|
|
1674
|
+
});
|
|
1675
|
+
t("saveCheckpointsIfPending writes a wide batch", async () => {
|
|
1676
|
+
if (!store.saveCheckpointsIfPending) return;
|
|
1677
|
+
await store.createRun(run());
|
|
1678
|
+
const n = 600;
|
|
1679
|
+
const rows = Array.from({
|
|
1680
|
+
length: n
|
|
1681
|
+
}, (_, seq) => checkpoint({
|
|
1682
|
+
seq,
|
|
1683
|
+
stepId: `r1:${seq}`,
|
|
1684
|
+
status: "pending",
|
|
1685
|
+
output: void 0
|
|
1686
|
+
}));
|
|
1687
|
+
for (const row of rows) await store.saveCheckpoint(row);
|
|
1688
|
+
const wrote = await store.saveCheckpointsIfPending(rows.map((row) => ({
|
|
1689
|
+
...row,
|
|
1690
|
+
status: "completed",
|
|
1691
|
+
output: {
|
|
1692
|
+
seq: row.seq
|
|
1693
|
+
}
|
|
1694
|
+
})));
|
|
1695
|
+
expect3(wrote.every((w) => w === true)).toBe(true);
|
|
1696
|
+
expect3(wrote).toHaveLength(n);
|
|
1697
|
+
expect3(await store.countCheckpoints?.("r1", 0, n - 1, [
|
|
1698
|
+
"completed"
|
|
1699
|
+
])).toBe(n);
|
|
1700
|
+
expect3((await store.getCheckpoint("r1", n - 1))?.output).toEqual({
|
|
1701
|
+
seq: n - 1
|
|
1702
|
+
});
|
|
1200
1703
|
});
|
|
1201
1704
|
t("upserts checkpoints, reads them by (runId, seq), and lists them ordered by seq", async () => {
|
|
1202
1705
|
await store.createRun(run());
|
|
@@ -1214,13 +1717,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1214
1717
|
again: true
|
|
1215
1718
|
}
|
|
1216
1719
|
}));
|
|
1217
|
-
|
|
1720
|
+
expect3((await store.getCheckpoint("r1", 0))?.output).toEqual({
|
|
1218
1721
|
ok: true,
|
|
1219
1722
|
again: true
|
|
1220
1723
|
});
|
|
1221
|
-
|
|
1724
|
+
expect3((await store.getCheckpoint("r1", 1))?.name).toBe("charge");
|
|
1222
1725
|
const list = await store.listCheckpoints("r1");
|
|
1223
|
-
|
|
1726
|
+
expect3(list.map((c) => c.seq)).toEqual([
|
|
1224
1727
|
0,
|
|
1225
1728
|
1
|
|
1226
1729
|
]);
|
|
@@ -1256,14 +1759,14 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1256
1759
|
}
|
|
1257
1760
|
}));
|
|
1258
1761
|
const getLatestCheckpointByName = store.getLatestCheckpointByName;
|
|
1259
|
-
|
|
1762
|
+
expect3(getLatestCheckpointByName).toBeDefined();
|
|
1260
1763
|
if (!getLatestCheckpointByName) return;
|
|
1261
1764
|
const latest = await getLatestCheckpointByName.call(store, "r1", "event:progress");
|
|
1262
|
-
|
|
1263
|
-
|
|
1765
|
+
expect3(latest?.seq).toBe(3);
|
|
1766
|
+
expect3(latest?.output).toEqual({
|
|
1264
1767
|
pct: 90
|
|
1265
1768
|
});
|
|
1266
|
-
|
|
1769
|
+
expect3(await getLatestCheckpointByName.call(store, "r1", "event:missing")).toBeUndefined();
|
|
1267
1770
|
});
|
|
1268
1771
|
t("listCheckpointsByNamePrefix returns prefix matches ordered by seq (empty => none)", async () => {
|
|
1269
1772
|
await store.createRun(run());
|
|
@@ -1290,18 +1793,48 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1290
1793
|
output: "child-b"
|
|
1291
1794
|
}));
|
|
1292
1795
|
const listCheckpointsByNamePrefix = store.listCheckpointsByNamePrefix;
|
|
1293
|
-
|
|
1796
|
+
expect3(listCheckpointsByNamePrefix).toBeDefined();
|
|
1294
1797
|
if (!listCheckpointsByNamePrefix) return;
|
|
1295
1798
|
const matches = await listCheckpointsByNamePrefix.call(store, "r1", [
|
|
1296
1799
|
"signal:child:",
|
|
1297
1800
|
"spawn:"
|
|
1298
1801
|
]);
|
|
1299
|
-
|
|
1802
|
+
expect3(matches.map((c) => c.name)).toEqual([
|
|
1300
1803
|
"spawn:0",
|
|
1301
1804
|
"signal:child:c2",
|
|
1302
1805
|
"spawn:1"
|
|
1303
1806
|
]);
|
|
1304
|
-
|
|
1807
|
+
expect3(await listCheckpointsByNamePrefix.call(store, "r1", [])).toEqual([]);
|
|
1808
|
+
});
|
|
1809
|
+
t("a name or token prefix matches literally \u2014 `_` and `%` are not wildcards", async () => {
|
|
1810
|
+
await store.createRun(run());
|
|
1811
|
+
await store.saveCheckpoint(checkpoint({
|
|
1812
|
+
seq: 0,
|
|
1813
|
+
name: "signal:child:job_1.onError.0"
|
|
1814
|
+
}));
|
|
1815
|
+
await store.saveCheckpoint(checkpoint({
|
|
1816
|
+
seq: 1,
|
|
1817
|
+
name: "signal:child:jobX1.onError.0"
|
|
1818
|
+
}));
|
|
1819
|
+
await store.putSignalWaiter({
|
|
1820
|
+
token: "child:job_1.child.0",
|
|
1821
|
+
runId: "r1",
|
|
1822
|
+
seq: 2
|
|
1823
|
+
});
|
|
1824
|
+
await store.putSignalWaiter({
|
|
1825
|
+
token: "child:jobX1.child.0",
|
|
1826
|
+
runId: "r1",
|
|
1827
|
+
seq: 3
|
|
1828
|
+
});
|
|
1829
|
+
const matches = await store.listCheckpointsByNamePrefix?.("r1", [
|
|
1830
|
+
"signal:child:job_1."
|
|
1831
|
+
]);
|
|
1832
|
+
expect3(matches?.map((c) => c.name)).toEqual([
|
|
1833
|
+
"signal:child:job_1.onError.0"
|
|
1834
|
+
]);
|
|
1835
|
+
expect3((await store.listSignalWaiters("child:job_1.")).map((w) => w.token)).toEqual([
|
|
1836
|
+
"child:job_1.child.0"
|
|
1837
|
+
]);
|
|
1305
1838
|
});
|
|
1306
1839
|
t("round-trips checkpoint events and reads back a missing checkpoint as null", async () => {
|
|
1307
1840
|
await store.createRun(run());
|
|
@@ -1316,7 +1849,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1316
1849
|
}
|
|
1317
1850
|
]
|
|
1318
1851
|
}));
|
|
1319
|
-
|
|
1852
|
+
expect3((await store.getCheckpoint("r1", 0))?.events).toEqual([
|
|
1320
1853
|
{
|
|
1321
1854
|
at: 1,
|
|
1322
1855
|
level: "error",
|
|
@@ -1325,7 +1858,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1325
1858
|
status: "failed"
|
|
1326
1859
|
}
|
|
1327
1860
|
]);
|
|
1328
|
-
|
|
1861
|
+
expect3(await store.getCheckpoint("r1", 99)).toBeNull();
|
|
1329
1862
|
});
|
|
1330
1863
|
t("lists incomplete runs, pending runs (FIFO), and due timers", async () => {
|
|
1331
1864
|
await store.createRun(run({
|
|
@@ -1351,20 +1884,20 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1351
1884
|
status: "pending",
|
|
1352
1885
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:01.000Z")
|
|
1353
1886
|
}));
|
|
1354
|
-
|
|
1887
|
+
expect3((await store.listIncompleteRuns()).map((r) => r.id)).toEqual([
|
|
1355
1888
|
"running1"
|
|
1356
1889
|
]);
|
|
1357
|
-
|
|
1890
|
+
expect3((await store.listPendingRuns(10)).map((r) => r.id)).toEqual([
|
|
1358
1891
|
"pending1",
|
|
1359
1892
|
"pending2"
|
|
1360
1893
|
]);
|
|
1361
|
-
|
|
1894
|
+
expect3((await store.listPendingRuns(1)).map((r) => r.id)).toEqual([
|
|
1362
1895
|
"pending1"
|
|
1363
1896
|
]);
|
|
1364
|
-
|
|
1897
|
+
expect3((await store.listDueTimers(1e4)).map((r) => r.id)).toEqual([
|
|
1365
1898
|
"suspended1"
|
|
1366
1899
|
]);
|
|
1367
|
-
|
|
1900
|
+
expect3(await store.listDueTimers(1e3)).toHaveLength(0);
|
|
1368
1901
|
});
|
|
1369
1902
|
t("keeps a worker inside its own namespace on every path that picks work up", async () => {
|
|
1370
1903
|
await store.createRun(run({
|
|
@@ -1399,33 +1932,33 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1399
1932
|
wakeAt: 5e3,
|
|
1400
1933
|
namespace: "beta"
|
|
1401
1934
|
}));
|
|
1402
|
-
|
|
1935
|
+
expect3((await store.listPendingRuns(10, "alpha")).map((r) => r.id)).toEqual([
|
|
1403
1936
|
"a-pending"
|
|
1404
1937
|
]);
|
|
1405
|
-
|
|
1938
|
+
expect3((await store.listIncompleteRuns("alpha")).map((r) => r.id)).toEqual([
|
|
1406
1939
|
"a-running"
|
|
1407
1940
|
]);
|
|
1408
|
-
|
|
1941
|
+
expect3((await store.listDueTimers(1e4, "alpha")).map((r) => r.id)).toEqual([
|
|
1409
1942
|
"a-timer"
|
|
1410
1943
|
]);
|
|
1411
|
-
|
|
1944
|
+
expect3((await store.listRuns({
|
|
1412
1945
|
namespace: "alpha",
|
|
1413
1946
|
status: "running"
|
|
1414
1947
|
})).map((r) => r.id)).toEqual([
|
|
1415
1948
|
"a-running"
|
|
1416
1949
|
]);
|
|
1417
|
-
|
|
1950
|
+
expect3((await store.listPendingRuns(1, "alpha")).map((r) => r.id)).toEqual([
|
|
1418
1951
|
"a-pending"
|
|
1419
1952
|
]);
|
|
1420
|
-
|
|
1953
|
+
expect3((await store.listPendingRuns(10)).map((r) => r.id).sort()).toEqual([
|
|
1421
1954
|
"a-pending",
|
|
1422
1955
|
"b-pending"
|
|
1423
1956
|
]);
|
|
1424
|
-
|
|
1957
|
+
expect3((await store.listIncompleteRuns()).map((r) => r.id).sort()).toEqual([
|
|
1425
1958
|
"a-running",
|
|
1426
1959
|
"b-running"
|
|
1427
1960
|
]);
|
|
1428
|
-
|
|
1961
|
+
expect3((await store.listDueTimers(1e4)).map((r) => r.id).sort()).toEqual([
|
|
1429
1962
|
"a-timer",
|
|
1430
1963
|
"b-timer"
|
|
1431
1964
|
]);
|
|
@@ -1434,20 +1967,20 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1434
1967
|
await store.createRun(run({
|
|
1435
1968
|
id: "r1"
|
|
1436
1969
|
}));
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1970
|
+
expect3(await store.tryLockRun("r1", "A", 2e3, 1e3)).toBe(true);
|
|
1971
|
+
expect3(await store.tryLockRun("r1", "B", 3e3, 1500)).toBe(false);
|
|
1972
|
+
expect3(await store.tryLockRun("r1", "B", 4e3, 2500)).toBe(true);
|
|
1440
1973
|
await store.releaseRunLock("r1");
|
|
1441
|
-
|
|
1974
|
+
expect3(await store.tryLockRun("r1", "C", 9e3, 2600)).toBe(true);
|
|
1442
1975
|
});
|
|
1443
1976
|
t("renewRunLock only succeeds for the current owner", async () => {
|
|
1444
1977
|
await store.createRun(run({
|
|
1445
1978
|
id: "r1"
|
|
1446
1979
|
}));
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1980
|
+
expect3(await store.tryLockRun("r1", "A", 2e3, 1e3)).toBe(true);
|
|
1981
|
+
expect3(await store.renewRunLock("r1", "A", 5e3)).toBe(true);
|
|
1982
|
+
expect3(await store.renewRunLock("r1", "B", 9e3)).toBe(false);
|
|
1983
|
+
expect3(await store.tryLockRun("r1", "B", 6e3, 4e3)).toBe(false);
|
|
1451
1984
|
});
|
|
1452
1985
|
t("filters listRuns by workflow and by status", async () => {
|
|
1453
1986
|
await store.createRun(run({
|
|
@@ -1465,19 +1998,19 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1465
1998
|
workflow: "checkout",
|
|
1466
1999
|
status: "completed"
|
|
1467
2000
|
}));
|
|
1468
|
-
|
|
2001
|
+
expect3((await store.listRuns({
|
|
1469
2002
|
workflow: "checkout"
|
|
1470
2003
|
})).map((r) => r.id).sort()).toEqual([
|
|
1471
2004
|
"a",
|
|
1472
2005
|
"c"
|
|
1473
2006
|
]);
|
|
1474
|
-
|
|
2007
|
+
expect3((await store.listRuns({
|
|
1475
2008
|
status: "running"
|
|
1476
2009
|
})).map((r) => r.id).sort()).toEqual([
|
|
1477
2010
|
"a",
|
|
1478
2011
|
"b"
|
|
1479
2012
|
]);
|
|
1480
|
-
|
|
2013
|
+
expect3((await store.listRuns({
|
|
1481
2014
|
workflow: "checkout",
|
|
1482
2015
|
status: "running"
|
|
1483
2016
|
})).map((r) => r.id)).toEqual([
|
|
@@ -1501,7 +2034,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1501
2034
|
id: "d",
|
|
1502
2035
|
status: "pending"
|
|
1503
2036
|
}));
|
|
1504
|
-
|
|
2037
|
+
expect3((await store.listRuns({
|
|
1505
2038
|
statuses: [
|
|
1506
2039
|
"running",
|
|
1507
2040
|
"suspended"
|
|
@@ -1510,7 +2043,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1510
2043
|
"a",
|
|
1511
2044
|
"b"
|
|
1512
2045
|
]);
|
|
1513
|
-
|
|
2046
|
+
expect3((await store.listRuns({
|
|
1514
2047
|
status: "running",
|
|
1515
2048
|
statuses: [
|
|
1516
2049
|
"running",
|
|
@@ -1519,7 +2052,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1519
2052
|
})).map((r) => r.id)).toEqual([
|
|
1520
2053
|
"a"
|
|
1521
2054
|
]);
|
|
1522
|
-
|
|
2055
|
+
expect3(await store.listRuns({
|
|
1523
2056
|
statuses: []
|
|
1524
2057
|
})).toHaveLength(0);
|
|
1525
2058
|
});
|
|
@@ -1541,21 +2074,21 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1541
2074
|
await store.createRun(run({
|
|
1542
2075
|
id: "c"
|
|
1543
2076
|
}));
|
|
1544
|
-
|
|
2077
|
+
expect3((await store.getRun("a"))?.tags).toEqual([
|
|
1545
2078
|
"etl",
|
|
1546
2079
|
"critical"
|
|
1547
2080
|
]);
|
|
1548
|
-
|
|
2081
|
+
expect3((await store.listRuns({
|
|
1549
2082
|
tag: "etl"
|
|
1550
2083
|
})).map((r) => r.id)).toEqual([
|
|
1551
2084
|
"a"
|
|
1552
2085
|
]);
|
|
1553
|
-
|
|
2086
|
+
expect3((await store.listRuns({
|
|
1554
2087
|
tag: "etl-foo"
|
|
1555
2088
|
})).map((r) => r.id)).toEqual([
|
|
1556
2089
|
"b"
|
|
1557
2090
|
]);
|
|
1558
|
-
|
|
2091
|
+
expect3(await store.listRuns({
|
|
1559
2092
|
tag: "nope"
|
|
1560
2093
|
})).toHaveLength(0);
|
|
1561
2094
|
});
|
|
@@ -1571,17 +2104,17 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1571
2104
|
await store.createRun(run({
|
|
1572
2105
|
id: "c"
|
|
1573
2106
|
}));
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
2107
|
+
expect3((await store.getRun("a"))?.origin).toBe("@dudousxd/nestjs-catalog-pipeline");
|
|
2108
|
+
expect3((await store.getRun("c"))?.origin).toBeUndefined();
|
|
2109
|
+
expect3((await store.listRuns({
|
|
1577
2110
|
origin: "@dudousxd/nestjs-catalog-pipeline"
|
|
1578
2111
|
})).map((r) => r.id)).toEqual([
|
|
1579
2112
|
"a"
|
|
1580
2113
|
]);
|
|
1581
|
-
|
|
2114
|
+
expect3(await store.listRuns({
|
|
1582
2115
|
origin: "unknown"
|
|
1583
2116
|
})).toEqual([]);
|
|
1584
|
-
|
|
2117
|
+
expect3((await store.listRuns({})).map((r) => r.id).sort()).toEqual([
|
|
1585
2118
|
"a",
|
|
1586
2119
|
"b",
|
|
1587
2120
|
"c"
|
|
@@ -1598,7 +2131,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1598
2131
|
await store.createRun(run({
|
|
1599
2132
|
id: "c"
|
|
1600
2133
|
}));
|
|
1601
|
-
|
|
2134
|
+
expect3((await store.listRuns({
|
|
1602
2135
|
origin: null
|
|
1603
2136
|
})).map((r) => r.id).sort()).toEqual([
|
|
1604
2137
|
"b",
|
|
@@ -1608,7 +2141,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1608
2141
|
id: "d",
|
|
1609
2142
|
status: "dead"
|
|
1610
2143
|
}));
|
|
1611
|
-
|
|
2144
|
+
expect3((await store.listRuns({
|
|
1612
2145
|
origin: null,
|
|
1613
2146
|
status: "dead"
|
|
1614
2147
|
})).map((r) => r.id)).toEqual([
|
|
@@ -1641,22 +2174,22 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1641
2174
|
status: "failed"
|
|
1642
2175
|
}));
|
|
1643
2176
|
const facets = await store.runFacets({});
|
|
1644
|
-
|
|
2177
|
+
expect3(facets).toContainEqual({
|
|
1645
2178
|
status: "completed",
|
|
1646
2179
|
origin: "pkg-a",
|
|
1647
2180
|
count: 2
|
|
1648
2181
|
});
|
|
1649
|
-
|
|
2182
|
+
expect3(facets).toContainEqual({
|
|
1650
2183
|
status: "failed",
|
|
1651
2184
|
origin: "pkg-b",
|
|
1652
2185
|
count: 1
|
|
1653
2186
|
});
|
|
1654
|
-
|
|
2187
|
+
expect3(facets).toContainEqual({
|
|
1655
2188
|
status: "failed",
|
|
1656
2189
|
origin: null,
|
|
1657
2190
|
count: 2
|
|
1658
2191
|
});
|
|
1659
|
-
|
|
2192
|
+
expect3(facets.reduce((n, f) => n + f.count, 0)).toBe(5);
|
|
1660
2193
|
});
|
|
1661
2194
|
t("narrows runFacets on the same predicates listRuns pages", async () => {
|
|
1662
2195
|
if (!store.runFacets) return;
|
|
@@ -1670,7 +2203,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1670
2203
|
status: "failed",
|
|
1671
2204
|
namespace: "globex"
|
|
1672
2205
|
}));
|
|
1673
|
-
|
|
2206
|
+
expect3(await store.runFacets({
|
|
1674
2207
|
namespace: "acme"
|
|
1675
2208
|
})).toEqual([
|
|
1676
2209
|
{
|
|
@@ -1699,7 +2232,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1699
2232
|
namespace: "initech",
|
|
1700
2233
|
workflow: "wc"
|
|
1701
2234
|
}));
|
|
1702
|
-
|
|
2235
|
+
expect3((await store.listRuns({
|
|
1703
2236
|
statuses: [
|
|
1704
2237
|
"failed",
|
|
1705
2238
|
"dead"
|
|
@@ -1708,7 +2241,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1708
2241
|
"a",
|
|
1709
2242
|
"b"
|
|
1710
2243
|
]);
|
|
1711
|
-
|
|
2244
|
+
expect3((await store.listRuns({
|
|
1712
2245
|
namespaces: [
|
|
1713
2246
|
"acme",
|
|
1714
2247
|
"globex"
|
|
@@ -1717,7 +2250,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1717
2250
|
"a",
|
|
1718
2251
|
"b"
|
|
1719
2252
|
]);
|
|
1720
|
-
|
|
2253
|
+
expect3((await store.listRuns({
|
|
1721
2254
|
workflows: [
|
|
1722
2255
|
"wa",
|
|
1723
2256
|
"wc"
|
|
@@ -1739,7 +2272,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1739
2272
|
await store.createRun(run({
|
|
1740
2273
|
id: "c"
|
|
1741
2274
|
}));
|
|
1742
|
-
|
|
2275
|
+
expect3((await store.listRuns({
|
|
1743
2276
|
origins: [
|
|
1744
2277
|
"pkg-a",
|
|
1745
2278
|
null
|
|
@@ -1748,7 +2281,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1748
2281
|
"a",
|
|
1749
2282
|
"c"
|
|
1750
2283
|
]);
|
|
1751
|
-
|
|
2284
|
+
expect3((await store.listRuns({
|
|
1752
2285
|
origins: [
|
|
1753
2286
|
"pkg-a",
|
|
1754
2287
|
"pkg-b"
|
|
@@ -1769,7 +2302,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1769
2302
|
status: "dead",
|
|
1770
2303
|
namespace: "acme"
|
|
1771
2304
|
}));
|
|
1772
|
-
|
|
2305
|
+
expect3((await store.listRuns({
|
|
1773
2306
|
namespaces: [
|
|
1774
2307
|
"acme"
|
|
1775
2308
|
],
|
|
@@ -1779,13 +2312,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1779
2312
|
})).map((r) => r.id)).toEqual([
|
|
1780
2313
|
"b"
|
|
1781
2314
|
]);
|
|
1782
|
-
|
|
2315
|
+
expect3(await store.listRuns({
|
|
1783
2316
|
namespaces: []
|
|
1784
2317
|
})).toEqual([]);
|
|
1785
|
-
|
|
2318
|
+
expect3(await store.listRuns({
|
|
1786
2319
|
statuses: []
|
|
1787
2320
|
})).toEqual([]);
|
|
1788
|
-
|
|
2321
|
+
expect3(await store.listRuns({
|
|
1789
2322
|
origins: []
|
|
1790
2323
|
})).toEqual([]);
|
|
1791
2324
|
});
|
|
@@ -1810,7 +2343,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1810
2343
|
"adhoc"
|
|
1811
2344
|
]
|
|
1812
2345
|
}));
|
|
1813
|
-
|
|
2346
|
+
expect3((await store.listRuns({
|
|
1814
2347
|
tags: [
|
|
1815
2348
|
"etl",
|
|
1816
2349
|
"nightly"
|
|
@@ -1819,7 +2352,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1819
2352
|
"a",
|
|
1820
2353
|
"b"
|
|
1821
2354
|
]);
|
|
1822
|
-
|
|
2355
|
+
expect3(await store.listRuns({
|
|
1823
2356
|
tags: []
|
|
1824
2357
|
})).toEqual([]);
|
|
1825
2358
|
});
|
|
@@ -1860,7 +2393,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1860
2393
|
}
|
|
1861
2394
|
]
|
|
1862
2395
|
});
|
|
1863
|
-
|
|
2396
|
+
expect3(inSet.map((r) => r.id).sort()).toEqual([
|
|
1864
2397
|
"a",
|
|
1865
2398
|
"b"
|
|
1866
2399
|
]);
|
|
@@ -1876,10 +2409,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1876
2409
|
}
|
|
1877
2410
|
]
|
|
1878
2411
|
});
|
|
1879
|
-
|
|
2412
|
+
expect3(mixed.map((r) => r.id)).toEqual([
|
|
1880
2413
|
"d"
|
|
1881
2414
|
]);
|
|
1882
|
-
|
|
2415
|
+
expect3(await store.listRuns({
|
|
1883
2416
|
attributes: [
|
|
1884
2417
|
{
|
|
1885
2418
|
key: "tier",
|
|
@@ -1888,7 +2421,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1888
2421
|
}
|
|
1889
2422
|
]
|
|
1890
2423
|
})).toEqual([]);
|
|
1891
|
-
|
|
2424
|
+
expect3((await store.listRuns({
|
|
1892
2425
|
attributes: [
|
|
1893
2426
|
{
|
|
1894
2427
|
key: "tier",
|
|
@@ -1916,7 +2449,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1916
2449
|
id: "c",
|
|
1917
2450
|
namespace: "globex"
|
|
1918
2451
|
}));
|
|
1919
|
-
|
|
2452
|
+
expect3(await store.runValueFacets({
|
|
1920
2453
|
field: "namespace"
|
|
1921
2454
|
}, {})).toEqual([
|
|
1922
2455
|
{
|
|
@@ -1941,11 +2474,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1941
2474
|
const values = await store.runValueFacets({
|
|
1942
2475
|
field: "origin"
|
|
1943
2476
|
}, {});
|
|
1944
|
-
|
|
2477
|
+
expect3(values).toContainEqual({
|
|
1945
2478
|
value: "pkg-a",
|
|
1946
2479
|
count: 1
|
|
1947
2480
|
});
|
|
1948
|
-
|
|
2481
|
+
expect3(values).toContainEqual({
|
|
1949
2482
|
value: null,
|
|
1950
2483
|
count: 1
|
|
1951
2484
|
});
|
|
@@ -1967,7 +2500,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1967
2500
|
"nightly"
|
|
1968
2501
|
]
|
|
1969
2502
|
}));
|
|
1970
|
-
|
|
2503
|
+
expect3(await store.runValueFacets({
|
|
1971
2504
|
field: "tag"
|
|
1972
2505
|
}, {
|
|
1973
2506
|
namespace: "acme"
|
|
@@ -1999,7 +2532,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1999
2532
|
tier: "free"
|
|
2000
2533
|
}
|
|
2001
2534
|
}));
|
|
2002
|
-
|
|
2535
|
+
expect3(await store.runValueFacets({
|
|
2003
2536
|
field: "tag"
|
|
2004
2537
|
}, {})).toEqual([
|
|
2005
2538
|
{
|
|
@@ -2011,7 +2544,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2011
2544
|
count: 1
|
|
2012
2545
|
}
|
|
2013
2546
|
]);
|
|
2014
|
-
|
|
2547
|
+
expect3(await store.runValueFacets({
|
|
2015
2548
|
field: "attributeKey"
|
|
2016
2549
|
}, {})).toEqual([
|
|
2017
2550
|
{
|
|
@@ -2019,7 +2552,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2019
2552
|
count: 2
|
|
2020
2553
|
}
|
|
2021
2554
|
]);
|
|
2022
|
-
|
|
2555
|
+
expect3((await store.runValueFacets({
|
|
2023
2556
|
field: "attributeValue",
|
|
2024
2557
|
key: "tier"
|
|
2025
2558
|
}, {})).map((r) => r.value).sort()).toEqual([
|
|
@@ -2050,18 +2583,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2050
2583
|
}, {}, {
|
|
2051
2584
|
search: "acme"
|
|
2052
2585
|
});
|
|
2053
|
-
|
|
2586
|
+
expect3(matches.map((row) => row.value).sort()).toEqual([
|
|
2054
2587
|
"acme-prod",
|
|
2055
2588
|
"acme-staging"
|
|
2056
2589
|
]);
|
|
2057
|
-
|
|
2590
|
+
expect3((await store.runValueFacets({
|
|
2058
2591
|
field: "namespace"
|
|
2059
2592
|
}, {}, {
|
|
2060
2593
|
limit: 1
|
|
2061
2594
|
})).map((r) => r.value)).toEqual([
|
|
2062
2595
|
"globex"
|
|
2063
2596
|
]);
|
|
2064
|
-
|
|
2597
|
+
expect3((await store.runValueFacets({
|
|
2065
2598
|
field: "namespace"
|
|
2066
2599
|
}, {}, {
|
|
2067
2600
|
limit: 1,
|
|
@@ -2107,7 +2640,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2107
2640
|
limit: 2,
|
|
2108
2641
|
offset: 2
|
|
2109
2642
|
});
|
|
2110
|
-
|
|
2643
|
+
expect3(first).toEqual([
|
|
2111
2644
|
{
|
|
2112
2645
|
value: "one",
|
|
2113
2646
|
count: 3
|
|
@@ -2117,13 +2650,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2117
2650
|
count: 2
|
|
2118
2651
|
}
|
|
2119
2652
|
]);
|
|
2120
|
-
|
|
2653
|
+
expect3(second).toEqual([
|
|
2121
2654
|
{
|
|
2122
2655
|
value: "three",
|
|
2123
2656
|
count: 1
|
|
2124
2657
|
}
|
|
2125
2658
|
]);
|
|
2126
|
-
|
|
2659
|
+
expect3(new Set([
|
|
2127
2660
|
...first,
|
|
2128
2661
|
...second
|
|
2129
2662
|
].map((r) => r.value)).size).toBe(3);
|
|
@@ -2151,7 +2684,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2151
2684
|
"etl"
|
|
2152
2685
|
]
|
|
2153
2686
|
}));
|
|
2154
|
-
|
|
2687
|
+
expect3((await store.runValueFacets({
|
|
2155
2688
|
field: "tag"
|
|
2156
2689
|
}, {}, {
|
|
2157
2690
|
search: "type:"
|
|
@@ -2159,7 +2692,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2159
2692
|
"type:mel",
|
|
2160
2693
|
"type:mvr"
|
|
2161
2694
|
]);
|
|
2162
|
-
|
|
2695
|
+
expect3(await store.runValueFacets({
|
|
2163
2696
|
field: "tag"
|
|
2164
2697
|
}, {}, {
|
|
2165
2698
|
limit: 1
|
|
@@ -2184,7 +2717,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2184
2717
|
id: "c",
|
|
2185
2718
|
namespace: "globex"
|
|
2186
2719
|
}));
|
|
2187
|
-
|
|
2720
|
+
expect3(await store.runValueFacets({
|
|
2188
2721
|
field: "namespace"
|
|
2189
2722
|
}, {}, {
|
|
2190
2723
|
limit: 1
|
|
@@ -2196,7 +2729,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2196
2729
|
]);
|
|
2197
2730
|
});
|
|
2198
2731
|
t("countRuns counts the same set listRuns returns, without the paging", async () => {
|
|
2199
|
-
|
|
2732
|
+
expect3(typeof store.countRuns).toBe("function");
|
|
2200
2733
|
await store.createRun(run({
|
|
2201
2734
|
id: "n1",
|
|
2202
2735
|
status: "running",
|
|
@@ -2226,22 +2759,22 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2226
2759
|
"q"
|
|
2227
2760
|
]
|
|
2228
2761
|
}));
|
|
2229
|
-
|
|
2230
|
-
|
|
2762
|
+
expect3(await store.countRuns?.({})).toBe(4);
|
|
2763
|
+
expect3(await store.countRuns?.({
|
|
2231
2764
|
namespace: "a"
|
|
2232
2765
|
})).toBe(3);
|
|
2233
|
-
|
|
2766
|
+
expect3(await store.countRuns?.({
|
|
2234
2767
|
namespace: "a",
|
|
2235
2768
|
statuses: [
|
|
2236
2769
|
"pending",
|
|
2237
2770
|
"running"
|
|
2238
2771
|
]
|
|
2239
2772
|
})).toBe(2);
|
|
2240
|
-
|
|
2773
|
+
expect3(await store.countRuns?.({
|
|
2241
2774
|
statuses: []
|
|
2242
2775
|
})).toBe(0);
|
|
2243
2776
|
if (supportsTagFilter) {
|
|
2244
|
-
|
|
2777
|
+
expect3(await store.countRuns?.({
|
|
2245
2778
|
tag: "q",
|
|
2246
2779
|
statuses: [
|
|
2247
2780
|
"running",
|
|
@@ -2268,28 +2801,28 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2268
2801
|
await engine.start("q-wait", {}, "q1", {
|
|
2269
2802
|
concurrency: quota
|
|
2270
2803
|
});
|
|
2271
|
-
|
|
2804
|
+
expect3((await engine.waitForRun("q1", {
|
|
2272
2805
|
timeoutMs: 2e4
|
|
2273
2806
|
})).status).toBe("suspended");
|
|
2274
|
-
await
|
|
2807
|
+
await expect3(engine.start("q-wait", {}, "q2", {
|
|
2275
2808
|
concurrency: quota
|
|
2276
2809
|
})).rejects.toThrow(/concurrency limit/);
|
|
2277
|
-
|
|
2810
|
+
expect3(await store.getRun("q2")).toBeNull();
|
|
2278
2811
|
await engine.start("q-wait", {}, "q3", {
|
|
2279
2812
|
concurrency: {
|
|
2280
2813
|
...quota,
|
|
2281
2814
|
key: "tenant:b"
|
|
2282
2815
|
}
|
|
2283
2816
|
});
|
|
2284
|
-
|
|
2817
|
+
expect3((await engine.waitForRun("q1", {
|
|
2285
2818
|
timeoutMs: 2e4
|
|
2286
2819
|
})).status).toBe("suspended");
|
|
2287
2820
|
await engine.cancel("q1");
|
|
2288
|
-
|
|
2821
|
+
expect3((await store.getRun("q1"))?.status).toBe("cancelled");
|
|
2289
2822
|
await engine.start("q-wait", {}, "q4", {
|
|
2290
2823
|
concurrency: quota
|
|
2291
2824
|
});
|
|
2292
|
-
|
|
2825
|
+
expect3(await store.getRun("q4")).not.toBeNull();
|
|
2293
2826
|
for (const id of [
|
|
2294
2827
|
"q3",
|
|
2295
2828
|
"q4"
|
|
@@ -2328,10 +2861,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2328
2861
|
...over
|
|
2329
2862
|
}), "schedule");
|
|
2330
2863
|
t("saves, reads back, replaces and deletes a schedule", async () => {
|
|
2331
|
-
|
|
2864
|
+
expect3(typeof store.saveSchedule).toBe("function");
|
|
2332
2865
|
await store.saveSchedule?.(schedule());
|
|
2333
2866
|
const loaded = await store.getSchedule?.("s1");
|
|
2334
|
-
|
|
2867
|
+
expect3(loaded).toMatchObject({
|
|
2335
2868
|
id: "s1",
|
|
2336
2869
|
namespace: "default",
|
|
2337
2870
|
workflow: "digest",
|
|
@@ -2357,27 +2890,27 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2357
2890
|
window: NOW + 6e4
|
|
2358
2891
|
}
|
|
2359
2892
|
});
|
|
2360
|
-
|
|
2893
|
+
expect3(loaded?.createdAt.getTime()).toBe(at.getTime());
|
|
2361
2894
|
await store.saveSchedule?.(schedule({
|
|
2362
2895
|
workflow: "digest2",
|
|
2363
2896
|
nextFireAt: null,
|
|
2364
2897
|
tags: void 0
|
|
2365
2898
|
}));
|
|
2366
2899
|
const replaced = await store.getSchedule?.("s1");
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2900
|
+
expect3(replaced?.workflow).toBe("digest2");
|
|
2901
|
+
expect3(replaced?.nextFireAt).toBeNull();
|
|
2902
|
+
expect3(replaced?.tags ?? []).toEqual([]);
|
|
2903
|
+
expect3(await store.getSchedule?.("nope")).toBeNull();
|
|
2904
|
+
expect3(await store.deleteSchedule?.("s1")).toBe(true);
|
|
2905
|
+
expect3(await store.deleteSchedule?.("s1")).toBe(false);
|
|
2906
|
+
expect3(await store.getSchedule?.("s1")).toBeNull();
|
|
2374
2907
|
});
|
|
2375
2908
|
t("updateSchedule patches, and compare-and-sets on nextFireAt", async () => {
|
|
2376
2909
|
await store.saveSchedule?.(schedule());
|
|
2377
|
-
|
|
2910
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2378
2911
|
paused: true
|
|
2379
2912
|
})).toBe(true);
|
|
2380
|
-
|
|
2913
|
+
expect3((await store.getSchedule?.("s1"))?.paused).toBe(true);
|
|
2381
2914
|
const next = {
|
|
2382
2915
|
nextFireAt: NOW + 12e4,
|
|
2383
2916
|
state: {
|
|
@@ -2385,24 +2918,24 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2385
2918
|
fires: 1
|
|
2386
2919
|
}
|
|
2387
2920
|
};
|
|
2388
|
-
|
|
2389
|
-
|
|
2921
|
+
expect3(await store.updateSchedule?.("s1", next, NOW + 6e4)).toBe(true);
|
|
2922
|
+
expect3(await store.updateSchedule?.("s1", next, NOW + 6e4)).toBe(false);
|
|
2390
2923
|
const after = await store.getSchedule?.("s1");
|
|
2391
|
-
|
|
2392
|
-
|
|
2924
|
+
expect3(after?.nextFireAt).toBe(NOW + 12e4);
|
|
2925
|
+
expect3(after?.state).toEqual({
|
|
2393
2926
|
window: NOW + 12e4,
|
|
2394
2927
|
fires: 1
|
|
2395
2928
|
});
|
|
2396
2929
|
await store.updateSchedule?.("s1", {
|
|
2397
2930
|
nextFireAt: null
|
|
2398
2931
|
});
|
|
2399
|
-
|
|
2932
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2400
2933
|
nextFireAt: NOW
|
|
2401
2934
|
}, NOW + 1)).toBe(false);
|
|
2402
|
-
|
|
2935
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2403
2936
|
nextFireAt: NOW
|
|
2404
2937
|
}, null)).toBe(true);
|
|
2405
|
-
|
|
2938
|
+
expect3(await store.updateSchedule?.("ghost", {
|
|
2406
2939
|
paused: true
|
|
2407
2940
|
})).toBe(false);
|
|
2408
2941
|
});
|
|
@@ -2434,47 +2967,47 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2434
2967
|
]
|
|
2435
2968
|
}));
|
|
2436
2969
|
const ids2 = /* @__PURE__ */ __name(async (q) => (await store.listSchedules?.(q) ?? []).map((r) => r.id), "ids");
|
|
2437
|
-
|
|
2970
|
+
expect3(await ids2({})).toEqual([
|
|
2438
2971
|
"c",
|
|
2439
2972
|
"b",
|
|
2440
2973
|
"a",
|
|
2441
2974
|
"d",
|
|
2442
2975
|
"e"
|
|
2443
2976
|
]);
|
|
2444
|
-
|
|
2977
|
+
expect3(await ids2({
|
|
2445
2978
|
dueBy: NOW
|
|
2446
2979
|
})).toEqual([
|
|
2447
2980
|
"b",
|
|
2448
2981
|
"a"
|
|
2449
2982
|
]);
|
|
2450
|
-
|
|
2983
|
+
expect3(await ids2({
|
|
2451
2984
|
dueBy: NOW,
|
|
2452
2985
|
namespace: "default"
|
|
2453
2986
|
})).toEqual([
|
|
2454
2987
|
"a"
|
|
2455
2988
|
]);
|
|
2456
|
-
|
|
2989
|
+
expect3(await ids2({
|
|
2457
2990
|
dueBy: NOW,
|
|
2458
2991
|
limit: 1
|
|
2459
2992
|
})).toEqual([
|
|
2460
2993
|
"b"
|
|
2461
2994
|
]);
|
|
2462
|
-
|
|
2995
|
+
expect3(await ids2({
|
|
2463
2996
|
workflow: "other"
|
|
2464
2997
|
})).toEqual([
|
|
2465
2998
|
"d"
|
|
2466
2999
|
]);
|
|
2467
|
-
|
|
3000
|
+
expect3(await ids2({
|
|
2468
3001
|
paused: true
|
|
2469
3002
|
})).toEqual([
|
|
2470
3003
|
"c"
|
|
2471
3004
|
]);
|
|
2472
|
-
if (supportsTagFilter)
|
|
3005
|
+
if (supportsTagFilter) expect3(await ids2({
|
|
2473
3006
|
tag: "tenant:b"
|
|
2474
3007
|
})).toEqual([
|
|
2475
3008
|
"e"
|
|
2476
3009
|
]);
|
|
2477
|
-
|
|
3010
|
+
expect3(await ids2({
|
|
2478
3011
|
limit: 2,
|
|
2479
3012
|
offset: 1
|
|
2480
3013
|
})).toEqual([
|
|
@@ -2495,18 +3028,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2495
3028
|
id: "new",
|
|
2496
3029
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:02.000Z")
|
|
2497
3030
|
}));
|
|
2498
|
-
|
|
3031
|
+
expect3((await store.listRuns({})).map((r) => r.id)).toEqual([
|
|
2499
3032
|
"new",
|
|
2500
3033
|
"mid",
|
|
2501
3034
|
"old"
|
|
2502
3035
|
]);
|
|
2503
|
-
|
|
3036
|
+
expect3((await store.listRuns({
|
|
2504
3037
|
limit: 2
|
|
2505
3038
|
})).map((r) => r.id)).toEqual([
|
|
2506
3039
|
"new",
|
|
2507
3040
|
"mid"
|
|
2508
3041
|
]);
|
|
2509
|
-
|
|
3042
|
+
expect3((await store.listRuns({
|
|
2510
3043
|
limit: 2,
|
|
2511
3044
|
offset: 1
|
|
2512
3045
|
})).map((r) => r.id)).toEqual([
|
|
@@ -2536,11 +3069,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2536
3069
|
tier: "pro"
|
|
2537
3070
|
}
|
|
2538
3071
|
}));
|
|
2539
|
-
|
|
3072
|
+
expect3((await store.getRun("b"))?.searchAttributes).toEqual({
|
|
2540
3073
|
amount: 200,
|
|
2541
3074
|
tier: "pro"
|
|
2542
3075
|
});
|
|
2543
|
-
|
|
3076
|
+
expect3((await store.listRuns({
|
|
2544
3077
|
attributes: [
|
|
2545
3078
|
{
|
|
2546
3079
|
key: "amount",
|
|
@@ -2552,7 +3085,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2552
3085
|
"b",
|
|
2553
3086
|
"c"
|
|
2554
3087
|
]);
|
|
2555
|
-
|
|
3088
|
+
expect3((await store.listRuns({
|
|
2556
3089
|
attributes: [
|
|
2557
3090
|
{
|
|
2558
3091
|
key: "tier",
|
|
@@ -2568,7 +3101,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2568
3101
|
})).map((r) => r.id)).toEqual([
|
|
2569
3102
|
"b"
|
|
2570
3103
|
]);
|
|
2571
|
-
|
|
3104
|
+
expect3((await store.listRuns({
|
|
2572
3105
|
attributes: [
|
|
2573
3106
|
{
|
|
2574
3107
|
key: "tier",
|
|
@@ -2600,7 +3133,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2600
3133
|
other: 1
|
|
2601
3134
|
}
|
|
2602
3135
|
}));
|
|
2603
|
-
|
|
3136
|
+
expect3((await store.listRuns({
|
|
2604
3137
|
attributes: [
|
|
2605
3138
|
{
|
|
2606
3139
|
key: "vip",
|
|
@@ -2611,7 +3144,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2611
3144
|
})).map((r) => r.id)).toEqual([
|
|
2612
3145
|
"a"
|
|
2613
3146
|
]);
|
|
2614
|
-
|
|
3147
|
+
expect3((await store.listRuns({
|
|
2615
3148
|
attributes: [
|
|
2616
3149
|
{
|
|
2617
3150
|
key: "vip",
|
|
@@ -2638,7 +3171,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2638
3171
|
"lt",
|
|
2639
3172
|
"lte"
|
|
2640
3173
|
]) {
|
|
2641
|
-
|
|
3174
|
+
expect3(await store.listRuns({
|
|
2642
3175
|
attributes: [
|
|
2643
3176
|
{
|
|
2644
3177
|
key: "missing",
|
|
@@ -2657,7 +3190,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2657
3190
|
amount: 10
|
|
2658
3191
|
}
|
|
2659
3192
|
}));
|
|
2660
|
-
|
|
3193
|
+
expect3((await store.listRuns({
|
|
2661
3194
|
attributes: [
|
|
2662
3195
|
{
|
|
2663
3196
|
key: "tier",
|
|
@@ -2673,7 +3206,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2673
3206
|
tier: "pro"
|
|
2674
3207
|
}
|
|
2675
3208
|
});
|
|
2676
|
-
|
|
3209
|
+
expect3((await store.listRuns({
|
|
2677
3210
|
attributes: [
|
|
2678
3211
|
{
|
|
2679
3212
|
key: "tier",
|
|
@@ -2684,7 +3217,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2684
3217
|
})).map((r) => r.id)).toEqual([
|
|
2685
3218
|
"a"
|
|
2686
3219
|
]);
|
|
2687
|
-
|
|
3220
|
+
expect3(await store.listRuns({
|
|
2688
3221
|
attributes: [
|
|
2689
3222
|
{
|
|
2690
3223
|
key: "tier",
|
|
@@ -2693,7 +3226,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2693
3226
|
}
|
|
2694
3227
|
]
|
|
2695
3228
|
})).toHaveLength(0);
|
|
2696
|
-
|
|
3229
|
+
expect3(await store.listRuns({
|
|
2697
3230
|
attributes: [
|
|
2698
3231
|
{
|
|
2699
3232
|
key: "amount",
|
|
@@ -2736,7 +3269,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2736
3269
|
],
|
|
2737
3270
|
limit: 10
|
|
2738
3271
|
});
|
|
2739
|
-
|
|
3272
|
+
expect3(res.map((r) => r.id)).toEqual([
|
|
2740
3273
|
"a"
|
|
2741
3274
|
]);
|
|
2742
3275
|
});
|
|
@@ -2756,12 +3289,12 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2756
3289
|
runId: "r3",
|
|
2757
3290
|
seq: 5
|
|
2758
3291
|
});
|
|
2759
|
-
|
|
3292
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.token).sort()).toEqual([
|
|
2760
3293
|
"approve-1",
|
|
2761
3294
|
"approve-2"
|
|
2762
3295
|
]);
|
|
2763
|
-
|
|
2764
|
-
|
|
3296
|
+
expect3((await store.takeSignalWaiter("approve-1"))?.seq).toBe(3);
|
|
3297
|
+
expect3(await store.takeSignalWaiter("approve-1")).toBeNull();
|
|
2765
3298
|
});
|
|
2766
3299
|
t("round-trips a signal waiter parallelGroup (child-fan tag) through put \u2192 take \u2192 list", async () => {
|
|
2767
3300
|
await store.putSignalWaiter({
|
|
@@ -2776,13 +3309,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2776
3309
|
seq: 9
|
|
2777
3310
|
});
|
|
2778
3311
|
const listed = await store.listSignalWaiters("child:");
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
3312
|
+
expect3(listed.find((w) => w.token === "child:r.child.0")?.parallelGroup).toBe("gather:0");
|
|
3313
|
+
expect3(listed.find((w) => w.token === "child:r.child.9")?.parallelGroup).toBeUndefined();
|
|
3314
|
+
expect3((await store.takeSignalWaiter("child:r.child.0"))?.parallelGroup).toBe("gather:0");
|
|
3315
|
+
expect3((await store.takeSignalWaiter("child:r.child.9"))?.parallelGroup).toBeUndefined();
|
|
2783
3316
|
});
|
|
2784
3317
|
t("registerChildWaits writes every waiter and placeholder, and upserts on repeat", async () => {
|
|
2785
|
-
|
|
3318
|
+
expect3(store.registerChildWaits).toBeTypeOf("function");
|
|
2786
3319
|
await store.createRun(run({
|
|
2787
3320
|
id: "p"
|
|
2788
3321
|
}));
|
|
@@ -2808,24 +3341,24 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2808
3341
|
}, (_, i) => entry(i, "running"));
|
|
2809
3342
|
await store.registerChildWaits?.(entries);
|
|
2810
3343
|
const waiters = await store.listSignalWaiters("child:p.all.0.");
|
|
2811
|
-
|
|
2812
|
-
|
|
3344
|
+
expect3(waiters).toHaveLength(250);
|
|
3345
|
+
expect3(waiters.find((w) => w.token === "child:p.all.0.7")).toEqual(entries[7]?.waiter);
|
|
2813
3346
|
const cps = await store.listCheckpoints("p");
|
|
2814
|
-
|
|
2815
|
-
|
|
3347
|
+
expect3(cps).toHaveLength(250);
|
|
3348
|
+
expect3(cps[7]).toMatchObject({
|
|
2816
3349
|
name: "signal:child:p.all.0.7",
|
|
2817
3350
|
status: "running"
|
|
2818
3351
|
});
|
|
2819
|
-
|
|
3352
|
+
expect3(cps[7]?.parallelGroup).toBe("all:0");
|
|
2820
3353
|
await store.registerChildWaits?.([
|
|
2821
3354
|
entry(7, "completed")
|
|
2822
3355
|
]);
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
await
|
|
3356
|
+
expect3((await store.getCheckpoint("p", 7))?.status).toBe("completed");
|
|
3357
|
+
expect3(await store.listCheckpoints("p")).toHaveLength(250);
|
|
3358
|
+
await expect3(store.registerChildWaits?.([])).resolves.toBeUndefined();
|
|
2826
3359
|
});
|
|
2827
3360
|
t("compactCheckpoints replaces a seq range with one checkpoint, atomically", async () => {
|
|
2828
|
-
|
|
3361
|
+
expect3(store.compactCheckpoints).toBeTypeOf("function");
|
|
2829
3362
|
await store.createRun(run({
|
|
2830
3363
|
id: "r"
|
|
2831
3364
|
}));
|
|
@@ -2864,18 +3397,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2864
3397
|
});
|
|
2865
3398
|
await store.compactCheckpoints?.("r", 2, 7, into);
|
|
2866
3399
|
const left = await store.listCheckpoints("r");
|
|
2867
|
-
|
|
3400
|
+
expect3(left.map((cp2) => cp2.seq)).toEqual([
|
|
2868
3401
|
0,
|
|
2869
3402
|
1,
|
|
2870
3403
|
2,
|
|
2871
3404
|
8,
|
|
2872
3405
|
9
|
|
2873
3406
|
]);
|
|
2874
|
-
|
|
3407
|
+
expect3(left[2]).toMatchObject({
|
|
2875
3408
|
name: "fan:r.all.2.",
|
|
2876
3409
|
parallelGroup: "all:2"
|
|
2877
3410
|
});
|
|
2878
|
-
|
|
3411
|
+
expect3(left[2]?.output).toEqual({
|
|
2879
3412
|
from: 0,
|
|
2880
3413
|
values: [
|
|
2881
3414
|
{
|
|
@@ -2885,10 +3418,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2885
3418
|
null
|
|
2886
3419
|
]
|
|
2887
3420
|
});
|
|
2888
|
-
|
|
3421
|
+
expect3(await store.listCheckpoints("other")).toHaveLength(1);
|
|
2889
3422
|
});
|
|
2890
3423
|
t("listSignalWaitersByRun returns only the given runs' waiters", async () => {
|
|
2891
|
-
|
|
3424
|
+
expect3(store.listSignalWaitersByRun).toBeTypeOf("function");
|
|
2892
3425
|
await store.putSignalWaiter({
|
|
2893
3426
|
token: "child:a.all.0.0",
|
|
2894
3427
|
runId: "a",
|
|
@@ -2911,7 +3444,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2911
3444
|
seq: 0
|
|
2912
3445
|
});
|
|
2913
3446
|
const tokens = /* @__PURE__ */ __name(async (ids2) => (await store.listSignalWaitersByRun?.(ids2) ?? []).map((w) => w.token).sort(), "tokens");
|
|
2914
|
-
|
|
3447
|
+
expect3(await tokens([
|
|
2915
3448
|
"a",
|
|
2916
3449
|
"b"
|
|
2917
3450
|
])).toEqual([
|
|
@@ -2919,11 +3452,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2919
3452
|
"approve-b",
|
|
2920
3453
|
"child:a.all.0.0"
|
|
2921
3454
|
]);
|
|
2922
|
-
|
|
3455
|
+
expect3(await tokens([
|
|
2923
3456
|
"missing"
|
|
2924
3457
|
])).toEqual([]);
|
|
2925
|
-
|
|
2926
|
-
|
|
3458
|
+
expect3(await tokens([])).toEqual([]);
|
|
3459
|
+
expect3((await store.listSignalWaitersByRun?.([
|
|
2927
3460
|
"a"
|
|
2928
3461
|
]))?.find((w) => w.seq === 0)).toEqual({
|
|
2929
3462
|
token: "child:a.all.0.0",
|
|
@@ -2933,7 +3466,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2933
3466
|
});
|
|
2934
3467
|
});
|
|
2935
3468
|
t("hasSignalWaiter answers by run and exact token prefix", async () => {
|
|
2936
|
-
|
|
3469
|
+
expect3(store.hasSignalWaiter).toBeTypeOf("function");
|
|
2937
3470
|
const has = /* @__PURE__ */ __name((runId, prefix) => store.hasSignalWaiter?.(runId, prefix), "has");
|
|
2938
3471
|
await store.putSignalWaiter({
|
|
2939
3472
|
token: "child:p_1.all.3.0",
|
|
@@ -2951,13 +3484,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2951
3484
|
runId: "p_1",
|
|
2952
3485
|
seq: 9
|
|
2953
3486
|
});
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3487
|
+
expect3(await has("p_1", "child:p_1.all.3.")).toBe(true);
|
|
3488
|
+
expect3(await has("p_1", "child:p_1.all.4.")).toBe(false);
|
|
3489
|
+
expect3(await has("other", "child:p_1.all.3.")).toBe(false);
|
|
3490
|
+
expect3(await has("p_1", "child:p%.all.3.")).toBe(false);
|
|
3491
|
+
expect3(await has("p_1", "CHILD:p_1.all.3.")).toBe(false);
|
|
2959
3492
|
await store.takeSignalWaiter("child:p_1.all.3.0");
|
|
2960
|
-
|
|
3493
|
+
expect3(await has("p_1", "child:p_1.all.3.")).toBe(false);
|
|
2961
3494
|
});
|
|
2962
3495
|
t("removeSignalWaiter deletes only the exact (token, runId, seq) row, never a different run's", async () => {
|
|
2963
3496
|
await store.putSignalWaiter({
|
|
@@ -2975,7 +3508,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2975
3508
|
runId: "r1",
|
|
2976
3509
|
seq: 3
|
|
2977
3510
|
});
|
|
2978
|
-
|
|
3511
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.runId)).toEqual([
|
|
2979
3512
|
"r2"
|
|
2980
3513
|
]);
|
|
2981
3514
|
await store.removeSignalWaiter({
|
|
@@ -2983,7 +3516,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2983
3516
|
runId: "r2",
|
|
2984
3517
|
seq: 999
|
|
2985
3518
|
});
|
|
2986
|
-
|
|
3519
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.runId)).toEqual([
|
|
2987
3520
|
"r2"
|
|
2988
3521
|
]);
|
|
2989
3522
|
await store.removeSignalWaiter({
|
|
@@ -2991,7 +3524,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2991
3524
|
runId: "r2",
|
|
2992
3525
|
seq: 7
|
|
2993
3526
|
});
|
|
2994
|
-
|
|
3527
|
+
expect3(await store.listSignalWaiters("approve-")).toEqual([]);
|
|
2995
3528
|
await store.removeSignalWaiter({
|
|
2996
3529
|
token: "never-registered",
|
|
2997
3530
|
runId: "r1",
|
|
@@ -3008,18 +3541,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3008
3541
|
await store.bufferSignal("other", {
|
|
3009
3542
|
n: 9
|
|
3010
3543
|
});
|
|
3011
|
-
|
|
3544
|
+
expect3(await store.takeBufferedSignal("sig")).toEqual({
|
|
3012
3545
|
payload: {
|
|
3013
3546
|
n: 1
|
|
3014
3547
|
}
|
|
3015
3548
|
});
|
|
3016
|
-
|
|
3549
|
+
expect3(await store.takeBufferedSignal("sig")).toEqual({
|
|
3017
3550
|
payload: {
|
|
3018
3551
|
n: 2
|
|
3019
3552
|
}
|
|
3020
3553
|
});
|
|
3021
|
-
|
|
3022
|
-
|
|
3554
|
+
expect3(await store.takeBufferedSignal("sig")).toBeNull();
|
|
3555
|
+
expect3(await store.takeBufferedSignal("other")).toEqual({
|
|
3023
3556
|
payload: {
|
|
3024
3557
|
n: 9
|
|
3025
3558
|
}
|
|
@@ -3051,7 +3584,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3051
3584
|
publishedAt: 50
|
|
3052
3585
|
});
|
|
3053
3586
|
const listed = await store.listBufferedEvents("order.paid", 10);
|
|
3054
|
-
|
|
3587
|
+
expect3(listed.map((e) => e.payload)).toEqual([
|
|
3055
3588
|
{
|
|
3056
3589
|
n: 1
|
|
3057
3590
|
},
|
|
@@ -3059,13 +3592,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3059
3592
|
n: 2
|
|
3060
3593
|
}
|
|
3061
3594
|
]);
|
|
3062
|
-
|
|
3595
|
+
expect3(listed.map((e) => e.id)).toEqual([
|
|
3063
3596
|
"e1",
|
|
3064
3597
|
"e2"
|
|
3065
3598
|
]);
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3599
|
+
expect3(listed[0]?.publishedAt).toBe(100);
|
|
3600
|
+
expect3(await store.listBufferedEvents("order.paid", 10)).toHaveLength(2);
|
|
3601
|
+
expect3(await store.listBufferedEvents("other.event", 10)).toEqual([
|
|
3069
3602
|
{
|
|
3070
3603
|
id: "e3",
|
|
3071
3604
|
payload: {
|
|
@@ -3094,7 +3627,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3094
3627
|
id: "c",
|
|
3095
3628
|
publishedAt: 3
|
|
3096
3629
|
});
|
|
3097
|
-
|
|
3630
|
+
expect3(await store.listBufferedEvents("evt", 2)).toHaveLength(2);
|
|
3098
3631
|
});
|
|
3099
3632
|
t("removeBufferedEvent atomically deletes by id \u2014 true iff a row existed, false on a repeat/absent id", async () => {
|
|
3100
3633
|
await store.bufferEvent({
|
|
@@ -3105,10 +3638,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3105
3638
|
id: "x1",
|
|
3106
3639
|
publishedAt: 1
|
|
3107
3640
|
});
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3641
|
+
expect3(await store.removeBufferedEvent("x1")).toBe(true);
|
|
3642
|
+
expect3(await store.listBufferedEvents("evt", 10)).toEqual([]);
|
|
3643
|
+
expect3(await store.removeBufferedEvent("x1")).toBe(false);
|
|
3644
|
+
expect3(await store.removeBufferedEvent("never-buffered")).toBe(false);
|
|
3112
3645
|
});
|
|
3113
3646
|
t("transaction commits the checkpoint atomically and returns the work result", async () => {
|
|
3114
3647
|
if (!store.transaction) return;
|
|
@@ -3124,10 +3657,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3124
3657
|
}));
|
|
3125
3658
|
return "ok";
|
|
3126
3659
|
});
|
|
3127
|
-
|
|
3128
|
-
const
|
|
3129
|
-
|
|
3130
|
-
|
|
3660
|
+
expect3(result).toBe("ok");
|
|
3661
|
+
const cp2 = await store.getCheckpoint("r1", 7);
|
|
3662
|
+
expect3(cp2?.name).toBe("tx-step");
|
|
3663
|
+
expect3(cp2?.output).toEqual({
|
|
3131
3664
|
paid: true
|
|
3132
3665
|
});
|
|
3133
3666
|
});
|
|
@@ -3154,11 +3687,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3154
3687
|
await engine.start("wf", {
|
|
3155
3688
|
x: 1
|
|
3156
3689
|
}, "run1");
|
|
3157
|
-
|
|
3690
|
+
expect3((await engine.waitForRun("run1")).status).toBe("failed");
|
|
3158
3691
|
const resumed = await engine.resume("run1");
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3692
|
+
expect3(resumed.status).toBe("completed");
|
|
3693
|
+
expect3(resumed.output).toBe(15);
|
|
3694
|
+
expect3(aRuns).toBe(1);
|
|
3162
3695
|
});
|
|
3163
3696
|
t("starts the version the caller pinned, and refuses one that is not registered", async () => {
|
|
3164
3697
|
const engine = new WorkflowEngine3({
|
|
@@ -3179,19 +3712,19 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3179
3712
|
const pinned = await engine.waitForRun("pinned-run", {
|
|
3180
3713
|
timeoutMs: 2e4
|
|
3181
3714
|
});
|
|
3182
|
-
|
|
3183
|
-
|
|
3715
|
+
expect3(pinned.output).toBe("v1-output");
|
|
3716
|
+
expect3(ran).toEqual([
|
|
3184
3717
|
"v1"
|
|
3185
3718
|
]);
|
|
3186
|
-
|
|
3719
|
+
expect3((await store.getRun("pinned-run"))?.workflowVersion).toBe("1");
|
|
3187
3720
|
await engine.start("vwf", {}, "newest-run");
|
|
3188
|
-
|
|
3721
|
+
expect3((await engine.waitForRun("newest-run", {
|
|
3189
3722
|
timeoutMs: 2e4
|
|
3190
3723
|
})).output).toBe("v2-output");
|
|
3191
|
-
await
|
|
3724
|
+
await expect3(engine.start("vwf", {}, "ghost-run", {
|
|
3192
3725
|
version: "9"
|
|
3193
3726
|
})).rejects.toThrow(/vwf@9 is not registered/);
|
|
3194
|
-
|
|
3727
|
+
expect3(await store.getRun("ghost-run")).toBeNull();
|
|
3195
3728
|
});
|
|
3196
3729
|
t("cancels a timed-out run\u2019s live child subtree, not just the run itself", async () => {
|
|
3197
3730
|
const engine = new WorkflowEngine3({
|
|
@@ -3214,16 +3747,16 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3214
3747
|
"tc",
|
|
3215
3748
|
"tgc"
|
|
3216
3749
|
]) {
|
|
3217
|
-
|
|
3750
|
+
expect3((await engine.waitForRun(id, {
|
|
3218
3751
|
timeoutMs: 2e4
|
|
3219
3752
|
})).status).toBe("suspended");
|
|
3220
3753
|
}
|
|
3221
3754
|
await engine.sweepTimeouts(Date.now() + 37e5);
|
|
3222
3755
|
const parent = await store.getRun("tp");
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3756
|
+
expect3(parent?.status).toBe("cancelled");
|
|
3757
|
+
expect3(parent?.error?.code).toBe("execution_timeout");
|
|
3758
|
+
expect3((await store.getRun("tc"))?.status).toBe("cancelled");
|
|
3759
|
+
expect3((await store.getRun("tgc"))?.status).toBe("cancelled");
|
|
3227
3760
|
});
|
|
3228
3761
|
});
|
|
3229
3762
|
}
|
|
@@ -3295,6 +3828,7 @@ export {
|
|
|
3295
3828
|
DURABLE_CANONICAL_COLUMNS,
|
|
3296
3829
|
MutableClock,
|
|
3297
3830
|
StateStoreUnavailableError,
|
|
3831
|
+
aggregationFixture,
|
|
3298
3832
|
assertDurableColumns,
|
|
3299
3833
|
assertOutput,
|
|
3300
3834
|
assertReplayable,
|
|
@@ -3307,6 +3841,7 @@ export {
|
|
|
3307
3841
|
failTimes,
|
|
3308
3842
|
preserveColumnExpectation,
|
|
3309
3843
|
recordedSteps,
|
|
3844
|
+
registerCheckpointAggregationCases,
|
|
3310
3845
|
runAdmissionBackendContract,
|
|
3311
3846
|
runStateStoreContract
|
|
3312
3847
|
};
|