@rotorsoft/act-tck 0.1.0 → 0.3.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/README.md +2 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/store-tck.d.ts.map +1 -1
- package/dist/index.cjs +441 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +442 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -189,7 +189,7 @@ var runLoggerTck = (options) => {
|
|
|
189
189
|
};
|
|
190
190
|
|
|
191
191
|
// src/store-tck.ts
|
|
192
|
-
import { ConcurrencyError } from "@rotorsoft/act";
|
|
192
|
+
import { ConcurrencyError, SNAP_EVENT, TOMBSTONE_EVENT } from "@rotorsoft/act";
|
|
193
193
|
import { afterAll, beforeAll, describe as describe3, expect as expect3, it as it3 } from "vitest";
|
|
194
194
|
var runStoreTck = (options) => {
|
|
195
195
|
describe3(`TCK / Store / ${options.name}`, () => {
|
|
@@ -311,6 +311,14 @@ var runStoreTck = (options) => {
|
|
|
311
311
|
expect3(backward.map((e) => e.id)).toEqual(
|
|
312
312
|
[...committed].reverse().map((c) => c.id)
|
|
313
313
|
);
|
|
314
|
+
const latest = await collect(store, {
|
|
315
|
+
stream: s,
|
|
316
|
+
stream_exact: true,
|
|
317
|
+
backward: true,
|
|
318
|
+
limit: 1
|
|
319
|
+
});
|
|
320
|
+
expect3(latest).toHaveLength(1);
|
|
321
|
+
expect3(latest[0].id).toBe(committed.at(-1).id);
|
|
314
322
|
});
|
|
315
323
|
it3("after/before bound the id range", async () => {
|
|
316
324
|
const s = `q-bounds-${uid()}`;
|
|
@@ -630,6 +638,216 @@ var runStoreTck = (options) => {
|
|
|
630
638
|
expect3(await store.reset([])).toBe(0);
|
|
631
639
|
});
|
|
632
640
|
});
|
|
641
|
+
describe3("unblock", () => {
|
|
642
|
+
it3("clears blocked flag and preserves the watermark", async () => {
|
|
643
|
+
const s = `unblock-${uid()}`;
|
|
644
|
+
await store.subscribe([{ stream: s }]);
|
|
645
|
+
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
646
|
+
await store.commit(s, [inc(2)], makeMeta({ stream: s }));
|
|
647
|
+
const first = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
648
|
+
const m1 = first.find((l) => l.stream === s);
|
|
649
|
+
await store.ack([{ ...m1, at: m1.at }]);
|
|
650
|
+
const beforeBlock = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
651
|
+
const m2 = beforeBlock.find((l) => l.stream === s);
|
|
652
|
+
expect3(m2).toBeDefined();
|
|
653
|
+
const watermarkBefore = m2.at;
|
|
654
|
+
await store.block([{ ...m2, error: "permanent" }]);
|
|
655
|
+
let blockedFlag;
|
|
656
|
+
await store.query_streams(
|
|
657
|
+
(p) => {
|
|
658
|
+
blockedFlag = p.blocked;
|
|
659
|
+
},
|
|
660
|
+
{ stream: s, stream_exact: true, limit: 1 }
|
|
661
|
+
);
|
|
662
|
+
expect3(blockedFlag).toBe(true);
|
|
663
|
+
expect3(await store.unblock([s])).toBe(1);
|
|
664
|
+
const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
665
|
+
const back = after.find((l) => l.stream === s);
|
|
666
|
+
expect3(back).toBeDefined();
|
|
667
|
+
expect3(back.at).toBe(watermarkBefore);
|
|
668
|
+
expect3(back.retry).toBe(0);
|
|
669
|
+
});
|
|
670
|
+
it3("returns 0 when the stream is not blocked", async () => {
|
|
671
|
+
const s = `unblock-noop-${uid()}`;
|
|
672
|
+
await store.subscribe([{ stream: s }]);
|
|
673
|
+
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
674
|
+
expect3(await store.unblock([s])).toBe(0);
|
|
675
|
+
});
|
|
676
|
+
it3("returns 0 for unknown streams and empty input", async () => {
|
|
677
|
+
expect3(await store.unblock([`missing-${uid()}`])).toBe(0);
|
|
678
|
+
expect3(await store.unblock([])).toBe(0);
|
|
679
|
+
});
|
|
680
|
+
it3("only counts streams that were actually blocked", async () => {
|
|
681
|
+
const s1 = `unblock-mix-a-${uid()}`;
|
|
682
|
+
const s2 = `unblock-mix-b-${uid()}`;
|
|
683
|
+
await store.subscribe([{ stream: s1 }, { stream: s2 }]);
|
|
684
|
+
await store.commit(
|
|
685
|
+
s1,
|
|
686
|
+
[inc(1)],
|
|
687
|
+
makeMeta({ stream: s1 })
|
|
688
|
+
);
|
|
689
|
+
await store.commit(
|
|
690
|
+
s2,
|
|
691
|
+
[inc(1)],
|
|
692
|
+
makeMeta({ stream: s2 })
|
|
693
|
+
);
|
|
694
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
695
|
+
const m1 = leased.find((l) => l.stream === s1);
|
|
696
|
+
const others = leased.filter((l) => l.stream !== s1);
|
|
697
|
+
await store.ack(others);
|
|
698
|
+
await store.block([{ ...m1, error: "boom" }]);
|
|
699
|
+
expect3(await store.unblock([s1, s2])).toBe(1);
|
|
700
|
+
});
|
|
701
|
+
it3("filter form: unblocks by stream pattern", async () => {
|
|
702
|
+
const tag = uid();
|
|
703
|
+
const s1 = `unblock-filter-${tag}-a`;
|
|
704
|
+
const s2 = `unblock-filter-${tag}-b`;
|
|
705
|
+
const s3 = `other-${tag}`;
|
|
706
|
+
await store.subscribe([{ stream: s1 }, { stream: s2 }, { stream: s3 }]);
|
|
707
|
+
await store.commit(
|
|
708
|
+
s1,
|
|
709
|
+
[inc(1)],
|
|
710
|
+
makeMeta({ stream: s1 })
|
|
711
|
+
);
|
|
712
|
+
await store.commit(
|
|
713
|
+
s2,
|
|
714
|
+
[inc(1)],
|
|
715
|
+
makeMeta({ stream: s2 })
|
|
716
|
+
);
|
|
717
|
+
await store.commit(
|
|
718
|
+
s3,
|
|
719
|
+
[inc(1)],
|
|
720
|
+
makeMeta({ stream: s3 })
|
|
721
|
+
);
|
|
722
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
723
|
+
const blockable = leased.filter((l) => l.stream === s1 || l.stream === s2 || l.stream === s3).map((l) => ({ ...l, error: "boom" }));
|
|
724
|
+
await store.ack(
|
|
725
|
+
leased.filter(
|
|
726
|
+
(l) => !(l.stream === s1 || l.stream === s2 || l.stream === s3)
|
|
727
|
+
)
|
|
728
|
+
);
|
|
729
|
+
await store.block(blockable);
|
|
730
|
+
const count = await store.unblock({
|
|
731
|
+
stream: `^unblock-filter-${tag}-`
|
|
732
|
+
});
|
|
733
|
+
expect3(count).toBe(2);
|
|
734
|
+
const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
735
|
+
expect3(after.find((l) => l.stream === s3)).toBeUndefined();
|
|
736
|
+
expect3(after.find((l) => l.stream === s1)).toBeDefined();
|
|
737
|
+
expect3(after.find((l) => l.stream === s2)).toBeDefined();
|
|
738
|
+
});
|
|
739
|
+
it3("filter form: empty filter unblocks every blocked stream", async () => {
|
|
740
|
+
const tag = uid();
|
|
741
|
+
const s1 = `unblock-empty-${tag}-a`;
|
|
742
|
+
const s2 = `unblock-empty-${tag}-b`;
|
|
743
|
+
await store.subscribe([{ stream: s1 }, { stream: s2 }]);
|
|
744
|
+
await store.commit(
|
|
745
|
+
s1,
|
|
746
|
+
[inc(1)],
|
|
747
|
+
makeMeta({ stream: s1 })
|
|
748
|
+
);
|
|
749
|
+
await store.commit(
|
|
750
|
+
s2,
|
|
751
|
+
[inc(1)],
|
|
752
|
+
makeMeta({ stream: s2 })
|
|
753
|
+
);
|
|
754
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
755
|
+
const mine = leased.filter((l) => l.stream === s1 || l.stream === s2);
|
|
756
|
+
await store.ack(leased.filter((l) => !mine.includes(l)));
|
|
757
|
+
await store.block(
|
|
758
|
+
mine.map((l) => ({ ...l, error: "boom" }))
|
|
759
|
+
);
|
|
760
|
+
const count = await store.unblock({
|
|
761
|
+
stream: `^unblock-empty-${tag}-`
|
|
762
|
+
});
|
|
763
|
+
expect3(count).toBe(2);
|
|
764
|
+
});
|
|
765
|
+
it3("filter form: explicit blocked:false matches nothing", async () => {
|
|
766
|
+
const tag = uid();
|
|
767
|
+
const s = `unblock-blocked-false-${tag}`;
|
|
768
|
+
await store.subscribe([{ stream: s }]);
|
|
769
|
+
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
770
|
+
expect3(
|
|
771
|
+
await store.unblock({
|
|
772
|
+
stream: `^unblock-blocked-false-${tag}`,
|
|
773
|
+
blocked: false
|
|
774
|
+
})
|
|
775
|
+
).toBe(0);
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
describe3("reset filter form", () => {
|
|
779
|
+
it3("resets streams matching a stream pattern", async () => {
|
|
780
|
+
const tag = uid();
|
|
781
|
+
const s1 = `reset-filter-${tag}-a`;
|
|
782
|
+
const s2 = `reset-filter-${tag}-b`;
|
|
783
|
+
const other = `other-reset-${tag}`;
|
|
784
|
+
await store.subscribe([
|
|
785
|
+
{ stream: s1 },
|
|
786
|
+
{ stream: s2 },
|
|
787
|
+
{ stream: other }
|
|
788
|
+
]);
|
|
789
|
+
await store.commit(
|
|
790
|
+
s1,
|
|
791
|
+
[inc(1)],
|
|
792
|
+
makeMeta({ stream: s1 })
|
|
793
|
+
);
|
|
794
|
+
await store.commit(
|
|
795
|
+
s2,
|
|
796
|
+
[inc(1)],
|
|
797
|
+
makeMeta({ stream: s2 })
|
|
798
|
+
);
|
|
799
|
+
await store.commit(
|
|
800
|
+
other,
|
|
801
|
+
[inc(1)],
|
|
802
|
+
makeMeta({ stream: other })
|
|
803
|
+
);
|
|
804
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
805
|
+
const mine = leased.filter(
|
|
806
|
+
(l) => l.stream === s1 || l.stream === s2 || l.stream === other
|
|
807
|
+
);
|
|
808
|
+
await store.ack(mine.map((l) => ({ ...l, at: l.at + 100 })));
|
|
809
|
+
const count = await store.reset({ stream: `^reset-filter-${tag}-` });
|
|
810
|
+
expect3(count).toBe(2);
|
|
811
|
+
const positionFor = async (name) => {
|
|
812
|
+
let at = null;
|
|
813
|
+
await store.query_streams(
|
|
814
|
+
(p) => {
|
|
815
|
+
at = p.at;
|
|
816
|
+
},
|
|
817
|
+
{ stream: name, stream_exact: true, limit: 1 }
|
|
818
|
+
);
|
|
819
|
+
return at;
|
|
820
|
+
};
|
|
821
|
+
expect3(await positionFor(s1)).toBe(-1);
|
|
822
|
+
expect3(await positionFor(s2)).toBe(-1);
|
|
823
|
+
expect3(await positionFor(other)).toBeGreaterThan(-1);
|
|
824
|
+
});
|
|
825
|
+
it3("filter form: resets only blocked streams when blocked:true", async () => {
|
|
826
|
+
const tag = uid();
|
|
827
|
+
const s1 = `reset-blocked-${tag}-blocked`;
|
|
828
|
+
const s2 = `reset-blocked-${tag}-fine`;
|
|
829
|
+
await store.subscribe([{ stream: s1 }, { stream: s2 }]);
|
|
830
|
+
await store.commit(
|
|
831
|
+
s1,
|
|
832
|
+
[inc(1)],
|
|
833
|
+
makeMeta({ stream: s1 })
|
|
834
|
+
);
|
|
835
|
+
await store.commit(
|
|
836
|
+
s2,
|
|
837
|
+
[inc(1)],
|
|
838
|
+
makeMeta({ stream: s2 })
|
|
839
|
+
);
|
|
840
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
841
|
+
const m1 = leased.find((l) => l.stream === s1);
|
|
842
|
+
await store.ack(leased.filter((l) => l.stream !== s1));
|
|
843
|
+
await store.block([{ ...m1, error: "boom" }]);
|
|
844
|
+
const count = await store.reset({
|
|
845
|
+
stream: `^reset-blocked-${tag}-`,
|
|
846
|
+
blocked: true
|
|
847
|
+
});
|
|
848
|
+
expect3(count).toBe(1);
|
|
849
|
+
});
|
|
850
|
+
});
|
|
633
851
|
describe3("prioritize", () => {
|
|
634
852
|
it3("sets priority directly, overriding subscribe's max() rule", async () => {
|
|
635
853
|
const tag = uid();
|
|
@@ -809,6 +1027,229 @@ var runStoreTck = (options) => {
|
|
|
809
1027
|
expect3(unblocked).toEqual([sibling]);
|
|
810
1028
|
});
|
|
811
1029
|
});
|
|
1030
|
+
describe3("query_stats", () => {
|
|
1031
|
+
it3("array input \u2014 returns head per stream, absent when not in input", async () => {
|
|
1032
|
+
const tag = uid();
|
|
1033
|
+
const sA = `qst-${tag}-a`;
|
|
1034
|
+
const sB = `qst-${tag}-b`;
|
|
1035
|
+
const sUnasked = `qst-${tag}-unasked`;
|
|
1036
|
+
await store.commit(
|
|
1037
|
+
sA,
|
|
1038
|
+
[inc(1), inc(2)],
|
|
1039
|
+
makeMeta({ stream: sA })
|
|
1040
|
+
);
|
|
1041
|
+
await store.commit(
|
|
1042
|
+
sB,
|
|
1043
|
+
[dec(5)],
|
|
1044
|
+
makeMeta({ stream: sB })
|
|
1045
|
+
);
|
|
1046
|
+
await store.commit(
|
|
1047
|
+
sUnasked,
|
|
1048
|
+
[inc(99)],
|
|
1049
|
+
makeMeta({ stream: sUnasked })
|
|
1050
|
+
);
|
|
1051
|
+
const stats = await store.query_stats([sA, sB]);
|
|
1052
|
+
expect3(stats.size).toBe(2);
|
|
1053
|
+
expect3(stats.get(sA)?.head.name).toBe("Incremented");
|
|
1054
|
+
expect3((stats.get(sA)?.head.data).amount).toBe(2);
|
|
1055
|
+
expect3(stats.get(sB)?.head.name).toBe("Decremented");
|
|
1056
|
+
expect3((stats.get(sB)?.head.data).amount).toBe(5);
|
|
1057
|
+
expect3(stats.has(sUnasked)).toBe(false);
|
|
1058
|
+
const empty = await store.query_stats([]);
|
|
1059
|
+
expect3(empty.size).toBe(0);
|
|
1060
|
+
const unknown = await store.query_stats([`qst-${tag}-missing`]);
|
|
1061
|
+
expect3(unknown.size).toBe(0);
|
|
1062
|
+
});
|
|
1063
|
+
it3("tail returns the earliest event per stream", async () => {
|
|
1064
|
+
const tag = uid();
|
|
1065
|
+
const s = `qst-tail-${tag}`;
|
|
1066
|
+
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
1067
|
+
await store.commit(s, [inc(2)], makeMeta({ stream: s }));
|
|
1068
|
+
await store.commit(s, [inc(3)], makeMeta({ stream: s }));
|
|
1069
|
+
const stats = await store.query_stats([s], {
|
|
1070
|
+
tail: true
|
|
1071
|
+
});
|
|
1072
|
+
const r = stats.get(s);
|
|
1073
|
+
expect3(r?.head.name).toBe("Incremented");
|
|
1074
|
+
expect3((r?.head.data).amount).toBe(3);
|
|
1075
|
+
expect3(r?.tail?.name).toBe("Incremented");
|
|
1076
|
+
expect3((r?.tail?.data).amount).toBe(1);
|
|
1077
|
+
});
|
|
1078
|
+
it3("count + names \u2014 full aggregates including framework markers", async () => {
|
|
1079
|
+
const tag = uid();
|
|
1080
|
+
const s = `qst-cn-${tag}`;
|
|
1081
|
+
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
1082
|
+
await store.truncate([{ stream: s, snapshot: { count: 99 } }]);
|
|
1083
|
+
await store.commit(
|
|
1084
|
+
s,
|
|
1085
|
+
[inc(2), inc(3), dec(1)],
|
|
1086
|
+
makeMeta({ stream: s })
|
|
1087
|
+
);
|
|
1088
|
+
const stats = await store.query_stats([s], {
|
|
1089
|
+
count: true,
|
|
1090
|
+
names: true
|
|
1091
|
+
});
|
|
1092
|
+
const r = stats.get(s);
|
|
1093
|
+
expect3(r?.count).toBe(4);
|
|
1094
|
+
expect3(r?.names?.[SNAP_EVENT]).toBe(1);
|
|
1095
|
+
expect3(r?.names?.Incremented).toBe(2);
|
|
1096
|
+
expect3(r?.names?.Decremented).toBe(1);
|
|
1097
|
+
expect3(r?.names?.[SNAP_EVENT]).toBe(1);
|
|
1098
|
+
});
|
|
1099
|
+
it3("exclude shifts head past filtered events; stream absent when all filtered", async () => {
|
|
1100
|
+
const tag = uid();
|
|
1101
|
+
const s = `qst-excl-${tag}`;
|
|
1102
|
+
const sAllOut = `qst-allout-${tag}`;
|
|
1103
|
+
await store.commit(
|
|
1104
|
+
s,
|
|
1105
|
+
[inc(1), dec(2), inc(3)],
|
|
1106
|
+
makeMeta({ stream: s })
|
|
1107
|
+
);
|
|
1108
|
+
await store.commit(
|
|
1109
|
+
sAllOut,
|
|
1110
|
+
[inc(7)],
|
|
1111
|
+
makeMeta({ stream: sAllOut })
|
|
1112
|
+
);
|
|
1113
|
+
const all = await store.query_stats([s]);
|
|
1114
|
+
expect3(all.get(s)?.head.name).toBe("Incremented");
|
|
1115
|
+
expect3((all.get(s)?.head.data).amount).toBe(3);
|
|
1116
|
+
const excl = await store.query_stats([s], {
|
|
1117
|
+
exclude: ["Incremented"]
|
|
1118
|
+
});
|
|
1119
|
+
expect3(excl.get(s)?.head.name).toBe("Decremented");
|
|
1120
|
+
expect3((excl.get(s)?.head.data).amount).toBe(2);
|
|
1121
|
+
const wipe = await store.query_stats([sAllOut], {
|
|
1122
|
+
exclude: ["Incremented", "Decremented", "Reset"]
|
|
1123
|
+
});
|
|
1124
|
+
expect3(wipe.has(sAllOut)).toBe(false);
|
|
1125
|
+
const noTomb = await store.query_stats([s], {
|
|
1126
|
+
exclude: [TOMBSTONE_EVENT]
|
|
1127
|
+
});
|
|
1128
|
+
expect3(noTomb.get(s)?.head.name).toBe("Incremented");
|
|
1129
|
+
});
|
|
1130
|
+
it3("before \u2014 time travel narrows head/tail/count", async () => {
|
|
1131
|
+
const tag = uid();
|
|
1132
|
+
const s = `qst-tt-${tag}`;
|
|
1133
|
+
const c1 = await store.commit(
|
|
1134
|
+
s,
|
|
1135
|
+
[inc(1)],
|
|
1136
|
+
makeMeta({ stream: s })
|
|
1137
|
+
);
|
|
1138
|
+
const c2 = await store.commit(
|
|
1139
|
+
s,
|
|
1140
|
+
[inc(2)],
|
|
1141
|
+
makeMeta({ stream: s })
|
|
1142
|
+
);
|
|
1143
|
+
await store.commit(s, [inc(3)], makeMeta({ stream: s }));
|
|
1144
|
+
const before = c2[0].id;
|
|
1145
|
+
const stats = await store.query_stats([s], {
|
|
1146
|
+
tail: true,
|
|
1147
|
+
count: true,
|
|
1148
|
+
before
|
|
1149
|
+
});
|
|
1150
|
+
const r = stats.get(s);
|
|
1151
|
+
expect3(r?.count).toBe(1);
|
|
1152
|
+
expect3(r?.head.id).toBe(c1[0].id);
|
|
1153
|
+
expect3(r?.tail?.id).toBe(c1[0].id);
|
|
1154
|
+
const empty = await store.query_stats([s], {
|
|
1155
|
+
before: 0
|
|
1156
|
+
});
|
|
1157
|
+
expect3(empty.has(s)).toBe(false);
|
|
1158
|
+
});
|
|
1159
|
+
it3("filter form \u2014 stream regex, stream_exact, empty {} match", async () => {
|
|
1160
|
+
const tag = uid();
|
|
1161
|
+
const sA = `qsf-${tag}-orders-1`;
|
|
1162
|
+
const sB = `qsf-${tag}-orders-2`;
|
|
1163
|
+
const sOther = `qsf-${tag}-users-1`;
|
|
1164
|
+
await store.commit(
|
|
1165
|
+
sA,
|
|
1166
|
+
[inc(1)],
|
|
1167
|
+
makeMeta({ stream: sA })
|
|
1168
|
+
);
|
|
1169
|
+
await store.commit(
|
|
1170
|
+
sB,
|
|
1171
|
+
[inc(2)],
|
|
1172
|
+
makeMeta({ stream: sB })
|
|
1173
|
+
);
|
|
1174
|
+
await store.commit(
|
|
1175
|
+
sOther,
|
|
1176
|
+
[inc(3)],
|
|
1177
|
+
makeMeta({ stream: sOther })
|
|
1178
|
+
);
|
|
1179
|
+
const orders = await store.query_stats({
|
|
1180
|
+
stream: `^qsf-${tag}-orders-`
|
|
1181
|
+
});
|
|
1182
|
+
expect3([...orders.keys()].sort()).toEqual([sA, sB].sort());
|
|
1183
|
+
const exact = await store.query_stats({
|
|
1184
|
+
stream: sA,
|
|
1185
|
+
stream_exact: true
|
|
1186
|
+
});
|
|
1187
|
+
expect3([...exact.keys()]).toEqual([sA]);
|
|
1188
|
+
const all = await store.query_stats({
|
|
1189
|
+
stream: `^qsf-${tag}-`
|
|
1190
|
+
});
|
|
1191
|
+
expect3([...all.keys()].sort()).toEqual([sA, sB, sOther].sort());
|
|
1192
|
+
});
|
|
1193
|
+
it3("compose with query_streams for subscription-level filters", async () => {
|
|
1194
|
+
const tag = uid();
|
|
1195
|
+
const a = `qsc-${tag}-a`;
|
|
1196
|
+
const b = `qsc-${tag}-b`;
|
|
1197
|
+
await store.subscribe([{ stream: a }, { stream: b }]);
|
|
1198
|
+
await store.commit(a, [inc(1)], makeMeta({ stream: a }));
|
|
1199
|
+
await store.commit(b, [inc(2)], makeMeta({ stream: b }));
|
|
1200
|
+
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
1201
|
+
const mine = leased.find((l) => l.stream === a);
|
|
1202
|
+
expect3(mine).toBeDefined();
|
|
1203
|
+
const others = leased.filter((l) => l.stream !== a);
|
|
1204
|
+
await store.ack(others);
|
|
1205
|
+
await store.block([{ ...mine, error: "boom" }]);
|
|
1206
|
+
const blockedNames = [];
|
|
1207
|
+
await store.query_streams((p) => blockedNames.push(p.stream), {
|
|
1208
|
+
stream: `^qsc-${tag}-`,
|
|
1209
|
+
blocked: true
|
|
1210
|
+
});
|
|
1211
|
+
expect3(blockedNames).toEqual([a]);
|
|
1212
|
+
const stats = await store.query_stats(blockedNames);
|
|
1213
|
+
expect3(stats.get(a)?.head.name).toBe("Incremented");
|
|
1214
|
+
expect3(stats.has(b)).toBe(false);
|
|
1215
|
+
});
|
|
1216
|
+
it3("empty filter {} \u2014 matches every event-bearing stream", async () => {
|
|
1217
|
+
const tag = uid();
|
|
1218
|
+
const a = `qse-${tag}-a`;
|
|
1219
|
+
const b = `qse-${tag}-b`;
|
|
1220
|
+
await store.commit(a, [inc(1)], makeMeta({ stream: a }));
|
|
1221
|
+
await store.commit(b, [dec(2)], makeMeta({ stream: b }));
|
|
1222
|
+
const all = await store.query_stats({});
|
|
1223
|
+
expect3(all.has(a)).toBe(true);
|
|
1224
|
+
expect3(all.has(b)).toBe(true);
|
|
1225
|
+
});
|
|
1226
|
+
it3("stat-flag combinations \u2014 count-only, names-only, tail-only", async () => {
|
|
1227
|
+
const tag = uid();
|
|
1228
|
+
const s = `qsfl-${tag}`;
|
|
1229
|
+
await store.commit(
|
|
1230
|
+
s,
|
|
1231
|
+
[inc(1), inc(2), dec(3)],
|
|
1232
|
+
makeMeta({ stream: s })
|
|
1233
|
+
);
|
|
1234
|
+
const c = await store.query_stats([s], {
|
|
1235
|
+
count: true
|
|
1236
|
+
});
|
|
1237
|
+
expect3(c.get(s)?.count).toBe(3);
|
|
1238
|
+
expect3(c.get(s)?.names).toBeUndefined();
|
|
1239
|
+
expect3(c.get(s)?.tail).toBeUndefined();
|
|
1240
|
+
const n = await store.query_stats([s], {
|
|
1241
|
+
names: true
|
|
1242
|
+
});
|
|
1243
|
+
expect3(n.get(s)?.names).toEqual({ Incremented: 2, Decremented: 1 });
|
|
1244
|
+
expect3(n.get(s)?.count).toBeUndefined();
|
|
1245
|
+
expect3(n.get(s)?.tail).toBeUndefined();
|
|
1246
|
+
const t = await store.query_stats([s], { tail: true });
|
|
1247
|
+
expect3(t.get(s)?.tail?.name).toBe("Incremented");
|
|
1248
|
+
expect3((t.get(s)?.tail?.data).amount).toBe(1);
|
|
1249
|
+
expect3(t.get(s)?.count).toBeUndefined();
|
|
1250
|
+
expect3(t.get(s)?.names).toBeUndefined();
|
|
1251
|
+
});
|
|
1252
|
+
});
|
|
812
1253
|
describe3("query_streams anchor contract", () => {
|
|
813
1254
|
it3("plain regex without anchors is a substring match", async () => {
|
|
814
1255
|
const tag = uid();
|