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
+}