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

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

8 9
	blocks "github.com/ipfs/go-block-format"
	"github.com/ipfs/go-graphsync"
Hannah Howard's avatar
Hannah Howard committed
10
	"github.com/stretchr/testify/require"
11 12 13

	"github.com/ipfs/go-graphsync/metadata"
	"github.com/ipfs/go-graphsync/testutil"
14
	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
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 56

	ipld "github.com/ipld/go-ipld-prime"
)

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)
57 58
	requestID1 := graphsync.RequestID(rand.Int31())
	requestID2 := graphsync.RequestID(rand.Int31())
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 89

	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,
		},
	}

90
	responses := map[graphsync.RequestID]metadata.Metadata{
91 92 93 94 95 96 97 98 99 100 101
		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
102 103
	require.Len(t, fubs.blocks(), len(blks)-1, "should prune block with no references")
	testutil.RefuteContainsBlock(t, fubs.blocks(), blks[2])
104 105 106

	// 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
107 108 109
	require.NoError(t, err)
	require.Equal(t, blks[4].RawData(), data, "did not load correct block")

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

	// 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
116 117
	require.Error(t, err)
	require.Nil(t, data, "no data should be returned for missing block")
118 119 120

	// 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
121 122 123
	require.NoError(t, err)
	require.Equal(t, blks[1].RawData(), data)

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

	// 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
130 131
	require.NoError(t, err)
	require.Nil(t, data, "no data should be returned for unknown block")
132 133 134

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

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

143
}