@cryptotaxi247 / kubo / commits / c23fa6e2f

test(pinning): add pin ls tests for indirect pin traversal and pin type precedence

This commit was moved from ipfs/interface-go-ipfs-core@cd7be61c71d8169a47604247588b258699f45b5d This commit was moved from ipfs/boxo@30eb9c7c432be938b40951a4c98b8cfbe67899cc

Adin Schmahmann committed Oct 11, 2019 at 11:14 UTC c23fa6e2f1f8732794fcb227ce109a5d04f5ed0f
1 file changed +268 -1
core/coreiface/tests/pin.go
+268 -1
@@ -2,14 +2,15 @@ package tests
2
3 import (
4 "context"
5 - "github.com/ipfs/interface-go-ipfs-core/path"
5 "math"
6 "strings"
7 "testing"
8
9 "github.com/ipfs/interface-go-ipfs-core"
10 opt "github.com/ipfs/interface-go-ipfs-core/options"
11 + "github.com/ipfs/interface-go-ipfs-core/path"
12
13 + "github.com/ipfs/go-cid"
14 ipldcbor "github.com/ipfs/go-ipld-cbor"
15 ipld "github.com/ipfs/go-ipld-format"
16 )
@@ -25,6 +26,8 @@ func (tp *TestSuite) TestPin(t *testing.T) {
26 t.Run("TestPinAdd", tp.TestPinAdd)
27 t.Run("TestPinSimple", tp.TestPinSimple)
28 t.Run("TestPinRecursive", tp.TestPinRecursive)
29 + t.Run("TestPinLsIndirect", tp.TestPinLsIndirect)
30 + t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence)
31 }
32
33 func (tp *TestSuite) TestPinAdd(t *testing.T) {
@@ -238,3 +241,267 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
241 }
242 */
243 }
244 +
245 +// TestPinLsIndirect verifies that indirect nodes are listed by pin ls even if a parent node is directly pinned
246 +func (tp *TestSuite) TestPinLsIndirect(t *testing.T) {
247 + ctx, cancel := context.WithCancel(context.Background())
248 + defer cancel()
249 + api, err := tp.makeAPI(ctx)
250 + if err != nil {
251 + t.Fatal(err)
252 + }
253 +
254 + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foo")
255 +
256 + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
257 + if err != nil {
258 + t.Fatal(err)
259 + }
260 +
261 + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
262 + if err != nil {
263 + t.Fatal(err)
264 + }
265 +
266 + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf})
267 +}
268 +
269 +// TestPinLsPrecedence verifies the precedence of pins (recursive > direct > indirect)
270 +func (tp *TestSuite) TestPinLsPrecedence(t *testing.T) {
271 + // Testing precedence of recursive, direct and indirect pins
272 + // Results should be recursive > indirect, direct > indirect, and recursive > direct
273 +
274 + t.Run("TestPinLsPredenceRecursiveIndirect", tp.TestPinLsPredenceRecursiveIndirect)
275 + t.Run("TestPinLsPrecedenceDirectIndirect", tp.TestPinLsPrecedenceDirectIndirect)
276 + t.Run("TestPinLsPrecedenceRecursiveDirect", tp.TestPinLsPrecedenceRecursiveDirect)
277 +}
278 +
279 +func (tp *TestSuite) TestPinLsPredenceRecursiveIndirect(t *testing.T) {
280 + ctx, cancel := context.WithCancel(context.Background())
281 + defer cancel()
282 + api, err := tp.makeAPI(ctx)
283 + if err != nil {
284 + t.Fatal(err)
285 + }
286 +
287 + // Test recursive > indirect
288 + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive > indirect")
289 +
290 + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
291 + if err != nil {
292 + t.Fatal(err)
293 + }
294 +
295 + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()))
296 + if err != nil {
297 + t.Fatal(err)
298 + }
299 +
300 + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf})
301 +}
302 +
303 +func (tp *TestSuite) TestPinLsPrecedenceDirectIndirect(t *testing.T) {
304 + ctx, cancel := context.WithCancel(context.Background())
305 + defer cancel()
306 + api, err := tp.makeAPI(ctx)
307 + if err != nil {
308 + t.Fatal(err)
309 + }
310 +
311 + // Test direct > indirect
312 + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "direct > indirect")
313 +
314 + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
315 + if err != nil {
316 + t.Fatal(err)
317 + }
318 +
319 + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
320 + if err != nil {
321 + t.Fatal(err)
322 + }
323 +
324 + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf})
325 +}
326 +
327 +func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) {
328 + ctx, cancel := context.WithCancel(context.Background())
329 + defer cancel()
330 + api, err := tp.makeAPI(ctx)
331 + if err != nil {
332 + t.Fatal(err)
333 + }
334 +
335 + // Test recursive > direct
336 + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive + direct = error")
337 +
338 + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()))
339 + if err != nil {
340 + t.Fatal(err)
341 + }
342 +
343 + err = api.Pin().Add(ctx, path.IpldPath(parent.Cid()), opt.Pin.Recursive(false))
344 + if err == nil {
345 + t.Fatal("expected error directly pinning a recursively pinned node")
346 + }
347 +
348 + assertPinTypes(t, ctx, api, []cidContainer{parent}, []cidContainer{}, []cidContainer{leaf})
349 +
350 + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()), opt.Pin.Recursive(false))
351 + if err != nil {
352 + t.Fatal(err)
353 + }
354 +
355 + err = api.Pin().Add(ctx, path.IpldPath(grandparent.Cid()))
356 + if err != nil {
357 + t.Fatal(err)
358 + }
359 +
360 + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf})
361 +}
362 +
363 +type cidContainer interface {
364 + Cid() cid.Cid
365 +}
366 +
367 +func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, leafData string) (cidContainer, cidContainer, cidContainer) {
368 + leaf, err := api.Unixfs().Add(ctx, strFile(leafData)())
369 + if err != nil {
370 + t.Fatal(err)
371 + }
372 +
373 + parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.Cid().String()+`"}}`), math.MaxUint64, -1)
374 + if err != nil {
375 + t.Fatal(err)
376 + }
377 +
378 + grandparent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+parent.Cid().String()+`"}}`), math.MaxUint64, -1)
379 + if err != nil {
380 + t.Fatal(err)
381 + }
382 +
383 + if err := api.Dag().AddMany(ctx, []ipld.Node{parent, grandparent}); err != nil {
384 + t.Fatal(err)
385 + }
386 +
387 + return leaf, parent, grandparent
388 +}
389 +
390 +func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) {
391 + assertPinLsAllConsistency(t, ctx, api)
392 +
393 + list, err := api.Pin().Ls(ctx, opt.Pin.Type.Recursive())
394 + if err != nil {
395 + t.Fatal(err)
396 + }
397 +
398 + assertPinCids(t, list, recusive...)
399 +
400 + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Direct())
401 + if err != nil {
402 + t.Fatal(err)
403 + }
404 +
405 + assertPinCids(t, list, direct...)
406 +
407 + list, err = api.Pin().Ls(ctx, opt.Pin.Type.Indirect())
408 + if err != nil {
409 + t.Fatal(err)
410 + }
411 +
412 + assertPinCids(t, list, indirect...)
413 +}
414 +
415 +// assertPinCids verifies that the pins match the expected cids
416 +func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) {
417 + t.Helper()
418 +
419 + if expected, actual := len(cids), len(pins); expected != actual {
420 + t.Fatalf("expected pin list to have len %d, was %d", expected, actual)
421 + }
422 +
423 + cSet := cid.NewSet()
424 + for _, c := range cids {
425 + cSet.Add(c.Cid())
426 + }
427 +
428 + valid := true
429 + for _, p := range pins {
430 + c := p.Path().Cid()
431 + if cSet.Has(c) {
432 + cSet.Remove(c)
433 + } else {
434 + valid = false
435 + break
436 + }
437 + }
438 +
439 + valid = valid && cSet.Len() == 0
440 +
441 + if !valid {
442 + pinStrs := make([]string, len(pins))
443 + for i, p := range pins {
444 + pinStrs[i] = p.Path().Cid().String()
445 + }
446 + pathStrs := make([]string, len(cids))
447 + for i, c := range cids {
448 + pathStrs[i] = c.Cid().String()
449 + }
450 + t.Fatalf("expected: %s \nactual: %s", strings.Join(pathStrs, ", "), strings.Join(pinStrs, ", "))
451 + }
452 +}
453 +
454 +// assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually
455 +func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) {
456 + t.Helper()
457 + allPins, err := api.Pin().Ls(ctx)
458 + if err != nil {
459 + t.Fatal(err)
460 + }
461 +
462 + type pinTypeProps struct {
463 + *cid.Set
464 + opt.PinLsOption
465 + }
466 +
467 + all, recursive, direct, indirect := cid.NewSet(), cid.NewSet(), cid.NewSet(), cid.NewSet()
468 + typeMap := map[string]*pinTypeProps{
469 + "recursive": {recursive, opt.Pin.Type.Recursive()},
470 + "direct": {direct, opt.Pin.Type.Direct()},
471 + "indirect": {indirect, opt.Pin.Type.Indirect()},
472 + }
473 +
474 + for _, p := range allPins {
475 + if !all.Visit(p.Path().Cid()) {
476 + t.Fatalf("pin ls returned the same cid multiple times")
477 + }
478 +
479 + typeStr := p.Type()
480 + if typeSet, ok := typeMap[p.Type()]; ok {
481 + typeSet.Add(p.Path().Cid())
482 + } else {
483 + t.Fatalf("unknown pin type: %s", typeStr)
484 + }
485 + }
486 +
487 + for typeStr, pinProps := range typeMap {
488 + pins, err := api.Pin().Ls(ctx, pinProps.PinLsOption)
489 + if err != nil {
490 + t.Fatal(err)
491 + }
492 +
493 + if expected, actual := len(pins), pinProps.Set.Len(); expected != actual {
494 + t.Fatalf("pin ls all has %d pins of type %s, but pin ls for the type has %d", expected, typeStr, actual)
495 + }
496 +
497 + for _, p := range pins {
498 + if pinType := p.Type(); pinType != typeStr {
499 + t.Fatalf("returned wrong pin type: expected %s, got %s", typeStr, pinType)
500 + }
501 +
502 + if c := p.Path().Cid(); !pinProps.Has(c) {
503 + t.Fatalf("%s expected to be in pin ls all as type %s", c.String(), typeStr)
504 + }
505 + }
506 + }
507 +}