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