responsecache_test.go 4.24 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
	"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)
		}
	}
}

34 35 36 37
func (ubs *fakeUnverifiedBlockStore) PruneBlock(link ipld.Link) {
	delete(ubs.inMemoryBlocks, link)
}

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
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)
60 61
	requestID1 := graphsync.RequestID(rand.Int31())
	requestID2 := graphsync.RequestID(rand.Int31())
62 63 64

	request1Metadata := metadata.Metadata{
		metadata.Item{
65
			Link:         blks[0].Cid(),
66 67 68
			BlockPresent: true,
		},
		metadata.Item{
69
			Link:         blks[1].Cid(),
70 71 72
			BlockPresent: false,
		},
		metadata.Item{
73
			Link:         blks[3].Cid(),
74 75 76 77 78 79
			BlockPresent: true,
		},
	}

	request2Metadata := metadata.Metadata{
		metadata.Item{
80
			Link:         blks[1].Cid(),
81 82 83
			BlockPresent: true,
		},
		metadata.Item{
84
			Link:         blks[3].Cid(),
85 86 87
			BlockPresent: true,
		},
		metadata.Item{
88
			Link:         blks[4].Cid(),
89 90 91 92
			BlockPresent: true,
		},
	}

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

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

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

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

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

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

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

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

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

146
}