Skip to content

Commit f782a1f

Browse files
authored
Merge pull request ipfs/interface-go-ipfs-core#47 from ipfs/test/pin-ls
Add pin ls tests for indirect pin traversal and pin type precedence This commit was moved from ipfs/interface-go-ipfs-core@7bbf5bb
2 parents 8a619bd + eaac207 commit f782a1f

File tree

1 file changed

+268
-1
lines changed

1 file changed

+268
-1
lines changed

coreiface/tests/pin.go

+268-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package tests
22

33
import (
44
"context"
5-
"github.com/ipfs/interface-go-ipfs-core/path"
65
"math"
76
"strings"
87
"testing"
98

109
"github.com/ipfs/interface-go-ipfs-core"
1110
opt "github.com/ipfs/interface-go-ipfs-core/options"
11+
"github.com/ipfs/interface-go-ipfs-core/path"
1212

13+
"github.com/ipfs/go-cid"
1314
ipldcbor "github.com/ipfs/go-ipld-cbor"
1415
ipld "github.com/ipfs/go-ipld-format"
1516
)
@@ -25,6 +26,8 @@ func (tp *TestSuite) TestPin(t *testing.T) {
2526
t.Run("TestPinAdd", tp.TestPinAdd)
2627
t.Run("TestPinSimple", tp.TestPinSimple)
2728
t.Run("TestPinRecursive", tp.TestPinRecursive)
29+
t.Run("TestPinLsIndirect", tp.TestPinLsIndirect)
30+
t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence)
2831
}
2932

3033
func (tp *TestSuite) TestPinAdd(t *testing.T) {
@@ -238,3 +241,267 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
238241
}
239242
*/
240243
}
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+
}

0 commit comments

Comments
 (0)