github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/eds/adapters_test.go (about) 1 package eds 2 3 import ( 4 "context" 5 "errors" 6 mrand "math/rand" 7 "sort" 8 "testing" 9 "time" 10 11 blocks "github.com/ipfs/go-block-format" 12 "github.com/ipfs/go-cid" 13 "github.com/stretchr/testify/require" 14 15 "github.com/celestiaorg/celestia-node/share/ipld" 16 ) 17 18 func TestBlockGetter_GetBlocks(t *testing.T) { 19 t.Run("happy path", func(t *testing.T) { 20 cids := randCIDs(t, 32) 21 // sort cids in asc order 22 sort.Slice(cids, func(i, j int) bool { 23 return cids[i].String() < cids[j].String() 24 }) 25 26 bg := &BlockGetter{store: rbsMock{}} 27 blocksCh := bg.GetBlocks(context.Background(), cids) 28 29 // collect blocks from channel 30 blocks := make([]blocks.Block, 0, len(cids)) 31 for block := range blocksCh { 32 blocks = append(blocks, block) 33 } 34 35 // sort blocks in cid asc order 36 sort.Slice(blocks, func(i, j int) bool { 37 return blocks[i].Cid().String() < blocks[j].Cid().String() 38 }) 39 40 // validate results 41 require.Equal(t, len(cids), len(blocks)) 42 for i, block := range blocks { 43 require.Equal(t, cids[i].String(), block.Cid().String()) 44 } 45 }) 46 t.Run("retrieval error", func(t *testing.T) { 47 cids := randCIDs(t, 32) 48 49 // split cids into failed and succeeded 50 failedLen := mrand.Intn(len(cids)-1) + 1 51 failed := make(map[cid.Cid]struct{}, failedLen) 52 succeeded := make([]cid.Cid, 0, len(cids)-failedLen) 53 for i, cid := range cids { 54 if i < failedLen { 55 failed[cid] = struct{}{} 56 continue 57 } 58 succeeded = append(succeeded, cid) 59 } 60 61 // sort succeeded cids in asc order 62 sort.Slice(succeeded, func(i, j int) bool { 63 return succeeded[i].String() < succeeded[j].String() 64 }) 65 66 bg := &BlockGetter{store: rbsMock{failed: failed}} 67 blocksCh := bg.GetBlocks(context.Background(), cids) 68 69 // collect blocks from channel 70 blocks := make([]blocks.Block, 0, len(cids)) 71 for block := range blocksCh { 72 blocks = append(blocks, block) 73 } 74 75 // sort blocks in cid asc order 76 sort.Slice(blocks, func(i, j int) bool { 77 return blocks[i].Cid().String() < blocks[j].Cid().String() 78 }) 79 80 // validate results 81 require.Equal(t, len(succeeded), len(blocks)) 82 for i, block := range blocks { 83 require.Equal(t, succeeded[i].String(), block.Cid().String()) 84 } 85 }) 86 t.Run("retrieval timeout", func(t *testing.T) { 87 cids := randCIDs(t, 128) 88 89 bg := &BlockGetter{ 90 store: rbsMock{}, 91 } 92 93 // cancel the context before any blocks are collected 94 ctx, cancel := context.WithCancel(context.Background()) 95 cancel() 96 97 blocksCh := bg.GetBlocks(ctx, cids) 98 99 // pretend nobody is reading from blocksCh after context is canceled 100 time.Sleep(50 * time.Millisecond) 101 102 // blocksCh should be closed indicating GetBlocks exited 103 select { 104 case _, ok := <-blocksCh: 105 require.False(t, ok) 106 default: 107 t.Error("channel is not closed on canceled context") 108 } 109 }) 110 } 111 112 // rbsMock is a dagstore.ReadBlockstore mock 113 type rbsMock struct { 114 failed map[cid.Cid]struct{} 115 } 116 117 func (r rbsMock) Has(context.Context, cid.Cid) (bool, error) { 118 panic("implement me") 119 } 120 121 func (r rbsMock) Get(_ context.Context, cid cid.Cid) (blocks.Block, error) { 122 // return error for failed items 123 if _, ok := r.failed[cid]; ok { 124 return nil, errors.New("not found") 125 } 126 127 return blocks.NewBlockWithCid(nil, cid) 128 } 129 130 func (r rbsMock) GetSize(context.Context, cid.Cid) (int, error) { 131 panic("implement me") 132 } 133 134 func (r rbsMock) AllKeysChan(context.Context) (<-chan cid.Cid, error) { 135 panic("implement me") 136 } 137 138 func (r rbsMock) HashOnRead(bool) { 139 panic("implement me") 140 } 141 142 func randCIDs(t *testing.T, n int) []cid.Cid { 143 cids := make([]cid.Cid, n) 144 for i := range cids { 145 cids[i] = ipld.RandNamespacedCID(t) 146 } 147 return cids 148 }