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  }