@rotorsoft/act-tck 0.1.0 → 0.2.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.js CHANGED
@@ -630,6 +630,216 @@ var runStoreTck = (options) => {
630
630
  expect3(await store.reset([])).toBe(0);
631
631
  });
632
632
  });
633
+ describe3("unblock", () => {
634
+ it3("clears blocked flag and preserves the watermark", async () => {
635
+ const s = `unblock-${uid()}`;
636
+ await store.subscribe([{ stream: s }]);
637
+ await store.commit(s, [inc(1)], makeMeta({ stream: s }));
638
+ await store.commit(s, [inc(2)], makeMeta({ stream: s }));
639
+ const first = await store.claim(100, 0, `w-${uid()}`, 1e5);
640
+ const m1 = first.find((l) => l.stream === s);
641
+ await store.ack([{ ...m1, at: m1.at }]);
642
+ const beforeBlock = await store.claim(100, 0, `w-${uid()}`, 1e5);
643
+ const m2 = beforeBlock.find((l) => l.stream === s);
644
+ expect3(m2).toBeDefined();
645
+ const watermarkBefore = m2.at;
646
+ await store.block([{ ...m2, error: "permanent" }]);
647
+ let blockedFlag;
648
+ await store.query_streams(
649
+ (p) => {
650
+ blockedFlag = p.blocked;
651
+ },
652
+ { stream: s, stream_exact: true, limit: 1 }
653
+ );
654
+ expect3(blockedFlag).toBe(true);
655
+ expect3(await store.unblock([s])).toBe(1);
656
+ const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
657
+ const back = after.find((l) => l.stream === s);
658
+ expect3(back).toBeDefined();
659
+ expect3(back.at).toBe(watermarkBefore);
660
+ expect3(back.retry).toBe(0);
661
+ });
662
+ it3("returns 0 when the stream is not blocked", async () => {
663
+ const s = `unblock-noop-${uid()}`;
664
+ await store.subscribe([{ stream: s }]);
665
+ await store.commit(s, [inc(1)], makeMeta({ stream: s }));
666
+ expect3(await store.unblock([s])).toBe(0);
667
+ });
668
+ it3("returns 0 for unknown streams and empty input", async () => {
669
+ expect3(await store.unblock([`missing-${uid()}`])).toBe(0);
670
+ expect3(await store.unblock([])).toBe(0);
671
+ });
672
+ it3("only counts streams that were actually blocked", async () => {
673
+ const s1 = `unblock-mix-a-${uid()}`;
674
+ const s2 = `unblock-mix-b-${uid()}`;
675
+ await store.subscribe([{ stream: s1 }, { stream: s2 }]);
676
+ await store.commit(
677
+ s1,
678
+ [inc(1)],
679
+ makeMeta({ stream: s1 })
680
+ );
681
+ await store.commit(
682
+ s2,
683
+ [inc(1)],
684
+ makeMeta({ stream: s2 })
685
+ );
686
+ const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
687
+ const m1 = leased.find((l) => l.stream === s1);
688
+ const others = leased.filter((l) => l.stream !== s1);
689
+ await store.ack(others);
690
+ await store.block([{ ...m1, error: "boom" }]);
691
+ expect3(await store.unblock([s1, s2])).toBe(1);
692
+ });
693
+ it3("filter form: unblocks by stream pattern", async () => {
694
+ const tag = uid();
695
+ const s1 = `unblock-filter-${tag}-a`;
696
+ const s2 = `unblock-filter-${tag}-b`;
697
+ const s3 = `other-${tag}`;
698
+ await store.subscribe([{ stream: s1 }, { stream: s2 }, { stream: s3 }]);
699
+ await store.commit(
700
+ s1,
701
+ [inc(1)],
702
+ makeMeta({ stream: s1 })
703
+ );
704
+ await store.commit(
705
+ s2,
706
+ [inc(1)],
707
+ makeMeta({ stream: s2 })
708
+ );
709
+ await store.commit(
710
+ s3,
711
+ [inc(1)],
712
+ makeMeta({ stream: s3 })
713
+ );
714
+ const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
715
+ const blockable = leased.filter((l) => l.stream === s1 || l.stream === s2 || l.stream === s3).map((l) => ({ ...l, error: "boom" }));
716
+ await store.ack(
717
+ leased.filter(
718
+ (l) => !(l.stream === s1 || l.stream === s2 || l.stream === s3)
719
+ )
720
+ );
721
+ await store.block(blockable);
722
+ const count = await store.unblock({
723
+ stream: `^unblock-filter-${tag}-`
724
+ });
725
+ expect3(count).toBe(2);
726
+ const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
727
+ expect3(after.find((l) => l.stream === s3)).toBeUndefined();
728
+ expect3(after.find((l) => l.stream === s1)).toBeDefined();
729
+ expect3(after.find((l) => l.stream === s2)).toBeDefined();
730
+ });
731
+ it3("filter form: empty filter unblocks every blocked stream", async () => {
732
+ const tag = uid();
733
+ const s1 = `unblock-empty-${tag}-a`;
734
+ const s2 = `unblock-empty-${tag}-b`;
735
+ await store.subscribe([{ stream: s1 }, { stream: s2 }]);
736
+ await store.commit(
737
+ s1,
738
+ [inc(1)],
739
+ makeMeta({ stream: s1 })
740
+ );
741
+ await store.commit(
742
+ s2,
743
+ [inc(1)],
744
+ makeMeta({ stream: s2 })
745
+ );
746
+ const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
747
+ const mine = leased.filter((l) => l.stream === s1 || l.stream === s2);
748
+ await store.ack(leased.filter((l) => !mine.includes(l)));
749
+ await store.block(
750
+ mine.map((l) => ({ ...l, error: "boom" }))
751
+ );
752
+ const count = await store.unblock({
753
+ stream: `^unblock-empty-${tag}-`
754
+ });
755
+ expect3(count).toBe(2);
756
+ });
757
+ it3("filter form: explicit blocked:false matches nothing", async () => {
758
+ const tag = uid();
759
+ const s = `unblock-blocked-false-${tag}`;
760
+ await store.subscribe([{ stream: s }]);
761
+ await store.commit(s, [inc(1)], makeMeta({ stream: s }));
762
+ expect3(
763
+ await store.unblock({
764
+ stream: `^unblock-blocked-false-${tag}`,
765
+ blocked: false
766
+ })
767
+ ).toBe(0);
768
+ });
769
+ });
770
+ describe3("reset filter form", () => {
771
+ it3("resets streams matching a stream pattern", async () => {
772
+ const tag = uid();
773
+ const s1 = `reset-filter-${tag}-a`;
774
+ const s2 = `reset-filter-${tag}-b`;
775
+ const other = `other-reset-${tag}`;
776
+ await store.subscribe([
777
+ { stream: s1 },
778
+ { stream: s2 },
779
+ { stream: other }
780
+ ]);
781
+ await store.commit(
782
+ s1,
783
+ [inc(1)],
784
+ makeMeta({ stream: s1 })
785
+ );
786
+ await store.commit(
787
+ s2,
788
+ [inc(1)],
789
+ makeMeta({ stream: s2 })
790
+ );
791
+ await store.commit(
792
+ other,
793
+ [inc(1)],
794
+ makeMeta({ stream: other })
795
+ );
796
+ const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
797
+ const mine = leased.filter(
798
+ (l) => l.stream === s1 || l.stream === s2 || l.stream === other
799
+ );
800
+ await store.ack(mine.map((l) => ({ ...l, at: l.at + 100 })));
801
+ const count = await store.reset({ stream: `^reset-filter-${tag}-` });
802
+ expect3(count).toBe(2);
803
+ const positionFor = async (name) => {
804
+ let at = null;
805
+ await store.query_streams(
806
+ (p) => {
807
+ at = p.at;
808
+ },
809
+ { stream: name, stream_exact: true, limit: 1 }
810
+ );
811
+ return at;
812
+ };
813
+ expect3(await positionFor(s1)).toBe(-1);
814
+ expect3(await positionFor(s2)).toBe(-1);
815
+ expect3(await positionFor(other)).toBeGreaterThan(-1);
816
+ });
817
+ it3("filter form: resets only blocked streams when blocked:true", async () => {
818
+ const tag = uid();
819
+ const s1 = `reset-blocked-${tag}-blocked`;
820
+ const s2 = `reset-blocked-${tag}-fine`;
821
+ await store.subscribe([{ stream: s1 }, { stream: s2 }]);
822
+ await store.commit(
823
+ s1,
824
+ [inc(1)],
825
+ makeMeta({ stream: s1 })
826
+ );
827
+ await store.commit(
828
+ s2,
829
+ [inc(1)],
830
+ makeMeta({ stream: s2 })
831
+ );
832
+ const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
833
+ const m1 = leased.find((l) => l.stream === s1);
834
+ await store.ack(leased.filter((l) => l.stream !== s1));
835
+ await store.block([{ ...m1, error: "boom" }]);
836
+ const count = await store.reset({
837
+ stream: `^reset-blocked-${tag}-`,
838
+ blocked: true
839
+ });
840
+ expect3(count).toBe(1);
841
+ });
842
+ });
633
843
  describe3("prioritize", () => {
634
844
  it3("sets priority directly, overriding subscribe's max() rule", async () => {
635
845
  const tag = uid();