Skip to content

Commit 3605d23

Browse files
aschmahmannRobin
authored and
Robin
committed
test(pinning): add pin ls tests for indirect pin traversal and pin type precedence
1 parent 93e4b11 commit 3605d23

File tree

2 files changed

+268
-1
lines changed

2 files changed

+268
-1
lines changed

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,3 @@ require (
1616
github.com/multiformats/go-multihash v0.0.7
1717
)
1818

19-
go 1.12

tests/pin.go

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"github.com/TRON-US/interface-go-btfs-core"
1111
opt "github.com/TRON-US/interface-go-btfs-core/options"
1212

13+
"github.com/ipfs/interface-go-ipfs-core/path"
1314
ipldcbor "github.com/ipfs/go-ipld-cbor"
15+
"github.com/ipfs/go-cid"
1416
ipld "github.com/ipfs/go-ipld-format"
1517
)
1618

@@ -25,6 +27,8 @@ func (tp *TestSuite) TestPin(t *testing.T) {
2527
t.Run("TestPinAdd", tp.TestPinAdd)
2628
t.Run("TestPinSimple", tp.TestPinSimple)
2729
t.Run("TestPinRecursive", tp.TestPinRecursive)
30+
t.Run("TestPinLsIndirect", tp.TestPinLsIndirect)
31+
t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence)
2832
}
2933

3034
func (tp *TestSuite) TestPinAdd(t *testing.T) {
@@ -238,3 +242,267 @@ func (tp *TestSuite) TestPinRecursive(t *testing.T) {
238242
}
239243
*/
240244
}
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 \nactual: %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

Comments
 (0)