@dudousxd/nestjs-durable-testing 0.16.2 → 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 +879 -318
- 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,17 +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 () => {
|
|
1433
|
+
expect3(store.countCheckpoints).toBeDefined();
|
|
1088
1434
|
if (!store.countCheckpoints) return;
|
|
1089
1435
|
await store.createRun(run());
|
|
1090
1436
|
for (const [seq, status] of [
|
|
@@ -1124,19 +1470,33 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1124
1470
|
status: "pending",
|
|
1125
1471
|
stepId: "other:2"
|
|
1126
1472
|
}));
|
|
1127
|
-
|
|
1473
|
+
expect3(await store.countCheckpoints("r1", 1, 3, [
|
|
1128
1474
|
"pending"
|
|
1129
1475
|
])).toBe(2);
|
|
1130
|
-
|
|
1476
|
+
expect3(await store.countCheckpoints("r1", 0, 4, [
|
|
1131
1477
|
"pending",
|
|
1132
1478
|
"failed"
|
|
1133
1479
|
])).toBe(4);
|
|
1134
|
-
|
|
1480
|
+
expect3(await store.countCheckpoints("r1", 2, 2, [
|
|
1481
|
+
"pending"
|
|
1482
|
+
])).toBe(1);
|
|
1483
|
+
expect3(await store.countCheckpoints("r1", 1, 4, [
|
|
1484
|
+
"pending"
|
|
1485
|
+
])).toBe(3);
|
|
1486
|
+
expect3(await store.countCheckpoints("r1", 0, 0, [
|
|
1487
|
+
"completed"
|
|
1488
|
+
])).toBe(1);
|
|
1489
|
+
expect3(await store.countCheckpoints("other", 0, 4, [
|
|
1490
|
+
"pending"
|
|
1491
|
+
])).toBe(1);
|
|
1492
|
+
expect3(await store.countCheckpoints("r1", 5, 9, [
|
|
1135
1493
|
"pending"
|
|
1136
1494
|
])).toBe(0);
|
|
1137
|
-
|
|
1495
|
+
expect3(await store.countCheckpoints("r1", 0, 4, [])).toBe(0);
|
|
1138
1496
|
});
|
|
1497
|
+
registerCheckpointAggregationCases(t, () => store, run);
|
|
1139
1498
|
t("saveCheckpointIfPending writes over a pending row only", async () => {
|
|
1499
|
+
expect3(store.saveCheckpointIfPending).toBeDefined();
|
|
1140
1500
|
if (!store.saveCheckpointIfPending) return;
|
|
1141
1501
|
await store.createRun(run());
|
|
1142
1502
|
const pending = checkpoint({
|
|
@@ -1144,11 +1504,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1144
1504
|
output: void 0
|
|
1145
1505
|
});
|
|
1146
1506
|
await store.saveCheckpoint(pending);
|
|
1147
|
-
|
|
1507
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1148
1508
|
...pending,
|
|
1149
1509
|
wakeAt: 5e3
|
|
1150
1510
|
})).toBe(true);
|
|
1151
|
-
|
|
1511
|
+
expect3((await store.getCheckpoint("r1", 0))?.wakeAt).toBe(5e3);
|
|
1152
1512
|
const done = {
|
|
1153
1513
|
...pending,
|
|
1154
1514
|
status: "completed",
|
|
@@ -1156,21 +1516,190 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1156
1516
|
ok: true
|
|
1157
1517
|
}
|
|
1158
1518
|
};
|
|
1159
|
-
|
|
1160
|
-
|
|
1519
|
+
expect3(await store.saveCheckpointIfPending(done)).toBe(true);
|
|
1520
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1161
1521
|
...pending,
|
|
1162
1522
|
wakeAt: 9e3
|
|
1163
1523
|
})).toBe(false);
|
|
1164
1524
|
const after = await store.getCheckpoint("r1", 0);
|
|
1165
|
-
|
|
1166
|
-
|
|
1525
|
+
expect3(after?.status).toBe("completed");
|
|
1526
|
+
expect3(after?.output).toEqual({
|
|
1167
1527
|
ok: true
|
|
1168
1528
|
});
|
|
1169
|
-
|
|
1529
|
+
expect3(after?.wakeAt).toBeUndefined();
|
|
1530
|
+
await store.saveCheckpoint(checkpoint({
|
|
1531
|
+
seq: 1,
|
|
1532
|
+
status: "failed",
|
|
1533
|
+
stepId: "r1:1"
|
|
1534
|
+
}));
|
|
1535
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1536
|
+
...pending,
|
|
1537
|
+
seq: 1,
|
|
1538
|
+
stepId: "r1:1"
|
|
1539
|
+
})).toBe(false);
|
|
1540
|
+
expect3((await store.getCheckpoint("r1", 1))?.status).toBe("failed");
|
|
1541
|
+
expect3(await store.saveCheckpointIfPending({
|
|
1170
1542
|
...pending,
|
|
1171
1543
|
seq: 9
|
|
1172
1544
|
})).toBe(false);
|
|
1173
|
-
|
|
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
|
+
});
|
|
1174
1703
|
});
|
|
1175
1704
|
t("upserts checkpoints, reads them by (runId, seq), and lists them ordered by seq", async () => {
|
|
1176
1705
|
await store.createRun(run());
|
|
@@ -1188,13 +1717,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1188
1717
|
again: true
|
|
1189
1718
|
}
|
|
1190
1719
|
}));
|
|
1191
|
-
|
|
1720
|
+
expect3((await store.getCheckpoint("r1", 0))?.output).toEqual({
|
|
1192
1721
|
ok: true,
|
|
1193
1722
|
again: true
|
|
1194
1723
|
});
|
|
1195
|
-
|
|
1724
|
+
expect3((await store.getCheckpoint("r1", 1))?.name).toBe("charge");
|
|
1196
1725
|
const list = await store.listCheckpoints("r1");
|
|
1197
|
-
|
|
1726
|
+
expect3(list.map((c) => c.seq)).toEqual([
|
|
1198
1727
|
0,
|
|
1199
1728
|
1
|
|
1200
1729
|
]);
|
|
@@ -1230,14 +1759,14 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1230
1759
|
}
|
|
1231
1760
|
}));
|
|
1232
1761
|
const getLatestCheckpointByName = store.getLatestCheckpointByName;
|
|
1233
|
-
|
|
1762
|
+
expect3(getLatestCheckpointByName).toBeDefined();
|
|
1234
1763
|
if (!getLatestCheckpointByName) return;
|
|
1235
1764
|
const latest = await getLatestCheckpointByName.call(store, "r1", "event:progress");
|
|
1236
|
-
|
|
1237
|
-
|
|
1765
|
+
expect3(latest?.seq).toBe(3);
|
|
1766
|
+
expect3(latest?.output).toEqual({
|
|
1238
1767
|
pct: 90
|
|
1239
1768
|
});
|
|
1240
|
-
|
|
1769
|
+
expect3(await getLatestCheckpointByName.call(store, "r1", "event:missing")).toBeUndefined();
|
|
1241
1770
|
});
|
|
1242
1771
|
t("listCheckpointsByNamePrefix returns prefix matches ordered by seq (empty => none)", async () => {
|
|
1243
1772
|
await store.createRun(run());
|
|
@@ -1264,18 +1793,48 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1264
1793
|
output: "child-b"
|
|
1265
1794
|
}));
|
|
1266
1795
|
const listCheckpointsByNamePrefix = store.listCheckpointsByNamePrefix;
|
|
1267
|
-
|
|
1796
|
+
expect3(listCheckpointsByNamePrefix).toBeDefined();
|
|
1268
1797
|
if (!listCheckpointsByNamePrefix) return;
|
|
1269
1798
|
const matches = await listCheckpointsByNamePrefix.call(store, "r1", [
|
|
1270
1799
|
"signal:child:",
|
|
1271
1800
|
"spawn:"
|
|
1272
1801
|
]);
|
|
1273
|
-
|
|
1802
|
+
expect3(matches.map((c) => c.name)).toEqual([
|
|
1274
1803
|
"spawn:0",
|
|
1275
1804
|
"signal:child:c2",
|
|
1276
1805
|
"spawn:1"
|
|
1277
1806
|
]);
|
|
1278
|
-
|
|
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
|
+
]);
|
|
1279
1838
|
});
|
|
1280
1839
|
t("round-trips checkpoint events and reads back a missing checkpoint as null", async () => {
|
|
1281
1840
|
await store.createRun(run());
|
|
@@ -1290,7 +1849,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1290
1849
|
}
|
|
1291
1850
|
]
|
|
1292
1851
|
}));
|
|
1293
|
-
|
|
1852
|
+
expect3((await store.getCheckpoint("r1", 0))?.events).toEqual([
|
|
1294
1853
|
{
|
|
1295
1854
|
at: 1,
|
|
1296
1855
|
level: "error",
|
|
@@ -1299,7 +1858,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1299
1858
|
status: "failed"
|
|
1300
1859
|
}
|
|
1301
1860
|
]);
|
|
1302
|
-
|
|
1861
|
+
expect3(await store.getCheckpoint("r1", 99)).toBeNull();
|
|
1303
1862
|
});
|
|
1304
1863
|
t("lists incomplete runs, pending runs (FIFO), and due timers", async () => {
|
|
1305
1864
|
await store.createRun(run({
|
|
@@ -1325,20 +1884,20 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1325
1884
|
status: "pending",
|
|
1326
1885
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:01.000Z")
|
|
1327
1886
|
}));
|
|
1328
|
-
|
|
1887
|
+
expect3((await store.listIncompleteRuns()).map((r) => r.id)).toEqual([
|
|
1329
1888
|
"running1"
|
|
1330
1889
|
]);
|
|
1331
|
-
|
|
1890
|
+
expect3((await store.listPendingRuns(10)).map((r) => r.id)).toEqual([
|
|
1332
1891
|
"pending1",
|
|
1333
1892
|
"pending2"
|
|
1334
1893
|
]);
|
|
1335
|
-
|
|
1894
|
+
expect3((await store.listPendingRuns(1)).map((r) => r.id)).toEqual([
|
|
1336
1895
|
"pending1"
|
|
1337
1896
|
]);
|
|
1338
|
-
|
|
1897
|
+
expect3((await store.listDueTimers(1e4)).map((r) => r.id)).toEqual([
|
|
1339
1898
|
"suspended1"
|
|
1340
1899
|
]);
|
|
1341
|
-
|
|
1900
|
+
expect3(await store.listDueTimers(1e3)).toHaveLength(0);
|
|
1342
1901
|
});
|
|
1343
1902
|
t("keeps a worker inside its own namespace on every path that picks work up", async () => {
|
|
1344
1903
|
await store.createRun(run({
|
|
@@ -1373,33 +1932,33 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1373
1932
|
wakeAt: 5e3,
|
|
1374
1933
|
namespace: "beta"
|
|
1375
1934
|
}));
|
|
1376
|
-
|
|
1935
|
+
expect3((await store.listPendingRuns(10, "alpha")).map((r) => r.id)).toEqual([
|
|
1377
1936
|
"a-pending"
|
|
1378
1937
|
]);
|
|
1379
|
-
|
|
1938
|
+
expect3((await store.listIncompleteRuns("alpha")).map((r) => r.id)).toEqual([
|
|
1380
1939
|
"a-running"
|
|
1381
1940
|
]);
|
|
1382
|
-
|
|
1941
|
+
expect3((await store.listDueTimers(1e4, "alpha")).map((r) => r.id)).toEqual([
|
|
1383
1942
|
"a-timer"
|
|
1384
1943
|
]);
|
|
1385
|
-
|
|
1944
|
+
expect3((await store.listRuns({
|
|
1386
1945
|
namespace: "alpha",
|
|
1387
1946
|
status: "running"
|
|
1388
1947
|
})).map((r) => r.id)).toEqual([
|
|
1389
1948
|
"a-running"
|
|
1390
1949
|
]);
|
|
1391
|
-
|
|
1950
|
+
expect3((await store.listPendingRuns(1, "alpha")).map((r) => r.id)).toEqual([
|
|
1392
1951
|
"a-pending"
|
|
1393
1952
|
]);
|
|
1394
|
-
|
|
1953
|
+
expect3((await store.listPendingRuns(10)).map((r) => r.id).sort()).toEqual([
|
|
1395
1954
|
"a-pending",
|
|
1396
1955
|
"b-pending"
|
|
1397
1956
|
]);
|
|
1398
|
-
|
|
1957
|
+
expect3((await store.listIncompleteRuns()).map((r) => r.id).sort()).toEqual([
|
|
1399
1958
|
"a-running",
|
|
1400
1959
|
"b-running"
|
|
1401
1960
|
]);
|
|
1402
|
-
|
|
1961
|
+
expect3((await store.listDueTimers(1e4)).map((r) => r.id).sort()).toEqual([
|
|
1403
1962
|
"a-timer",
|
|
1404
1963
|
"b-timer"
|
|
1405
1964
|
]);
|
|
@@ -1408,20 +1967,20 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1408
1967
|
await store.createRun(run({
|
|
1409
1968
|
id: "r1"
|
|
1410
1969
|
}));
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
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);
|
|
1414
1973
|
await store.releaseRunLock("r1");
|
|
1415
|
-
|
|
1974
|
+
expect3(await store.tryLockRun("r1", "C", 9e3, 2600)).toBe(true);
|
|
1416
1975
|
});
|
|
1417
1976
|
t("renewRunLock only succeeds for the current owner", async () => {
|
|
1418
1977
|
await store.createRun(run({
|
|
1419
1978
|
id: "r1"
|
|
1420
1979
|
}));
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
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);
|
|
1425
1984
|
});
|
|
1426
1985
|
t("filters listRuns by workflow and by status", async () => {
|
|
1427
1986
|
await store.createRun(run({
|
|
@@ -1439,19 +1998,19 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1439
1998
|
workflow: "checkout",
|
|
1440
1999
|
status: "completed"
|
|
1441
2000
|
}));
|
|
1442
|
-
|
|
2001
|
+
expect3((await store.listRuns({
|
|
1443
2002
|
workflow: "checkout"
|
|
1444
2003
|
})).map((r) => r.id).sort()).toEqual([
|
|
1445
2004
|
"a",
|
|
1446
2005
|
"c"
|
|
1447
2006
|
]);
|
|
1448
|
-
|
|
2007
|
+
expect3((await store.listRuns({
|
|
1449
2008
|
status: "running"
|
|
1450
2009
|
})).map((r) => r.id).sort()).toEqual([
|
|
1451
2010
|
"a",
|
|
1452
2011
|
"b"
|
|
1453
2012
|
]);
|
|
1454
|
-
|
|
2013
|
+
expect3((await store.listRuns({
|
|
1455
2014
|
workflow: "checkout",
|
|
1456
2015
|
status: "running"
|
|
1457
2016
|
})).map((r) => r.id)).toEqual([
|
|
@@ -1475,7 +2034,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1475
2034
|
id: "d",
|
|
1476
2035
|
status: "pending"
|
|
1477
2036
|
}));
|
|
1478
|
-
|
|
2037
|
+
expect3((await store.listRuns({
|
|
1479
2038
|
statuses: [
|
|
1480
2039
|
"running",
|
|
1481
2040
|
"suspended"
|
|
@@ -1484,7 +2043,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1484
2043
|
"a",
|
|
1485
2044
|
"b"
|
|
1486
2045
|
]);
|
|
1487
|
-
|
|
2046
|
+
expect3((await store.listRuns({
|
|
1488
2047
|
status: "running",
|
|
1489
2048
|
statuses: [
|
|
1490
2049
|
"running",
|
|
@@ -1493,7 +2052,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1493
2052
|
})).map((r) => r.id)).toEqual([
|
|
1494
2053
|
"a"
|
|
1495
2054
|
]);
|
|
1496
|
-
|
|
2055
|
+
expect3(await store.listRuns({
|
|
1497
2056
|
statuses: []
|
|
1498
2057
|
})).toHaveLength(0);
|
|
1499
2058
|
});
|
|
@@ -1515,21 +2074,21 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1515
2074
|
await store.createRun(run({
|
|
1516
2075
|
id: "c"
|
|
1517
2076
|
}));
|
|
1518
|
-
|
|
2077
|
+
expect3((await store.getRun("a"))?.tags).toEqual([
|
|
1519
2078
|
"etl",
|
|
1520
2079
|
"critical"
|
|
1521
2080
|
]);
|
|
1522
|
-
|
|
2081
|
+
expect3((await store.listRuns({
|
|
1523
2082
|
tag: "etl"
|
|
1524
2083
|
})).map((r) => r.id)).toEqual([
|
|
1525
2084
|
"a"
|
|
1526
2085
|
]);
|
|
1527
|
-
|
|
2086
|
+
expect3((await store.listRuns({
|
|
1528
2087
|
tag: "etl-foo"
|
|
1529
2088
|
})).map((r) => r.id)).toEqual([
|
|
1530
2089
|
"b"
|
|
1531
2090
|
]);
|
|
1532
|
-
|
|
2091
|
+
expect3(await store.listRuns({
|
|
1533
2092
|
tag: "nope"
|
|
1534
2093
|
})).toHaveLength(0);
|
|
1535
2094
|
});
|
|
@@ -1545,17 +2104,17 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1545
2104
|
await store.createRun(run({
|
|
1546
2105
|
id: "c"
|
|
1547
2106
|
}));
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
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({
|
|
1551
2110
|
origin: "@dudousxd/nestjs-catalog-pipeline"
|
|
1552
2111
|
})).map((r) => r.id)).toEqual([
|
|
1553
2112
|
"a"
|
|
1554
2113
|
]);
|
|
1555
|
-
|
|
2114
|
+
expect3(await store.listRuns({
|
|
1556
2115
|
origin: "unknown"
|
|
1557
2116
|
})).toEqual([]);
|
|
1558
|
-
|
|
2117
|
+
expect3((await store.listRuns({})).map((r) => r.id).sort()).toEqual([
|
|
1559
2118
|
"a",
|
|
1560
2119
|
"b",
|
|
1561
2120
|
"c"
|
|
@@ -1572,7 +2131,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1572
2131
|
await store.createRun(run({
|
|
1573
2132
|
id: "c"
|
|
1574
2133
|
}));
|
|
1575
|
-
|
|
2134
|
+
expect3((await store.listRuns({
|
|
1576
2135
|
origin: null
|
|
1577
2136
|
})).map((r) => r.id).sort()).toEqual([
|
|
1578
2137
|
"b",
|
|
@@ -1582,7 +2141,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1582
2141
|
id: "d",
|
|
1583
2142
|
status: "dead"
|
|
1584
2143
|
}));
|
|
1585
|
-
|
|
2144
|
+
expect3((await store.listRuns({
|
|
1586
2145
|
origin: null,
|
|
1587
2146
|
status: "dead"
|
|
1588
2147
|
})).map((r) => r.id)).toEqual([
|
|
@@ -1615,22 +2174,22 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1615
2174
|
status: "failed"
|
|
1616
2175
|
}));
|
|
1617
2176
|
const facets = await store.runFacets({});
|
|
1618
|
-
|
|
2177
|
+
expect3(facets).toContainEqual({
|
|
1619
2178
|
status: "completed",
|
|
1620
2179
|
origin: "pkg-a",
|
|
1621
2180
|
count: 2
|
|
1622
2181
|
});
|
|
1623
|
-
|
|
2182
|
+
expect3(facets).toContainEqual({
|
|
1624
2183
|
status: "failed",
|
|
1625
2184
|
origin: "pkg-b",
|
|
1626
2185
|
count: 1
|
|
1627
2186
|
});
|
|
1628
|
-
|
|
2187
|
+
expect3(facets).toContainEqual({
|
|
1629
2188
|
status: "failed",
|
|
1630
2189
|
origin: null,
|
|
1631
2190
|
count: 2
|
|
1632
2191
|
});
|
|
1633
|
-
|
|
2192
|
+
expect3(facets.reduce((n, f) => n + f.count, 0)).toBe(5);
|
|
1634
2193
|
});
|
|
1635
2194
|
t("narrows runFacets on the same predicates listRuns pages", async () => {
|
|
1636
2195
|
if (!store.runFacets) return;
|
|
@@ -1644,7 +2203,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1644
2203
|
status: "failed",
|
|
1645
2204
|
namespace: "globex"
|
|
1646
2205
|
}));
|
|
1647
|
-
|
|
2206
|
+
expect3(await store.runFacets({
|
|
1648
2207
|
namespace: "acme"
|
|
1649
2208
|
})).toEqual([
|
|
1650
2209
|
{
|
|
@@ -1673,7 +2232,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1673
2232
|
namespace: "initech",
|
|
1674
2233
|
workflow: "wc"
|
|
1675
2234
|
}));
|
|
1676
|
-
|
|
2235
|
+
expect3((await store.listRuns({
|
|
1677
2236
|
statuses: [
|
|
1678
2237
|
"failed",
|
|
1679
2238
|
"dead"
|
|
@@ -1682,7 +2241,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1682
2241
|
"a",
|
|
1683
2242
|
"b"
|
|
1684
2243
|
]);
|
|
1685
|
-
|
|
2244
|
+
expect3((await store.listRuns({
|
|
1686
2245
|
namespaces: [
|
|
1687
2246
|
"acme",
|
|
1688
2247
|
"globex"
|
|
@@ -1691,7 +2250,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1691
2250
|
"a",
|
|
1692
2251
|
"b"
|
|
1693
2252
|
]);
|
|
1694
|
-
|
|
2253
|
+
expect3((await store.listRuns({
|
|
1695
2254
|
workflows: [
|
|
1696
2255
|
"wa",
|
|
1697
2256
|
"wc"
|
|
@@ -1713,7 +2272,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1713
2272
|
await store.createRun(run({
|
|
1714
2273
|
id: "c"
|
|
1715
2274
|
}));
|
|
1716
|
-
|
|
2275
|
+
expect3((await store.listRuns({
|
|
1717
2276
|
origins: [
|
|
1718
2277
|
"pkg-a",
|
|
1719
2278
|
null
|
|
@@ -1722,7 +2281,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1722
2281
|
"a",
|
|
1723
2282
|
"c"
|
|
1724
2283
|
]);
|
|
1725
|
-
|
|
2284
|
+
expect3((await store.listRuns({
|
|
1726
2285
|
origins: [
|
|
1727
2286
|
"pkg-a",
|
|
1728
2287
|
"pkg-b"
|
|
@@ -1743,7 +2302,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1743
2302
|
status: "dead",
|
|
1744
2303
|
namespace: "acme"
|
|
1745
2304
|
}));
|
|
1746
|
-
|
|
2305
|
+
expect3((await store.listRuns({
|
|
1747
2306
|
namespaces: [
|
|
1748
2307
|
"acme"
|
|
1749
2308
|
],
|
|
@@ -1753,13 +2312,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1753
2312
|
})).map((r) => r.id)).toEqual([
|
|
1754
2313
|
"b"
|
|
1755
2314
|
]);
|
|
1756
|
-
|
|
2315
|
+
expect3(await store.listRuns({
|
|
1757
2316
|
namespaces: []
|
|
1758
2317
|
})).toEqual([]);
|
|
1759
|
-
|
|
2318
|
+
expect3(await store.listRuns({
|
|
1760
2319
|
statuses: []
|
|
1761
2320
|
})).toEqual([]);
|
|
1762
|
-
|
|
2321
|
+
expect3(await store.listRuns({
|
|
1763
2322
|
origins: []
|
|
1764
2323
|
})).toEqual([]);
|
|
1765
2324
|
});
|
|
@@ -1784,7 +2343,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1784
2343
|
"adhoc"
|
|
1785
2344
|
]
|
|
1786
2345
|
}));
|
|
1787
|
-
|
|
2346
|
+
expect3((await store.listRuns({
|
|
1788
2347
|
tags: [
|
|
1789
2348
|
"etl",
|
|
1790
2349
|
"nightly"
|
|
@@ -1793,7 +2352,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1793
2352
|
"a",
|
|
1794
2353
|
"b"
|
|
1795
2354
|
]);
|
|
1796
|
-
|
|
2355
|
+
expect3(await store.listRuns({
|
|
1797
2356
|
tags: []
|
|
1798
2357
|
})).toEqual([]);
|
|
1799
2358
|
});
|
|
@@ -1834,7 +2393,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1834
2393
|
}
|
|
1835
2394
|
]
|
|
1836
2395
|
});
|
|
1837
|
-
|
|
2396
|
+
expect3(inSet.map((r) => r.id).sort()).toEqual([
|
|
1838
2397
|
"a",
|
|
1839
2398
|
"b"
|
|
1840
2399
|
]);
|
|
@@ -1850,10 +2409,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1850
2409
|
}
|
|
1851
2410
|
]
|
|
1852
2411
|
});
|
|
1853
|
-
|
|
2412
|
+
expect3(mixed.map((r) => r.id)).toEqual([
|
|
1854
2413
|
"d"
|
|
1855
2414
|
]);
|
|
1856
|
-
|
|
2415
|
+
expect3(await store.listRuns({
|
|
1857
2416
|
attributes: [
|
|
1858
2417
|
{
|
|
1859
2418
|
key: "tier",
|
|
@@ -1862,7 +2421,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1862
2421
|
}
|
|
1863
2422
|
]
|
|
1864
2423
|
})).toEqual([]);
|
|
1865
|
-
|
|
2424
|
+
expect3((await store.listRuns({
|
|
1866
2425
|
attributes: [
|
|
1867
2426
|
{
|
|
1868
2427
|
key: "tier",
|
|
@@ -1890,7 +2449,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1890
2449
|
id: "c",
|
|
1891
2450
|
namespace: "globex"
|
|
1892
2451
|
}));
|
|
1893
|
-
|
|
2452
|
+
expect3(await store.runValueFacets({
|
|
1894
2453
|
field: "namespace"
|
|
1895
2454
|
}, {})).toEqual([
|
|
1896
2455
|
{
|
|
@@ -1915,11 +2474,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1915
2474
|
const values = await store.runValueFacets({
|
|
1916
2475
|
field: "origin"
|
|
1917
2476
|
}, {});
|
|
1918
|
-
|
|
2477
|
+
expect3(values).toContainEqual({
|
|
1919
2478
|
value: "pkg-a",
|
|
1920
2479
|
count: 1
|
|
1921
2480
|
});
|
|
1922
|
-
|
|
2481
|
+
expect3(values).toContainEqual({
|
|
1923
2482
|
value: null,
|
|
1924
2483
|
count: 1
|
|
1925
2484
|
});
|
|
@@ -1941,7 +2500,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1941
2500
|
"nightly"
|
|
1942
2501
|
]
|
|
1943
2502
|
}));
|
|
1944
|
-
|
|
2503
|
+
expect3(await store.runValueFacets({
|
|
1945
2504
|
field: "tag"
|
|
1946
2505
|
}, {
|
|
1947
2506
|
namespace: "acme"
|
|
@@ -1973,7 +2532,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1973
2532
|
tier: "free"
|
|
1974
2533
|
}
|
|
1975
2534
|
}));
|
|
1976
|
-
|
|
2535
|
+
expect3(await store.runValueFacets({
|
|
1977
2536
|
field: "tag"
|
|
1978
2537
|
}, {})).toEqual([
|
|
1979
2538
|
{
|
|
@@ -1985,7 +2544,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1985
2544
|
count: 1
|
|
1986
2545
|
}
|
|
1987
2546
|
]);
|
|
1988
|
-
|
|
2547
|
+
expect3(await store.runValueFacets({
|
|
1989
2548
|
field: "attributeKey"
|
|
1990
2549
|
}, {})).toEqual([
|
|
1991
2550
|
{
|
|
@@ -1993,7 +2552,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
1993
2552
|
count: 2
|
|
1994
2553
|
}
|
|
1995
2554
|
]);
|
|
1996
|
-
|
|
2555
|
+
expect3((await store.runValueFacets({
|
|
1997
2556
|
field: "attributeValue",
|
|
1998
2557
|
key: "tier"
|
|
1999
2558
|
}, {})).map((r) => r.value).sort()).toEqual([
|
|
@@ -2024,18 +2583,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2024
2583
|
}, {}, {
|
|
2025
2584
|
search: "acme"
|
|
2026
2585
|
});
|
|
2027
|
-
|
|
2586
|
+
expect3(matches.map((row) => row.value).sort()).toEqual([
|
|
2028
2587
|
"acme-prod",
|
|
2029
2588
|
"acme-staging"
|
|
2030
2589
|
]);
|
|
2031
|
-
|
|
2590
|
+
expect3((await store.runValueFacets({
|
|
2032
2591
|
field: "namespace"
|
|
2033
2592
|
}, {}, {
|
|
2034
2593
|
limit: 1
|
|
2035
2594
|
})).map((r) => r.value)).toEqual([
|
|
2036
2595
|
"globex"
|
|
2037
2596
|
]);
|
|
2038
|
-
|
|
2597
|
+
expect3((await store.runValueFacets({
|
|
2039
2598
|
field: "namespace"
|
|
2040
2599
|
}, {}, {
|
|
2041
2600
|
limit: 1,
|
|
@@ -2081,7 +2640,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2081
2640
|
limit: 2,
|
|
2082
2641
|
offset: 2
|
|
2083
2642
|
});
|
|
2084
|
-
|
|
2643
|
+
expect3(first).toEqual([
|
|
2085
2644
|
{
|
|
2086
2645
|
value: "one",
|
|
2087
2646
|
count: 3
|
|
@@ -2091,13 +2650,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2091
2650
|
count: 2
|
|
2092
2651
|
}
|
|
2093
2652
|
]);
|
|
2094
|
-
|
|
2653
|
+
expect3(second).toEqual([
|
|
2095
2654
|
{
|
|
2096
2655
|
value: "three",
|
|
2097
2656
|
count: 1
|
|
2098
2657
|
}
|
|
2099
2658
|
]);
|
|
2100
|
-
|
|
2659
|
+
expect3(new Set([
|
|
2101
2660
|
...first,
|
|
2102
2661
|
...second
|
|
2103
2662
|
].map((r) => r.value)).size).toBe(3);
|
|
@@ -2125,7 +2684,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2125
2684
|
"etl"
|
|
2126
2685
|
]
|
|
2127
2686
|
}));
|
|
2128
|
-
|
|
2687
|
+
expect3((await store.runValueFacets({
|
|
2129
2688
|
field: "tag"
|
|
2130
2689
|
}, {}, {
|
|
2131
2690
|
search: "type:"
|
|
@@ -2133,7 +2692,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2133
2692
|
"type:mel",
|
|
2134
2693
|
"type:mvr"
|
|
2135
2694
|
]);
|
|
2136
|
-
|
|
2695
|
+
expect3(await store.runValueFacets({
|
|
2137
2696
|
field: "tag"
|
|
2138
2697
|
}, {}, {
|
|
2139
2698
|
limit: 1
|
|
@@ -2158,7 +2717,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2158
2717
|
id: "c",
|
|
2159
2718
|
namespace: "globex"
|
|
2160
2719
|
}));
|
|
2161
|
-
|
|
2720
|
+
expect3(await store.runValueFacets({
|
|
2162
2721
|
field: "namespace"
|
|
2163
2722
|
}, {}, {
|
|
2164
2723
|
limit: 1
|
|
@@ -2170,7 +2729,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2170
2729
|
]);
|
|
2171
2730
|
});
|
|
2172
2731
|
t("countRuns counts the same set listRuns returns, without the paging", async () => {
|
|
2173
|
-
|
|
2732
|
+
expect3(typeof store.countRuns).toBe("function");
|
|
2174
2733
|
await store.createRun(run({
|
|
2175
2734
|
id: "n1",
|
|
2176
2735
|
status: "running",
|
|
@@ -2200,22 +2759,22 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2200
2759
|
"q"
|
|
2201
2760
|
]
|
|
2202
2761
|
}));
|
|
2203
|
-
|
|
2204
|
-
|
|
2762
|
+
expect3(await store.countRuns?.({})).toBe(4);
|
|
2763
|
+
expect3(await store.countRuns?.({
|
|
2205
2764
|
namespace: "a"
|
|
2206
2765
|
})).toBe(3);
|
|
2207
|
-
|
|
2766
|
+
expect3(await store.countRuns?.({
|
|
2208
2767
|
namespace: "a",
|
|
2209
2768
|
statuses: [
|
|
2210
2769
|
"pending",
|
|
2211
2770
|
"running"
|
|
2212
2771
|
]
|
|
2213
2772
|
})).toBe(2);
|
|
2214
|
-
|
|
2773
|
+
expect3(await store.countRuns?.({
|
|
2215
2774
|
statuses: []
|
|
2216
2775
|
})).toBe(0);
|
|
2217
2776
|
if (supportsTagFilter) {
|
|
2218
|
-
|
|
2777
|
+
expect3(await store.countRuns?.({
|
|
2219
2778
|
tag: "q",
|
|
2220
2779
|
statuses: [
|
|
2221
2780
|
"running",
|
|
@@ -2242,28 +2801,28 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2242
2801
|
await engine.start("q-wait", {}, "q1", {
|
|
2243
2802
|
concurrency: quota
|
|
2244
2803
|
});
|
|
2245
|
-
|
|
2804
|
+
expect3((await engine.waitForRun("q1", {
|
|
2246
2805
|
timeoutMs: 2e4
|
|
2247
2806
|
})).status).toBe("suspended");
|
|
2248
|
-
await
|
|
2807
|
+
await expect3(engine.start("q-wait", {}, "q2", {
|
|
2249
2808
|
concurrency: quota
|
|
2250
2809
|
})).rejects.toThrow(/concurrency limit/);
|
|
2251
|
-
|
|
2810
|
+
expect3(await store.getRun("q2")).toBeNull();
|
|
2252
2811
|
await engine.start("q-wait", {}, "q3", {
|
|
2253
2812
|
concurrency: {
|
|
2254
2813
|
...quota,
|
|
2255
2814
|
key: "tenant:b"
|
|
2256
2815
|
}
|
|
2257
2816
|
});
|
|
2258
|
-
|
|
2817
|
+
expect3((await engine.waitForRun("q1", {
|
|
2259
2818
|
timeoutMs: 2e4
|
|
2260
2819
|
})).status).toBe("suspended");
|
|
2261
2820
|
await engine.cancel("q1");
|
|
2262
|
-
|
|
2821
|
+
expect3((await store.getRun("q1"))?.status).toBe("cancelled");
|
|
2263
2822
|
await engine.start("q-wait", {}, "q4", {
|
|
2264
2823
|
concurrency: quota
|
|
2265
2824
|
});
|
|
2266
|
-
|
|
2825
|
+
expect3(await store.getRun("q4")).not.toBeNull();
|
|
2267
2826
|
for (const id of [
|
|
2268
2827
|
"q3",
|
|
2269
2828
|
"q4"
|
|
@@ -2302,10 +2861,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2302
2861
|
...over
|
|
2303
2862
|
}), "schedule");
|
|
2304
2863
|
t("saves, reads back, replaces and deletes a schedule", async () => {
|
|
2305
|
-
|
|
2864
|
+
expect3(typeof store.saveSchedule).toBe("function");
|
|
2306
2865
|
await store.saveSchedule?.(schedule());
|
|
2307
2866
|
const loaded = await store.getSchedule?.("s1");
|
|
2308
|
-
|
|
2867
|
+
expect3(loaded).toMatchObject({
|
|
2309
2868
|
id: "s1",
|
|
2310
2869
|
namespace: "default",
|
|
2311
2870
|
workflow: "digest",
|
|
@@ -2331,27 +2890,27 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2331
2890
|
window: NOW + 6e4
|
|
2332
2891
|
}
|
|
2333
2892
|
});
|
|
2334
|
-
|
|
2893
|
+
expect3(loaded?.createdAt.getTime()).toBe(at.getTime());
|
|
2335
2894
|
await store.saveSchedule?.(schedule({
|
|
2336
2895
|
workflow: "digest2",
|
|
2337
2896
|
nextFireAt: null,
|
|
2338
2897
|
tags: void 0
|
|
2339
2898
|
}));
|
|
2340
2899
|
const replaced = await store.getSchedule?.("s1");
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
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();
|
|
2348
2907
|
});
|
|
2349
2908
|
t("updateSchedule patches, and compare-and-sets on nextFireAt", async () => {
|
|
2350
2909
|
await store.saveSchedule?.(schedule());
|
|
2351
|
-
|
|
2910
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2352
2911
|
paused: true
|
|
2353
2912
|
})).toBe(true);
|
|
2354
|
-
|
|
2913
|
+
expect3((await store.getSchedule?.("s1"))?.paused).toBe(true);
|
|
2355
2914
|
const next = {
|
|
2356
2915
|
nextFireAt: NOW + 12e4,
|
|
2357
2916
|
state: {
|
|
@@ -2359,24 +2918,24 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2359
2918
|
fires: 1
|
|
2360
2919
|
}
|
|
2361
2920
|
};
|
|
2362
|
-
|
|
2363
|
-
|
|
2921
|
+
expect3(await store.updateSchedule?.("s1", next, NOW + 6e4)).toBe(true);
|
|
2922
|
+
expect3(await store.updateSchedule?.("s1", next, NOW + 6e4)).toBe(false);
|
|
2364
2923
|
const after = await store.getSchedule?.("s1");
|
|
2365
|
-
|
|
2366
|
-
|
|
2924
|
+
expect3(after?.nextFireAt).toBe(NOW + 12e4);
|
|
2925
|
+
expect3(after?.state).toEqual({
|
|
2367
2926
|
window: NOW + 12e4,
|
|
2368
2927
|
fires: 1
|
|
2369
2928
|
});
|
|
2370
2929
|
await store.updateSchedule?.("s1", {
|
|
2371
2930
|
nextFireAt: null
|
|
2372
2931
|
});
|
|
2373
|
-
|
|
2932
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2374
2933
|
nextFireAt: NOW
|
|
2375
2934
|
}, NOW + 1)).toBe(false);
|
|
2376
|
-
|
|
2935
|
+
expect3(await store.updateSchedule?.("s1", {
|
|
2377
2936
|
nextFireAt: NOW
|
|
2378
2937
|
}, null)).toBe(true);
|
|
2379
|
-
|
|
2938
|
+
expect3(await store.updateSchedule?.("ghost", {
|
|
2380
2939
|
paused: true
|
|
2381
2940
|
})).toBe(false);
|
|
2382
2941
|
});
|
|
@@ -2408,47 +2967,47 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2408
2967
|
]
|
|
2409
2968
|
}));
|
|
2410
2969
|
const ids2 = /* @__PURE__ */ __name(async (q) => (await store.listSchedules?.(q) ?? []).map((r) => r.id), "ids");
|
|
2411
|
-
|
|
2970
|
+
expect3(await ids2({})).toEqual([
|
|
2412
2971
|
"c",
|
|
2413
2972
|
"b",
|
|
2414
2973
|
"a",
|
|
2415
2974
|
"d",
|
|
2416
2975
|
"e"
|
|
2417
2976
|
]);
|
|
2418
|
-
|
|
2977
|
+
expect3(await ids2({
|
|
2419
2978
|
dueBy: NOW
|
|
2420
2979
|
})).toEqual([
|
|
2421
2980
|
"b",
|
|
2422
2981
|
"a"
|
|
2423
2982
|
]);
|
|
2424
|
-
|
|
2983
|
+
expect3(await ids2({
|
|
2425
2984
|
dueBy: NOW,
|
|
2426
2985
|
namespace: "default"
|
|
2427
2986
|
})).toEqual([
|
|
2428
2987
|
"a"
|
|
2429
2988
|
]);
|
|
2430
|
-
|
|
2989
|
+
expect3(await ids2({
|
|
2431
2990
|
dueBy: NOW,
|
|
2432
2991
|
limit: 1
|
|
2433
2992
|
})).toEqual([
|
|
2434
2993
|
"b"
|
|
2435
2994
|
]);
|
|
2436
|
-
|
|
2995
|
+
expect3(await ids2({
|
|
2437
2996
|
workflow: "other"
|
|
2438
2997
|
})).toEqual([
|
|
2439
2998
|
"d"
|
|
2440
2999
|
]);
|
|
2441
|
-
|
|
3000
|
+
expect3(await ids2({
|
|
2442
3001
|
paused: true
|
|
2443
3002
|
})).toEqual([
|
|
2444
3003
|
"c"
|
|
2445
3004
|
]);
|
|
2446
|
-
if (supportsTagFilter)
|
|
3005
|
+
if (supportsTagFilter) expect3(await ids2({
|
|
2447
3006
|
tag: "tenant:b"
|
|
2448
3007
|
})).toEqual([
|
|
2449
3008
|
"e"
|
|
2450
3009
|
]);
|
|
2451
|
-
|
|
3010
|
+
expect3(await ids2({
|
|
2452
3011
|
limit: 2,
|
|
2453
3012
|
offset: 1
|
|
2454
3013
|
})).toEqual([
|
|
@@ -2469,18 +3028,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2469
3028
|
id: "new",
|
|
2470
3029
|
createdAt: /* @__PURE__ */ new Date("2026-06-11T00:00:02.000Z")
|
|
2471
3030
|
}));
|
|
2472
|
-
|
|
3031
|
+
expect3((await store.listRuns({})).map((r) => r.id)).toEqual([
|
|
2473
3032
|
"new",
|
|
2474
3033
|
"mid",
|
|
2475
3034
|
"old"
|
|
2476
3035
|
]);
|
|
2477
|
-
|
|
3036
|
+
expect3((await store.listRuns({
|
|
2478
3037
|
limit: 2
|
|
2479
3038
|
})).map((r) => r.id)).toEqual([
|
|
2480
3039
|
"new",
|
|
2481
3040
|
"mid"
|
|
2482
3041
|
]);
|
|
2483
|
-
|
|
3042
|
+
expect3((await store.listRuns({
|
|
2484
3043
|
limit: 2,
|
|
2485
3044
|
offset: 1
|
|
2486
3045
|
})).map((r) => r.id)).toEqual([
|
|
@@ -2510,11 +3069,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2510
3069
|
tier: "pro"
|
|
2511
3070
|
}
|
|
2512
3071
|
}));
|
|
2513
|
-
|
|
3072
|
+
expect3((await store.getRun("b"))?.searchAttributes).toEqual({
|
|
2514
3073
|
amount: 200,
|
|
2515
3074
|
tier: "pro"
|
|
2516
3075
|
});
|
|
2517
|
-
|
|
3076
|
+
expect3((await store.listRuns({
|
|
2518
3077
|
attributes: [
|
|
2519
3078
|
{
|
|
2520
3079
|
key: "amount",
|
|
@@ -2526,7 +3085,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2526
3085
|
"b",
|
|
2527
3086
|
"c"
|
|
2528
3087
|
]);
|
|
2529
|
-
|
|
3088
|
+
expect3((await store.listRuns({
|
|
2530
3089
|
attributes: [
|
|
2531
3090
|
{
|
|
2532
3091
|
key: "tier",
|
|
@@ -2542,7 +3101,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2542
3101
|
})).map((r) => r.id)).toEqual([
|
|
2543
3102
|
"b"
|
|
2544
3103
|
]);
|
|
2545
|
-
|
|
3104
|
+
expect3((await store.listRuns({
|
|
2546
3105
|
attributes: [
|
|
2547
3106
|
{
|
|
2548
3107
|
key: "tier",
|
|
@@ -2574,7 +3133,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2574
3133
|
other: 1
|
|
2575
3134
|
}
|
|
2576
3135
|
}));
|
|
2577
|
-
|
|
3136
|
+
expect3((await store.listRuns({
|
|
2578
3137
|
attributes: [
|
|
2579
3138
|
{
|
|
2580
3139
|
key: "vip",
|
|
@@ -2585,7 +3144,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2585
3144
|
})).map((r) => r.id)).toEqual([
|
|
2586
3145
|
"a"
|
|
2587
3146
|
]);
|
|
2588
|
-
|
|
3147
|
+
expect3((await store.listRuns({
|
|
2589
3148
|
attributes: [
|
|
2590
3149
|
{
|
|
2591
3150
|
key: "vip",
|
|
@@ -2612,7 +3171,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2612
3171
|
"lt",
|
|
2613
3172
|
"lte"
|
|
2614
3173
|
]) {
|
|
2615
|
-
|
|
3174
|
+
expect3(await store.listRuns({
|
|
2616
3175
|
attributes: [
|
|
2617
3176
|
{
|
|
2618
3177
|
key: "missing",
|
|
@@ -2631,7 +3190,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2631
3190
|
amount: 10
|
|
2632
3191
|
}
|
|
2633
3192
|
}));
|
|
2634
|
-
|
|
3193
|
+
expect3((await store.listRuns({
|
|
2635
3194
|
attributes: [
|
|
2636
3195
|
{
|
|
2637
3196
|
key: "tier",
|
|
@@ -2647,7 +3206,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2647
3206
|
tier: "pro"
|
|
2648
3207
|
}
|
|
2649
3208
|
});
|
|
2650
|
-
|
|
3209
|
+
expect3((await store.listRuns({
|
|
2651
3210
|
attributes: [
|
|
2652
3211
|
{
|
|
2653
3212
|
key: "tier",
|
|
@@ -2658,7 +3217,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2658
3217
|
})).map((r) => r.id)).toEqual([
|
|
2659
3218
|
"a"
|
|
2660
3219
|
]);
|
|
2661
|
-
|
|
3220
|
+
expect3(await store.listRuns({
|
|
2662
3221
|
attributes: [
|
|
2663
3222
|
{
|
|
2664
3223
|
key: "tier",
|
|
@@ -2667,7 +3226,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2667
3226
|
}
|
|
2668
3227
|
]
|
|
2669
3228
|
})).toHaveLength(0);
|
|
2670
|
-
|
|
3229
|
+
expect3(await store.listRuns({
|
|
2671
3230
|
attributes: [
|
|
2672
3231
|
{
|
|
2673
3232
|
key: "amount",
|
|
@@ -2710,7 +3269,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2710
3269
|
],
|
|
2711
3270
|
limit: 10
|
|
2712
3271
|
});
|
|
2713
|
-
|
|
3272
|
+
expect3(res.map((r) => r.id)).toEqual([
|
|
2714
3273
|
"a"
|
|
2715
3274
|
]);
|
|
2716
3275
|
});
|
|
@@ -2730,12 +3289,12 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2730
3289
|
runId: "r3",
|
|
2731
3290
|
seq: 5
|
|
2732
3291
|
});
|
|
2733
|
-
|
|
3292
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.token).sort()).toEqual([
|
|
2734
3293
|
"approve-1",
|
|
2735
3294
|
"approve-2"
|
|
2736
3295
|
]);
|
|
2737
|
-
|
|
2738
|
-
|
|
3296
|
+
expect3((await store.takeSignalWaiter("approve-1"))?.seq).toBe(3);
|
|
3297
|
+
expect3(await store.takeSignalWaiter("approve-1")).toBeNull();
|
|
2739
3298
|
});
|
|
2740
3299
|
t("round-trips a signal waiter parallelGroup (child-fan tag) through put \u2192 take \u2192 list", async () => {
|
|
2741
3300
|
await store.putSignalWaiter({
|
|
@@ -2750,13 +3309,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2750
3309
|
seq: 9
|
|
2751
3310
|
});
|
|
2752
3311
|
const listed = await store.listSignalWaiters("child:");
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
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();
|
|
2757
3316
|
});
|
|
2758
3317
|
t("registerChildWaits writes every waiter and placeholder, and upserts on repeat", async () => {
|
|
2759
|
-
|
|
3318
|
+
expect3(store.registerChildWaits).toBeTypeOf("function");
|
|
2760
3319
|
await store.createRun(run({
|
|
2761
3320
|
id: "p"
|
|
2762
3321
|
}));
|
|
@@ -2782,24 +3341,24 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2782
3341
|
}, (_, i) => entry(i, "running"));
|
|
2783
3342
|
await store.registerChildWaits?.(entries);
|
|
2784
3343
|
const waiters = await store.listSignalWaiters("child:p.all.0.");
|
|
2785
|
-
|
|
2786
|
-
|
|
3344
|
+
expect3(waiters).toHaveLength(250);
|
|
3345
|
+
expect3(waiters.find((w) => w.token === "child:p.all.0.7")).toEqual(entries[7]?.waiter);
|
|
2787
3346
|
const cps = await store.listCheckpoints("p");
|
|
2788
|
-
|
|
2789
|
-
|
|
3347
|
+
expect3(cps).toHaveLength(250);
|
|
3348
|
+
expect3(cps[7]).toMatchObject({
|
|
2790
3349
|
name: "signal:child:p.all.0.7",
|
|
2791
3350
|
status: "running"
|
|
2792
3351
|
});
|
|
2793
|
-
|
|
3352
|
+
expect3(cps[7]?.parallelGroup).toBe("all:0");
|
|
2794
3353
|
await store.registerChildWaits?.([
|
|
2795
3354
|
entry(7, "completed")
|
|
2796
3355
|
]);
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
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();
|
|
2800
3359
|
});
|
|
2801
3360
|
t("compactCheckpoints replaces a seq range with one checkpoint, atomically", async () => {
|
|
2802
|
-
|
|
3361
|
+
expect3(store.compactCheckpoints).toBeTypeOf("function");
|
|
2803
3362
|
await store.createRun(run({
|
|
2804
3363
|
id: "r"
|
|
2805
3364
|
}));
|
|
@@ -2838,18 +3397,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2838
3397
|
});
|
|
2839
3398
|
await store.compactCheckpoints?.("r", 2, 7, into);
|
|
2840
3399
|
const left = await store.listCheckpoints("r");
|
|
2841
|
-
|
|
3400
|
+
expect3(left.map((cp2) => cp2.seq)).toEqual([
|
|
2842
3401
|
0,
|
|
2843
3402
|
1,
|
|
2844
3403
|
2,
|
|
2845
3404
|
8,
|
|
2846
3405
|
9
|
|
2847
3406
|
]);
|
|
2848
|
-
|
|
3407
|
+
expect3(left[2]).toMatchObject({
|
|
2849
3408
|
name: "fan:r.all.2.",
|
|
2850
3409
|
parallelGroup: "all:2"
|
|
2851
3410
|
});
|
|
2852
|
-
|
|
3411
|
+
expect3(left[2]?.output).toEqual({
|
|
2853
3412
|
from: 0,
|
|
2854
3413
|
values: [
|
|
2855
3414
|
{
|
|
@@ -2859,10 +3418,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2859
3418
|
null
|
|
2860
3419
|
]
|
|
2861
3420
|
});
|
|
2862
|
-
|
|
3421
|
+
expect3(await store.listCheckpoints("other")).toHaveLength(1);
|
|
2863
3422
|
});
|
|
2864
3423
|
t("listSignalWaitersByRun returns only the given runs' waiters", async () => {
|
|
2865
|
-
|
|
3424
|
+
expect3(store.listSignalWaitersByRun).toBeTypeOf("function");
|
|
2866
3425
|
await store.putSignalWaiter({
|
|
2867
3426
|
token: "child:a.all.0.0",
|
|
2868
3427
|
runId: "a",
|
|
@@ -2885,7 +3444,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2885
3444
|
seq: 0
|
|
2886
3445
|
});
|
|
2887
3446
|
const tokens = /* @__PURE__ */ __name(async (ids2) => (await store.listSignalWaitersByRun?.(ids2) ?? []).map((w) => w.token).sort(), "tokens");
|
|
2888
|
-
|
|
3447
|
+
expect3(await tokens([
|
|
2889
3448
|
"a",
|
|
2890
3449
|
"b"
|
|
2891
3450
|
])).toEqual([
|
|
@@ -2893,11 +3452,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2893
3452
|
"approve-b",
|
|
2894
3453
|
"child:a.all.0.0"
|
|
2895
3454
|
]);
|
|
2896
|
-
|
|
3455
|
+
expect3(await tokens([
|
|
2897
3456
|
"missing"
|
|
2898
3457
|
])).toEqual([]);
|
|
2899
|
-
|
|
2900
|
-
|
|
3458
|
+
expect3(await tokens([])).toEqual([]);
|
|
3459
|
+
expect3((await store.listSignalWaitersByRun?.([
|
|
2901
3460
|
"a"
|
|
2902
3461
|
]))?.find((w) => w.seq === 0)).toEqual({
|
|
2903
3462
|
token: "child:a.all.0.0",
|
|
@@ -2907,7 +3466,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2907
3466
|
});
|
|
2908
3467
|
});
|
|
2909
3468
|
t("hasSignalWaiter answers by run and exact token prefix", async () => {
|
|
2910
|
-
|
|
3469
|
+
expect3(store.hasSignalWaiter).toBeTypeOf("function");
|
|
2911
3470
|
const has = /* @__PURE__ */ __name((runId, prefix) => store.hasSignalWaiter?.(runId, prefix), "has");
|
|
2912
3471
|
await store.putSignalWaiter({
|
|
2913
3472
|
token: "child:p_1.all.3.0",
|
|
@@ -2925,13 +3484,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2925
3484
|
runId: "p_1",
|
|
2926
3485
|
seq: 9
|
|
2927
3486
|
});
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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);
|
|
2933
3492
|
await store.takeSignalWaiter("child:p_1.all.3.0");
|
|
2934
|
-
|
|
3493
|
+
expect3(await has("p_1", "child:p_1.all.3.")).toBe(false);
|
|
2935
3494
|
});
|
|
2936
3495
|
t("removeSignalWaiter deletes only the exact (token, runId, seq) row, never a different run's", async () => {
|
|
2937
3496
|
await store.putSignalWaiter({
|
|
@@ -2949,7 +3508,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2949
3508
|
runId: "r1",
|
|
2950
3509
|
seq: 3
|
|
2951
3510
|
});
|
|
2952
|
-
|
|
3511
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.runId)).toEqual([
|
|
2953
3512
|
"r2"
|
|
2954
3513
|
]);
|
|
2955
3514
|
await store.removeSignalWaiter({
|
|
@@ -2957,7 +3516,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2957
3516
|
runId: "r2",
|
|
2958
3517
|
seq: 999
|
|
2959
3518
|
});
|
|
2960
|
-
|
|
3519
|
+
expect3((await store.listSignalWaiters("approve-")).map((w) => w.runId)).toEqual([
|
|
2961
3520
|
"r2"
|
|
2962
3521
|
]);
|
|
2963
3522
|
await store.removeSignalWaiter({
|
|
@@ -2965,7 +3524,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2965
3524
|
runId: "r2",
|
|
2966
3525
|
seq: 7
|
|
2967
3526
|
});
|
|
2968
|
-
|
|
3527
|
+
expect3(await store.listSignalWaiters("approve-")).toEqual([]);
|
|
2969
3528
|
await store.removeSignalWaiter({
|
|
2970
3529
|
token: "never-registered",
|
|
2971
3530
|
runId: "r1",
|
|
@@ -2982,18 +3541,18 @@ function runStateStoreContract(name, makeStore) {
|
|
|
2982
3541
|
await store.bufferSignal("other", {
|
|
2983
3542
|
n: 9
|
|
2984
3543
|
});
|
|
2985
|
-
|
|
3544
|
+
expect3(await store.takeBufferedSignal("sig")).toEqual({
|
|
2986
3545
|
payload: {
|
|
2987
3546
|
n: 1
|
|
2988
3547
|
}
|
|
2989
3548
|
});
|
|
2990
|
-
|
|
3549
|
+
expect3(await store.takeBufferedSignal("sig")).toEqual({
|
|
2991
3550
|
payload: {
|
|
2992
3551
|
n: 2
|
|
2993
3552
|
}
|
|
2994
3553
|
});
|
|
2995
|
-
|
|
2996
|
-
|
|
3554
|
+
expect3(await store.takeBufferedSignal("sig")).toBeNull();
|
|
3555
|
+
expect3(await store.takeBufferedSignal("other")).toEqual({
|
|
2997
3556
|
payload: {
|
|
2998
3557
|
n: 9
|
|
2999
3558
|
}
|
|
@@ -3025,7 +3584,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3025
3584
|
publishedAt: 50
|
|
3026
3585
|
});
|
|
3027
3586
|
const listed = await store.listBufferedEvents("order.paid", 10);
|
|
3028
|
-
|
|
3587
|
+
expect3(listed.map((e) => e.payload)).toEqual([
|
|
3029
3588
|
{
|
|
3030
3589
|
n: 1
|
|
3031
3590
|
},
|
|
@@ -3033,13 +3592,13 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3033
3592
|
n: 2
|
|
3034
3593
|
}
|
|
3035
3594
|
]);
|
|
3036
|
-
|
|
3595
|
+
expect3(listed.map((e) => e.id)).toEqual([
|
|
3037
3596
|
"e1",
|
|
3038
3597
|
"e2"
|
|
3039
3598
|
]);
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
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([
|
|
3043
3602
|
{
|
|
3044
3603
|
id: "e3",
|
|
3045
3604
|
payload: {
|
|
@@ -3068,7 +3627,7 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3068
3627
|
id: "c",
|
|
3069
3628
|
publishedAt: 3
|
|
3070
3629
|
});
|
|
3071
|
-
|
|
3630
|
+
expect3(await store.listBufferedEvents("evt", 2)).toHaveLength(2);
|
|
3072
3631
|
});
|
|
3073
3632
|
t("removeBufferedEvent atomically deletes by id \u2014 true iff a row existed, false on a repeat/absent id", async () => {
|
|
3074
3633
|
await store.bufferEvent({
|
|
@@ -3079,10 +3638,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3079
3638
|
id: "x1",
|
|
3080
3639
|
publishedAt: 1
|
|
3081
3640
|
});
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
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);
|
|
3086
3645
|
});
|
|
3087
3646
|
t("transaction commits the checkpoint atomically and returns the work result", async () => {
|
|
3088
3647
|
if (!store.transaction) return;
|
|
@@ -3098,10 +3657,10 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3098
3657
|
}));
|
|
3099
3658
|
return "ok";
|
|
3100
3659
|
});
|
|
3101
|
-
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3104
|
-
|
|
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({
|
|
3105
3664
|
paid: true
|
|
3106
3665
|
});
|
|
3107
3666
|
});
|
|
@@ -3128,11 +3687,11 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3128
3687
|
await engine.start("wf", {
|
|
3129
3688
|
x: 1
|
|
3130
3689
|
}, "run1");
|
|
3131
|
-
|
|
3690
|
+
expect3((await engine.waitForRun("run1")).status).toBe("failed");
|
|
3132
3691
|
const resumed = await engine.resume("run1");
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3692
|
+
expect3(resumed.status).toBe("completed");
|
|
3693
|
+
expect3(resumed.output).toBe(15);
|
|
3694
|
+
expect3(aRuns).toBe(1);
|
|
3136
3695
|
});
|
|
3137
3696
|
t("starts the version the caller pinned, and refuses one that is not registered", async () => {
|
|
3138
3697
|
const engine = new WorkflowEngine3({
|
|
@@ -3153,19 +3712,19 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3153
3712
|
const pinned = await engine.waitForRun("pinned-run", {
|
|
3154
3713
|
timeoutMs: 2e4
|
|
3155
3714
|
});
|
|
3156
|
-
|
|
3157
|
-
|
|
3715
|
+
expect3(pinned.output).toBe("v1-output");
|
|
3716
|
+
expect3(ran).toEqual([
|
|
3158
3717
|
"v1"
|
|
3159
3718
|
]);
|
|
3160
|
-
|
|
3719
|
+
expect3((await store.getRun("pinned-run"))?.workflowVersion).toBe("1");
|
|
3161
3720
|
await engine.start("vwf", {}, "newest-run");
|
|
3162
|
-
|
|
3721
|
+
expect3((await engine.waitForRun("newest-run", {
|
|
3163
3722
|
timeoutMs: 2e4
|
|
3164
3723
|
})).output).toBe("v2-output");
|
|
3165
|
-
await
|
|
3724
|
+
await expect3(engine.start("vwf", {}, "ghost-run", {
|
|
3166
3725
|
version: "9"
|
|
3167
3726
|
})).rejects.toThrow(/vwf@9 is not registered/);
|
|
3168
|
-
|
|
3727
|
+
expect3(await store.getRun("ghost-run")).toBeNull();
|
|
3169
3728
|
});
|
|
3170
3729
|
t("cancels a timed-out run\u2019s live child subtree, not just the run itself", async () => {
|
|
3171
3730
|
const engine = new WorkflowEngine3({
|
|
@@ -3188,16 +3747,16 @@ function runStateStoreContract(name, makeStore) {
|
|
|
3188
3747
|
"tc",
|
|
3189
3748
|
"tgc"
|
|
3190
3749
|
]) {
|
|
3191
|
-
|
|
3750
|
+
expect3((await engine.waitForRun(id, {
|
|
3192
3751
|
timeoutMs: 2e4
|
|
3193
3752
|
})).status).toBe("suspended");
|
|
3194
3753
|
}
|
|
3195
3754
|
await engine.sweepTimeouts(Date.now() + 37e5);
|
|
3196
3755
|
const parent = await store.getRun("tp");
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
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");
|
|
3201
3760
|
});
|
|
3202
3761
|
});
|
|
3203
3762
|
}
|
|
@@ -3269,6 +3828,7 @@ export {
|
|
|
3269
3828
|
DURABLE_CANONICAL_COLUMNS,
|
|
3270
3829
|
MutableClock,
|
|
3271
3830
|
StateStoreUnavailableError,
|
|
3831
|
+
aggregationFixture,
|
|
3272
3832
|
assertDurableColumns,
|
|
3273
3833
|
assertOutput,
|
|
3274
3834
|
assertReplayable,
|
|
@@ -3281,6 +3841,7 @@ export {
|
|
|
3281
3841
|
failTimes,
|
|
3282
3842
|
preserveColumnExpectation,
|
|
3283
3843
|
recordedSteps,
|
|
3844
|
+
registerCheckpointAggregationCases,
|
|
3284
3845
|
runAdmissionBackendContract,
|
|
3285
3846
|
runStateStoreContract
|
|
3286
3847
|
};
|