github.com/ipld/go-ipld-prime@v0.21.0/traversal/selector/exploreRecursive_test.go (about) 1 package selector 2 3 import ( 4 "strings" 5 "testing" 6 7 qt "github.com/frankban/quicktest" 8 9 "github.com/ipld/go-ipld-prime/codec/dagjson" 10 "github.com/ipld/go-ipld-prime/datamodel" 11 "github.com/ipld/go-ipld-prime/fluent" 12 "github.com/ipld/go-ipld-prime/node/basicnode" 13 ) 14 15 func TestParseExploreRecursive(t *testing.T) { 16 t.Run("parsing non map node should error", func(t *testing.T) { 17 sn := basicnode.NewInt(0) 18 _, err := ParseContext{}.ParseExploreRecursive(sn) 19 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: selector body must be a map") 20 }) 21 t.Run("parsing map node without sequence field should error", func(t *testing.T) { 22 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { 23 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 24 na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) 25 }) 26 }) 27 _, err := ParseContext{}.ParseExploreRecursive(sn) 28 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: sequence field must be present in ExploreRecursive selector") 29 }) 30 t.Run("parsing map node without limit field should error", func(t *testing.T) { 31 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 1, func(na fluent.MapAssembler) { 32 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 33 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 34 }) 35 }) 36 _, err := ParseContext{}.ParseExploreRecursive(sn) 37 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: limit field must be present in ExploreRecursive selector") 38 }) 39 t.Run("parsing map node with limit field that is not a map should fail", func(t *testing.T) { 40 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 41 na.AssembleEntry(SelectorKey_Limit).AssignString("cheese") 42 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 43 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 44 }) 45 }) 46 _, err := ParseContext{}.ParseExploreRecursive(sn) 47 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a map") 48 }) 49 t.Run("parsing map node with limit field that is not a single entry map should fail", func(t *testing.T) { 50 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 51 na.AssembleEntry(SelectorKey_Limit).CreateMap(2, func(na fluent.MapAssembler) { 52 na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) 53 na.AssembleEntry(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) 54 }) 55 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 56 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 57 }) 58 }) 59 _, err := ParseContext{}.ParseExploreRecursive(sn) 60 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a single-entry map") 61 }) 62 t.Run("parsing map node with limit field that does not have a known key should fail", func(t *testing.T) { 63 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 64 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 65 na.AssembleEntry("applesauce").AssignInt(2) 66 }) 67 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 68 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 69 }) 70 }) 71 _, err := ParseContext{}.ParseExploreRecursive(sn) 72 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: \"applesauce\" is not a known member of the limit union in ExploreRecursive") 73 }) 74 t.Run("parsing map node with limit field of type depth that is not an int should error", func(t *testing.T) { 75 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 76 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 77 na.AssembleEntry(SelectorKey_LimitDepth).AssignString("cheese") 78 }) 79 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 80 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 81 }) 82 }) 83 _, err := ParseContext{}.ParseExploreRecursive(sn) 84 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: limit field of type depth must be a number in ExploreRecursive selector") 85 }) 86 t.Run("parsing map node with sequence field with invalid selector node should return child's error", func(t *testing.T) { 87 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 88 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 89 na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) 90 }) 91 na.AssembleEntry(SelectorKey_Sequence).AssignInt(0) 92 }) 93 _, err := ParseContext{}.ParseExploreRecursive(sn) 94 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: selector is a keyed union and thus must be a map") 95 }) 96 t.Run("parsing map node with sequence field with valid selector w/o ExploreRecursiveEdge should not parse", func(t *testing.T) { 97 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 98 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 99 na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) 100 }) 101 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 102 na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { 103 na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { 104 na.AssembleEntry(SelectorKey_Matcher).CreateMap(0, func(na fluent.MapAssembler) {}) 105 }) 106 }) 107 }) 108 }) 109 _, err := ParseContext{}.ParseExploreRecursive(sn) 110 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: ExploreRecursive must have at least one ExploreRecursiveEdge") 111 }) 112 t.Run("parsing map node that is ExploreRecursiveEdge without ExploreRecursive parent should not parse", func(t *testing.T) { 113 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 0, func(na fluent.MapAssembler) {}) 114 _, err := ParseContext{}.ParseExploreRecursiveEdge(sn) 115 qt.Check(t, err, qt.ErrorMatches, "selector spec parse rejected: ExploreRecursiveEdge must be beneath ExploreRecursive") 116 }) 117 t.Run("parsing map node with sequence field with valid selector node should parse", func(t *testing.T) { 118 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 119 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 120 na.AssembleEntry(SelectorKey_LimitDepth).AssignInt(2) 121 }) 122 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 123 na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { 124 na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { 125 na.AssembleEntry(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) 126 }) 127 }) 128 }) 129 }) 130 s, err := ParseContext{}.ParseExploreRecursive(sn) 131 qt.Check(t, err, qt.IsNil) 132 qt.Check(t, s, qt.Equals, ExploreRecursive{ExploreAll{ExploreRecursiveEdge{}}, ExploreAll{ExploreRecursiveEdge{}}, RecursionLimit{RecursionLimit_Depth, 2}, nil}) 133 }) 134 135 t.Run("parsing map node with sequence field with valid selector node and limit type none should parse", func(t *testing.T) { 136 sn := fluent.MustBuildMap(basicnode.Prototype__Map{}, 2, func(na fluent.MapAssembler) { 137 na.AssembleEntry(SelectorKey_Limit).CreateMap(1, func(na fluent.MapAssembler) { 138 na.AssembleEntry(SelectorKey_LimitNone).CreateMap(0, func(na fluent.MapAssembler) {}) 139 }) 140 na.AssembleEntry(SelectorKey_Sequence).CreateMap(1, func(na fluent.MapAssembler) { 141 na.AssembleEntry(SelectorKey_ExploreAll).CreateMap(1, func(na fluent.MapAssembler) { 142 na.AssembleEntry(SelectorKey_Next).CreateMap(1, func(na fluent.MapAssembler) { 143 na.AssembleEntry(SelectorKey_ExploreRecursiveEdge).CreateMap(0, func(na fluent.MapAssembler) {}) 144 }) 145 }) 146 }) 147 }) 148 s, err := ParseContext{}.ParseExploreRecursive(sn) 149 qt.Check(t, err, qt.IsNil) 150 qt.Check(t, s, qt.Equals, ExploreRecursive{ExploreAll{ExploreRecursiveEdge{}}, ExploreAll{ExploreRecursiveEdge{}}, RecursionLimit{RecursionLimit_None, 0}, nil}) 151 }) 152 153 } 154 155 /* 156 157 { 158 exploreRecursive: { 159 maxDepth: 3 160 sequence: { 161 exploreFields: { 162 fields: { 163 Parents: { 164 exploreAll: { 165 exploreRecursiveEdge: {} 166 } 167 } 168 } 169 } 170 } 171 } 172 } 173 174 */ 175 176 func TestExploreRecursiveExplore(t *testing.T) { 177 recursiveEdge := ExploreRecursiveEdge{} 178 maxDepth := int64(3) 179 var rs Selector 180 t.Run("exploring should traverse until we get to maxDepth", func(t *testing.T) { 181 parentsSelector := ExploreAll{recursiveEdge} 182 subTree := ExploreFields{map[string]Selector{"Parents": parentsSelector}, []datamodel.PathSegment{datamodel.PathSegmentOfString("Parents")}} 183 rs = ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil} 184 nodeString := `{ 185 "Parents": [ 186 { 187 "Parents": [ 188 { 189 "Parents": [ 190 { 191 "Parents": [] 192 } 193 ] 194 } 195 ] 196 } 197 ] 198 } 199 ` 200 nb := basicnode.Prototype__Any{}.NewBuilder() 201 err := dagjson.Decode(nb, strings.NewReader(nodeString)) 202 qt.Check(t, err, qt.IsNil) 203 rn := nb.Build() 204 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 205 rn, err = rn.LookupByString("Parents") 206 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 207 qt.Check(t, err, qt.IsNil) 208 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 209 rn, err = rn.LookupByIndex(0) 210 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 211 qt.Check(t, err, qt.IsNil) 212 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 213 214 rn, err = rn.LookupByString("Parents") 215 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 216 qt.Check(t, err, qt.IsNil) 217 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 218 rn, err = rn.LookupByIndex(0) 219 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth - 2}, nil}) 220 qt.Check(t, err, qt.IsNil) 221 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 222 rn, err = rn.LookupByString("Parents") 223 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 2}, nil}) 224 qt.Check(t, err, qt.IsNil) 225 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 226 _, err = rn.LookupByIndex(0) 227 qt.Check(t, rs, qt.IsNil) 228 qt.Check(t, err, qt.IsNil) 229 }) 230 231 t.Run("exploring should traverse indefinitely if no depth specified", func(t *testing.T) { 232 parentsSelector := ExploreAll{recursiveEdge} 233 subTree := ExploreFields{map[string]Selector{"Parents": parentsSelector}, []datamodel.PathSegment{datamodel.PathSegmentOfString("Parents")}} 234 rs = ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_None, 0}, nil} 235 nodeString := `{ 236 "Parents": [ 237 { 238 "Parents": [ 239 { 240 "Parents": [ 241 { 242 "Parents": [] 243 } 244 ] 245 } 246 ] 247 } 248 ] 249 } 250 ` 251 nb := basicnode.Prototype__Any{}.NewBuilder() 252 err := dagjson.Decode(nb, strings.NewReader(nodeString)) 253 qt.Check(t, err, qt.IsNil) 254 rn := nb.Build() 255 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 256 rn, err = rn.LookupByString("Parents") 257 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_None, 0}, nil}) 258 qt.Check(t, err, qt.IsNil) 259 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 260 rn, err = rn.LookupByIndex(0) 261 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_None, 0}, nil}) 262 qt.Check(t, err, qt.IsNil) 263 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 264 rn, err = rn.LookupByString("Parents") 265 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_None, 0}, nil}) 266 qt.Check(t, err, qt.IsNil) 267 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 268 rn, err = rn.LookupByIndex(0) 269 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_None, 0}, nil}) 270 qt.Check(t, err, qt.IsNil) 271 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 272 rn, err = rn.LookupByString("Parents") 273 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_None, 0}, nil}) 274 qt.Check(t, err, qt.IsNil) 275 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 276 rn, err = rn.LookupByIndex(0) 277 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_None, 0}, nil}) 278 qt.Check(t, err, qt.IsNil) 279 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 280 _, err = rn.LookupByString("Parents") 281 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_None, 0}, nil}) 282 qt.Check(t, err, qt.IsNil) 283 }) 284 285 t.Run("exploring should continue till we get to selector that returns nil on explore", func(t *testing.T) { 286 parentsSelector := ExploreIndex{recursiveEdge, [1]datamodel.PathSegment{datamodel.PathSegmentOfInt(1)}} 287 subTree := ExploreFields{map[string]Selector{"Parents": parentsSelector}, []datamodel.PathSegment{datamodel.PathSegmentOfString("Parents")}} 288 rs = ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil} 289 nodeString := `{ 290 "Parents": { 291 } 292 } 293 ` 294 nb := basicnode.Prototype__Any{}.NewBuilder() 295 err := dagjson.Decode(nb, strings.NewReader(nodeString)) 296 qt.Check(t, err, qt.IsNil) 297 rn := nb.Build() 298 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 299 rn, err = rn.LookupByString("Parents") 300 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 301 qt.Check(t, err, qt.IsNil) 302 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 303 qt.Check(t, rs, qt.IsNil) 304 }) 305 t.Run("exploring should work when there is nested recursion", func(t *testing.T) { 306 parentsSelector := ExploreAll{recursiveEdge} 307 sideSelector := ExploreAll{recursiveEdge} 308 subTree := ExploreFields{map[string]Selector{ 309 "Parents": parentsSelector, 310 "Side": ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}, 311 }, []datamodel.PathSegment{ 312 datamodel.PathSegmentOfString("Parents"), 313 datamodel.PathSegmentOfString("Side"), 314 }, 315 } 316 s := ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil} 317 nodeString := `{ 318 "Parents": [ 319 { 320 "Parents": [], 321 "Side": { 322 "cheese": { 323 "whiz": { 324 } 325 } 326 } 327 } 328 ], 329 "Side": { 330 "real": { 331 "apple": { 332 "sauce": { 333 } 334 } 335 } 336 } 337 } 338 ` 339 nb := basicnode.Prototype__Any{}.NewBuilder() 340 err := dagjson.Decode(nb, strings.NewReader(nodeString)) 341 qt.Check(t, err, qt.IsNil) 342 n := nb.Build() 343 344 // traverse down Parent nodes 345 rn := n 346 rs = s 347 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 348 rn, err = rn.LookupByString("Parents") 349 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 350 qt.Check(t, err, qt.IsNil) 351 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 352 rn, err = rn.LookupByIndex(0) 353 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 354 qt.Check(t, err, qt.IsNil) 355 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 356 _, err = rn.LookupByString("Parents") 357 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 358 qt.Check(t, err, qt.IsNil) 359 360 // traverse down top level Side tree (nested recursion) 361 rn = n 362 rs = s 363 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Side")) 364 rn, err = rn.LookupByString("Side") 365 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 366 qt.Check(t, err, qt.IsNil) 367 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("real")) 368 rn, err = rn.LookupByString("real") 369 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 370 qt.Check(t, err, qt.IsNil) 371 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("apple")) 372 rn, err = rn.LookupByString("apple") 373 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 2}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 374 qt.Check(t, err, qt.IsNil) 375 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("sauce")) 376 _, err = rn.LookupByString("sauce") 377 qt.Check(t, rs, qt.IsNil) 378 qt.Check(t, err, qt.IsNil) 379 380 // traverse once down Parent (top level recursion) then down Side tree (nested recursion) 381 rn = n 382 rs = s 383 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 384 rn, err = rn.LookupByString("Parents") 385 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 386 qt.Check(t, err, qt.IsNil) 387 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 388 rn, err = rn.LookupByIndex(0) 389 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 390 qt.Check(t, err, qt.IsNil) 391 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Side")) 392 rn, err = rn.LookupByString("Side") 393 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 394 qt.Check(t, err, qt.IsNil) 395 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("cheese")) 396 rn, err = rn.LookupByString("cheese") 397 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 398 qt.Check(t, err, qt.IsNil) 399 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("whiz")) 400 _, err = rn.LookupByString("whiz") 401 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreRecursive{sideSelector, sideSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 2}, nil}, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 402 qt.Check(t, err, qt.IsNil) 403 }) 404 t.Run("exploring should work with explore union and recursion", func(t *testing.T) { 405 parentsSelector := ExploreUnion{[]Selector{ExploreAll{Matcher{}}, ExploreIndex{recursiveEdge, [1]datamodel.PathSegment{datamodel.PathSegmentOfInt(0)}}}} 406 subTree := ExploreFields{map[string]Selector{"Parents": parentsSelector}, []datamodel.PathSegment{datamodel.PathSegmentOfString("Parents")}} 407 rs = ExploreRecursive{subTree, subTree, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil} 408 nodeString := `{ 409 "Parents": [ 410 { 411 "Parents": [ 412 { 413 "Parents": [ 414 { 415 "Parents": [] 416 } 417 ] 418 } 419 ] 420 } 421 ] 422 } 423 ` 424 nb := basicnode.Prototype__Any{}.NewBuilder() 425 err := dagjson.Decode(nb, strings.NewReader(nodeString)) 426 qt.Check(t, err, qt.IsNil) 427 rn := nb.Build() 428 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 429 rn, err = rn.LookupByString("Parents") 430 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth}, nil}) 431 qt.Check(t, err, qt.IsNil) 432 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 433 rn, err = rn.LookupByIndex(0) 434 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreUnion{[]Selector{Matcher{}, subTree}}, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 435 qt.Check(t, err, qt.IsNil) 436 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfString("Parents")) 437 438 rn, err = rn.LookupByString("Parents") 439 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, parentsSelector, RecursionLimit{RecursionLimit_Depth, maxDepth - 1}, nil}) 440 qt.Check(t, err, qt.IsNil) 441 rs, _ = rs.Explore(rn, datamodel.PathSegmentOfInt(0)) 442 _, err = rn.LookupByIndex(0) 443 qt.Check(t, rs, deepEqualsAllowAllUnexported, ExploreRecursive{subTree, ExploreUnion{[]Selector{Matcher{}, subTree}}, RecursionLimit{RecursionLimit_Depth, maxDepth - 2}, nil}) 444 qt.Check(t, err, qt.IsNil) 445 }) 446 }