github.com/ipld/go-ipld-prime@v0.21.0/traversal/selector/matcher_test.go (about) 1 package selector_test 2 3 import ( 4 "fmt" 5 "math" 6 "regexp" 7 "testing" 8 9 qt "github.com/frankban/quicktest" 10 11 "github.com/ipld/go-ipld-prime/datamodel" 12 "github.com/ipld/go-ipld-prime/fluent/qp" 13 "github.com/ipld/go-ipld-prime/node/basicnode" 14 "github.com/ipld/go-ipld-prime/testutil" 15 "github.com/ipld/go-ipld-prime/traversal" 16 "github.com/ipld/go-ipld-prime/traversal/selector" 17 ) 18 19 func TestSubsetMatch(t *testing.T) { 20 expectedString := "foobarbaz!" 21 nodes := []struct { 22 name string 23 node datamodel.Node 24 }{ 25 {"stringNode", basicnode.NewString(expectedString)}, 26 {"bytesNode", testutil.NewSimpleBytes([]byte(expectedString))}, 27 {"largeBytesNode", testutil.NewMultiByteNode( 28 []byte("foo"), 29 []byte("bar"), 30 []byte("baz"), 31 []byte("!"), 32 )}, 33 } 34 35 // selector for a slice of the value of the "bipbop" field within a map 36 mkRangeSelector := func(from int64, to int64) (datamodel.Node, error) { 37 return qp.BuildMap(basicnode.Prototype.Map, 1, func(na datamodel.MapAssembler) { 38 qp.MapEntry(na, selector.SelectorKey_ExploreFields, qp.Map(1, func(na datamodel.MapAssembler) { 39 qp.MapEntry(na, selector.SelectorKey_Fields, qp.Map(1, func(na datamodel.MapAssembler) { 40 qp.MapEntry(na, "bipbop", qp.Map(1, func(na datamodel.MapAssembler) { 41 qp.MapEntry(na, selector.SelectorKey_Matcher, qp.Map(1, func(na datamodel.MapAssembler) { 42 qp.MapEntry(na, selector.SelectorKey_Subset, qp.Map(1, func(na datamodel.MapAssembler) { 43 qp.MapEntry(na, selector.SelectorKey_From, qp.Int(from)) 44 qp.MapEntry(na, selector.SelectorKey_To, qp.Int(to)) 45 })) 46 })) 47 })) 48 })) 49 })) 50 }) 51 } 52 53 for _, tc := range []struct { 54 from int64 55 to int64 56 exp string 57 match bool 58 }{ 59 {0, math.MaxInt64, expectedString, true}, 60 {0, int64(len(expectedString)), expectedString, true}, 61 {0, 0, "", true}, 62 {0, 1, "f", true}, 63 {0, 2, "fo", true}, 64 {0, 3, "foo", true}, 65 {0, 4, "foob", true}, 66 {1, 4, "oob", true}, 67 {2, 4, "ob", true}, 68 {3, 4, "b", true}, 69 {4, 4, "", true}, 70 {4, math.MaxInt64, "arbaz!", true}, 71 {4, int64(len(expectedString)), "arbaz!", true}, 72 {4, int64(len(expectedString) - 1), "arbaz", true}, 73 {0, int64(len(expectedString) - 1), expectedString[0 : len(expectedString)-1], true}, 74 {0, int64(len(expectedString) - 2), expectedString[0 : len(expectedString)-2], true}, 75 {0, -1, expectedString[0 : len(expectedString)-1], true}, 76 {0, -2, expectedString[0 : len(expectedString)-2], true}, 77 {-2, -1, "z", true}, 78 {-1, math.MaxInt64, "!", true}, 79 {-int64(len(expectedString)), math.MaxInt64, expectedString, true}, 80 {math.MaxInt64 - 1, math.MaxInt64, "", false}, 81 {int64(len(expectedString)), math.MaxInt64, "", false}, 82 {-1, -2, "", false}, // To < From, no match 83 {-1, -1, "", true}, // To==From, match zero bytes 84 {-1000, -100, "", false}, // From undeflow, adjusted to 0, To underflow, not adjusted, To < From, no match 85 {-100, -1000, "", false}, // From undeflow, adjusted to 0, To underflow, adjusted to 0, To < From, no match 86 {-1000, 1000, expectedString, true}, // From undeflow, adjusted to 0, To overflow, adjusted to len, match all 87 } { 88 for _, variant := range nodes { 89 t.Run(fmt.Sprintf("%s[%d:%d]", variant.name, tc.from, tc.to), func(t *testing.T) { 90 selNode, err := mkRangeSelector(tc.from, tc.to) 91 qt.Assert(t, err, qt.IsNil) 92 ss, err := selector.ParseSelector(selNode) 93 qt.Assert(t, err, qt.IsNil) 94 95 // node that the selector will match, with our variant node embedded in the "bipbop" field 96 n, err := qp.BuildMap(basicnode.Prototype.Map, 1, func(na datamodel.MapAssembler) { 97 qp.MapEntry(na, "bipbop", qp.Node(variant.node)) 98 }) 99 100 var got datamodel.Node 101 qt.Assert(t, err, qt.IsNil) 102 err = traversal.WalkMatching(n, ss, func(prog traversal.Progress, n datamodel.Node) error { 103 qt.Assert(t, got, qt.IsNil) 104 got = n 105 return nil 106 }) 107 qt.Assert(t, err, qt.IsNil) 108 109 if tc.match { 110 qt.Assert(t, got, qt.IsNotNil) 111 qt.Assert(t, got.Kind(), qt.Equals, variant.node.Kind()) 112 var gotString string 113 switch got.Kind() { 114 case datamodel.Kind_String: 115 gotString, err = got.AsString() 116 qt.Assert(t, err, qt.IsNil) 117 case datamodel.Kind_Bytes: 118 byts, err := got.AsBytes() 119 qt.Assert(t, err, qt.IsNil) 120 gotString = string(byts) 121 } 122 qt.Assert(t, gotString, qt.DeepEquals, tc.exp) 123 } else { 124 qt.Assert(t, got, qt.IsNil) 125 } 126 }) 127 } 128 } 129 130 // when both are positive, we can validate ranges up-front 131 t.Run("invalid range", func(t *testing.T) { 132 selNode, err := mkRangeSelector(1000, 100) 133 qt.Assert(t, err, qt.IsNil) 134 re, err := regexp.Compile("from.*less than or equal to.*to") 135 qt.Assert(t, err, qt.IsNil) 136 ss, err := selector.ParseSelector(selNode) 137 qt.Assert(t, ss, qt.IsNil) 138 qt.Assert(t, err, qt.ErrorMatches, re) 139 }) 140 }