@@ -2,14 +2,15 @@ package tests
2
2
3
3
import (
4
4
"context"
5
- "github.com/ipfs/interface-go-ipfs-core/path"
6
5
"math"
7
6
"strings"
8
7
"testing"
9
8
10
9
"github.com/ipfs/interface-go-ipfs-core"
11
10
opt "github.com/ipfs/interface-go-ipfs-core/options"
11
+ "github.com/ipfs/interface-go-ipfs-core/path"
12
12
13
+ "github.com/ipfs/go-cid"
13
14
ipldcbor "github.com/ipfs/go-ipld-cbor"
14
15
ipld "github.com/ipfs/go-ipld-format"
15
16
)
@@ -25,6 +26,8 @@ func (tp *TestSuite) TestPin(t *testing.T) {
25
26
t .Run ("TestPinAdd" , tp .TestPinAdd )
26
27
t .Run ("TestPinSimple" , tp .TestPinSimple )
27
28
t .Run ("TestPinRecursive" , tp .TestPinRecursive )
29
+ t .Run ("TestPinLsIndirect" , tp .TestPinLsIndirect )
30
+ t .Run ("TestPinLsPrecedence" , tp .TestPinLsPrecedence )
28
31
}
29
32
30
33
func (tp * TestSuite ) TestPinAdd (t * testing.T ) {
@@ -238,3 +241,267 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
238
241
}
239
242
*/
240
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 \n actual: %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
+ }
0 commit comments