responsecache_test.go 4.25 KB
Newer Older
1 2 3 4 5 6 7
package responsecache

import (
	"fmt"
	"math/rand"
	"testing"

8
	blocks "github.com/ipfs/go-block-format"
Hannah Howard's avatar
Hannah Howard committed
9 10
	ipld "github.com/ipld/go-ipld-prime"
	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
Hannah Howard's avatar
Hannah Howard committed
11
	"github.com/stretchr/testify/require"
12

Hannah Howard's avatar
Hannah Howard committed
13
	"github.com/ipfs/go-graphsync"
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	"github.com/ipfs/go-graphsync/metadata"
	"github.com/ipfs/go-graphsync/testutil"
)

type fakeUnverifiedBlockStore struct {
	inMemoryBlocks map[ipld.Link][]byte
}

func (ubs *fakeUnverifiedBlockStore) AddUnverifiedBlock(lnk ipld.Link, data []byte) {
	ubs.inMemoryBlocks[lnk] = data
}

func (ubs *fakeUnverifiedBlockStore) PruneBlocks(shouldPrune func(ipld.Link) bool) {
	for link := range ubs.inMemoryBlocks {
		if shouldPrune(link) {
			delete(ubs.inMemoryBlocks, link)
		}
	}
}

func (ubs *fakeUnverifiedBlockStore) VerifyBlock(lnk ipld.Link) ([]byte, error) {
	data, ok := ubs.inMemoryBlocks[lnk]
	if !ok {
		return nil, fmt.Errorf("Block not found")
	}
	delete(ubs.inMemoryBlocks, lnk)
	return data, nil
}

func (ubs *fakeUnverifiedBlockStore) blocks() []blocks.Block {
	blks := make([]blocks.Block, 0, len(ubs.inMemoryBlocks))
	for link, data := range ubs.inMemoryBlocks {
		blk, err := blocks.NewBlockWithCid(data, link.(cidlink.Link).Cid)
		if err == nil {
			blks = append(blks, blk)
		}
	}
	return blks
}

func TestResponseCacheManagingLinks(t *testing.T) {
	blks := testutil.GenerateBlocksOfSize(5, 100)
56 57
	requestID1 := graphsync.RequestID(rand.Int31())
	requestID2 := graphsync.RequestID(rand.Int31())
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

	request1Metadata := metadata.Metadata{
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[0].Cid()},
			BlockPresent: true,
		},
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[1].Cid()},
			BlockPresent: false,
		},
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[3].Cid()},
			BlockPresent: true,
		},
	}

	request2Metadata := metadata.Metadata{
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[1].Cid()},
			BlockPresent: true,
		},
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[3].Cid()},
			BlockPresent: true,
		},
		metadata.Item{
			Link:         cidlink.Link{Cid: blks[4].Cid()},
			BlockPresent: true,
		},
	}

89
	responses := map[graphsync.RequestID]metadata.Metadata{
90 91 92 93 94 95 96 97 98 99 100
		requestID1: request1Metadata,
		requestID2: request2Metadata,
	}

	fubs := &fakeUnverifiedBlockStore{
		inMemoryBlocks: make(map[ipld.Link][]byte),
	}
	responseCache := New(fubs)

	responseCache.ProcessResponse(responses, blks)

Hannah Howard's avatar
Hannah Howard committed
101 102
	require.Len(t, fubs.blocks(), len(blks)-1, "should prune block with no references")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[2])
103 104 105

	// should load block from unverified block store
	data, err := responseCache.AttemptLoad(requestID2, cidlink.Link{Cid: blks[4].Cid()})
Hannah Howard's avatar
Hannah Howard committed
106 107 108
	require.NoError(t, err)
	require.Equal(t, blks[4].RawData(), data, "did not load correct block")

109
	// which will remove block
Hannah Howard's avatar
Hannah Howard committed
110 111
	require.Len(t, fubs.blocks(), len(blks)-2, "should prune block once verified")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[4])
112 113 114

	// fails as it is a known missing block
	data, err = responseCache.AttemptLoad(requestID1, cidlink.Link{Cid: blks[1].Cid()})
Hannah Howard's avatar
Hannah Howard committed
115 116
	require.Error(t, err)
	require.Nil(t, data, "no data should be returned for missing block")
117 118 119

	// should succeed for request 2 where it's not a missing block
	data, err = responseCache.AttemptLoad(requestID2, cidlink.Link{Cid: blks[1].Cid()})
Hannah Howard's avatar
Hannah Howard committed
120 121 122
	require.NoError(t, err)
	require.Equal(t, blks[1].RawData(), data)

123
	// which will remove block
Hannah Howard's avatar
Hannah Howard committed
124 125
	require.Len(t, fubs.blocks(), len(blks)-3, "should prune block once verified")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[1])
126 127 128

	// should be unknown result as block is not known missing or present in block store
	data, err = responseCache.AttemptLoad(requestID1, cidlink.Link{Cid: blks[2].Cid()})
Hannah Howard's avatar
Hannah Howard committed
129 130
	require.NoError(t, err)
	require.Nil(t, data, "no data should be returned for unknown block")
131 132 133

	responseCache.FinishRequest(requestID1)
	// should remove only block 0, since it now has no refering outstanding requests
Hannah Howard's avatar
Hannah Howard committed
134 135
	require.Len(t, fubs.blocks(), len(blks)-4, "should prune block when it is orphaned")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[0])
136 137 138

	responseCache.FinishRequest(requestID2)
	// should remove last block since are no remaining references
Hannah Howard's avatar
Hannah Howard committed
139 140 141
	require.Len(t, fubs.blocks(), 0, "should prune block when it is orphaned")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[3])

142
}