@@ -50,6 +50,10 @@ func KVStore(
50
50
name : "CursorWithHints" ,
51
51
fn : KVCursorWithHints ,
52
52
},
53
+ {
54
+ name : "ForwardCursor" ,
55
+ fn : KVForwardCursor ,
56
+ },
53
57
{
54
58
name : "View" ,
55
59
fn : KVView ,
@@ -672,6 +676,239 @@ func KVCursorWithHints(
672
676
}
673
677
}
674
678
679
+ // KVForwardCursor tests the forward cursor contract for the key value store.
680
+ func KVForwardCursor (
681
+ init func (KVStoreFields , * testing.T ) (kv.Store , func ()),
682
+ t * testing.T ,
683
+ ) {
684
+ type args struct {
685
+ seek string
686
+ direction kv.CursorDirection
687
+ until string
688
+ hints []kv.CursorHint
689
+ }
690
+
691
+ pairs := func (keys ... string ) []kv.Pair {
692
+ p := make ([]kv.Pair , len (keys ))
693
+ for i , k := range keys {
694
+ p [i ].Key = []byte (k )
695
+ p [i ].Value = []byte ("val:" + k )
696
+ }
697
+ return p
698
+ }
699
+
700
+ tests := []struct {
701
+ name string
702
+ fields KVStoreFields
703
+ args args
704
+ exp []string
705
+ }{
706
+ {
707
+ name : "no hints" ,
708
+ fields : KVStoreFields {
709
+ Bucket : []byte ("bucket" ),
710
+ Pairs : pairs (
711
+ "aa/00" , "aa/01" ,
712
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
713
+ "bbb/00" , "bbb/01" , "bbb/02" ),
714
+ },
715
+ args : args {
716
+ seek : "aaa" ,
717
+ until : "bbb/00" ,
718
+ },
719
+ exp : []string {"aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" , "bbb/00" },
720
+ },
721
+ {
722
+ name : "prefix hint" ,
723
+ fields : KVStoreFields {
724
+ Bucket : []byte ("bucket" ),
725
+ Pairs : pairs (
726
+ "aa/00" , "aa/01" ,
727
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
728
+ "bbb/00" , "bbb/01" , "bbb/02" ),
729
+ },
730
+ args : args {
731
+ seek : "aaa" ,
732
+ until : "aaa/03" ,
733
+ hints : []kv.CursorHint {kv .WithCursorHintPrefix ("aaa/" )},
734
+ },
735
+ exp : []string {"aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" },
736
+ },
737
+ {
738
+ name : "start hint" ,
739
+ fields : KVStoreFields {
740
+ Bucket : []byte ("bucket" ),
741
+ Pairs : pairs (
742
+ "aa/00" , "aa/01" ,
743
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
744
+ "bbb/00" , "bbb/01" , "bbb/02" ),
745
+ },
746
+ args : args {
747
+ seek : "aaa" ,
748
+ until : "bbb/00" ,
749
+ hints : []kv.CursorHint {kv .WithCursorHintKeyStart ("aaa/" )},
750
+ },
751
+ exp : []string {"aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" , "bbb/00" },
752
+ },
753
+ {
754
+ name : "predicate for key" ,
755
+ fields : KVStoreFields {
756
+ Bucket : []byte ("bucket" ),
757
+ Pairs : pairs (
758
+ "aa/00" , "aa/01" ,
759
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
760
+ "bbb/00" , "bbb/01" , "bbb/02" ),
761
+ },
762
+ args : args {
763
+ seek : "aaa" ,
764
+ until : "aaa/03" ,
765
+ hints : []kv.CursorHint {
766
+ kv .WithCursorHintPredicate (func (key , _ []byte ) bool {
767
+ return len (key ) < 3 || string (key [:3 ]) == "aaa"
768
+ })},
769
+ },
770
+ exp : []string {"aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" },
771
+ },
772
+ {
773
+ name : "predicate for value" ,
774
+ fields : KVStoreFields {
775
+ Bucket : []byte ("bucket" ),
776
+ Pairs : pairs (
777
+ "aa/00" , "aa/01" ,
778
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
779
+ "bbb/00" , "bbb/01" , "bbb/02" ),
780
+ },
781
+ args : args {
782
+ seek : "" ,
783
+ until : "aa/01" ,
784
+ hints : []kv.CursorHint {
785
+ kv .WithCursorHintPredicate (func (_ , val []byte ) bool {
786
+ return len (val ) < 7 || string (val [:7 ]) == "val:aa/"
787
+ })},
788
+ },
789
+ exp : []string {"aa/00" , "aa/01" },
790
+ },
791
+ {
792
+ name : "no hints - descending" ,
793
+ fields : KVStoreFields {
794
+ Bucket : []byte ("bucket" ),
795
+ Pairs : pairs (
796
+ "aa/00" , "aa/01" ,
797
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
798
+ "bbb/00" , "bbb/01" , "bbb/02" ),
799
+ },
800
+ args : args {
801
+ seek : "bbb/00" ,
802
+ until : "aaa/00" ,
803
+ direction : kv .CursorDescending ,
804
+ },
805
+ exp : []string {"bbb/00" , "aaa/03" , "aaa/02" , "aaa/01" , "aaa/00" },
806
+ },
807
+ {
808
+ name : "start hint - descending" ,
809
+ fields : KVStoreFields {
810
+ Bucket : []byte ("bucket" ),
811
+ Pairs : pairs (
812
+ "aa/00" , "aa/01" ,
813
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
814
+ "bbb/00" , "bbb/01" , "bbb/02" ),
815
+ },
816
+ args : args {
817
+ seek : "bbb/00" ,
818
+ until : "aaa/00" ,
819
+ direction : kv .CursorDescending ,
820
+ hints : []kv.CursorHint {kv .WithCursorHintKeyStart ("aaa/" )},
821
+ },
822
+ exp : []string {"bbb/00" , "aaa/03" , "aaa/02" , "aaa/01" , "aaa/00" },
823
+ },
824
+ {
825
+ name : "predicate for key - descending" ,
826
+ fields : KVStoreFields {
827
+ Bucket : []byte ("bucket" ),
828
+ Pairs : pairs (
829
+ "aa/00" , "aa/01" ,
830
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
831
+ "bbb/00" , "bbb/01" , "bbb/02" ),
832
+ },
833
+ args : args {
834
+ seek : "aaa/03" ,
835
+ until : "aaa/00" ,
836
+ direction : kv .CursorDescending ,
837
+ hints : []kv.CursorHint {
838
+ kv .WithCursorHintPredicate (func (key , _ []byte ) bool {
839
+ return len (key ) < 3 || string (key [:3 ]) == "aaa"
840
+ })},
841
+ },
842
+ exp : []string {"aaa/03" , "aaa/02" , "aaa/01" , "aaa/00" },
843
+ },
844
+ {
845
+ name : "predicate for value - descending" ,
846
+ fields : KVStoreFields {
847
+ Bucket : []byte ("bucket" ),
848
+ Pairs : pairs (
849
+ "aa/00" , "aa/01" ,
850
+ "aaa/00" , "aaa/01" , "aaa/02" , "aaa/03" ,
851
+ "bbb/00" , "bbb/01" , "bbb/02" ),
852
+ },
853
+ args : args {
854
+ seek : "aa/01" ,
855
+ until : "aa/00" ,
856
+ direction : kv .CursorDescending ,
857
+ hints : []kv.CursorHint {
858
+ kv .WithCursorHintPredicate (func (_ , val []byte ) bool {
859
+ return len (val ) >= 7 && string (val [:7 ]) == "val:aa/"
860
+ })},
861
+ },
862
+ exp : []string {"aa/01" , "aa/00" },
863
+ },
864
+ }
865
+
866
+ for _ , tt := range tests {
867
+ t .Run (tt .name , func (t * testing.T ) {
868
+ s , fin := init (tt .fields , t )
869
+ defer fin ()
870
+
871
+ err := s .View (context .Background (), func (tx kv.Tx ) error {
872
+ b , err := tx .Bucket ([]byte ("bucket" ))
873
+ if err != nil {
874
+ t .Errorf ("unexpected error retrieving bucket: %v" , err )
875
+ return err
876
+ }
877
+
878
+ cur , err := b .ForwardCursor ([]byte (tt .args .seek ),
879
+ kv .WithCursorDirection (tt .args .direction ),
880
+ kv .WithCursorHints (tt .args .hints ... ))
881
+ if err != nil {
882
+ t .Errorf ("unexpected error: %v" , err )
883
+ return err
884
+ }
885
+
886
+ var got []string
887
+
888
+ k , _ := cur .Next ()
889
+ for len (k ) > 0 {
890
+ got = append (got , string (k ))
891
+ if string (k ) == tt .args .until {
892
+ break
893
+ }
894
+
895
+ k , _ = cur .Next ()
896
+ }
897
+
898
+ if exp := tt .exp ; ! cmp .Equal (got , exp ) {
899
+ t .Errorf ("unexpected cursor values: -got/+exp\n %v" , cmp .Diff (got , exp ))
900
+ }
901
+
902
+ return nil
903
+ })
904
+
905
+ if err != nil {
906
+ t .Fatalf ("error during view transaction: %v" , err )
907
+ }
908
+ })
909
+ }
910
+ }
911
+
675
912
// KVView tests the view method contract for the key value store.
676
913
func KVView (
677
914
init func (KVStoreFields , * testing.T ) (kv.Store , func ()),
0 commit comments