github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/netstore_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package storage
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"io/ioutil"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/FusionFoundation/efsn/p2p/discover"
    29  	ch "github.com/FusionFoundation/efsn/swarm/chunk"
    30  
    31  	"github.com/FusionFoundation/efsn/common"
    32  )
    33  
    34  var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
    35  
    36  type mockNetFetcher struct {
    37  	peers           *sync.Map
    38  	sources         []*discover.NodeID
    39  	peersPerRequest [][]Address
    40  	requestCalled   bool
    41  	offerCalled     bool
    42  	quit            <-chan struct{}
    43  	ctx             context.Context
    44  }
    45  
    46  func (m *mockNetFetcher) Offer(ctx context.Context, source *discover.NodeID) {
    47  	m.offerCalled = true
    48  	m.sources = append(m.sources, source)
    49  }
    50  
    51  func (m *mockNetFetcher) Request(ctx context.Context) {
    52  	m.requestCalled = true
    53  	var peers []Address
    54  	m.peers.Range(func(key interface{}, _ interface{}) bool {
    55  		peers = append(peers, common.FromHex(key.(string)))
    56  		return true
    57  	})
    58  	m.peersPerRequest = append(m.peersPerRequest, peers)
    59  }
    60  
    61  type mockNetFetchFuncFactory struct {
    62  	fetcher *mockNetFetcher
    63  }
    64  
    65  func (m *mockNetFetchFuncFactory) newMockNetFetcher(ctx context.Context, _ Address, peers *sync.Map) NetFetcher {
    66  	m.fetcher.peers = peers
    67  	m.fetcher.quit = ctx.Done()
    68  	m.fetcher.ctx = ctx
    69  	return m.fetcher
    70  }
    71  
    72  func mustNewNetStore(t *testing.T) *NetStore {
    73  	netStore, _ := mustNewNetStoreWithFetcher(t)
    74  	return netStore
    75  }
    76  
    77  func mustNewNetStoreWithFetcher(t *testing.T) (*NetStore, *mockNetFetcher) {
    78  	t.Helper()
    79  
    80  	datadir, err := ioutil.TempDir("", "netstore")
    81  	if err != nil {
    82  		t.Fatal(err)
    83  	}
    84  	naddr := make([]byte, 32)
    85  	params := NewDefaultLocalStoreParams()
    86  	params.Init(datadir)
    87  	params.BaseKey = naddr
    88  	localStore, err := NewTestLocalStoreForAddr(params)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  
    93  	fetcher := &mockNetFetcher{}
    94  	mockNetFetchFuncFactory := &mockNetFetchFuncFactory{
    95  		fetcher: fetcher,
    96  	}
    97  	netStore, err := NewNetStore(localStore, mockNetFetchFuncFactory.newMockNetFetcher)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  	return netStore, fetcher
   102  }
   103  
   104  // TestNetStoreGetAndPut tests calling NetStore.Get which is blocked until the same chunk is Put.
   105  // After the Put there should no active fetchers, and the context created for the fetcher should
   106  // be cancelled.
   107  func TestNetStoreGetAndPut(t *testing.T) {
   108  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   109  
   110  	chunk := GenerateRandomChunk(ch.DefaultSize)
   111  
   112  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   113  	defer cancel()
   114  
   115  	c := make(chan struct{}) // this channel ensures that the gouroutine with the Put does not run earlier than the Get
   116  	go func() {
   117  		<-c                                // wait for the Get to be called
   118  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   119  
   120  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   121  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   122  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   123  		}
   124  
   125  		err := netStore.Put(ctx, chunk)
   126  		if err != nil {
   127  			t.Fatalf("Expected no err got %v", err)
   128  		}
   129  	}()
   130  
   131  	close(c)
   132  	recChunk, err := netStore.Get(ctx, chunk.Address()) // this is blocked until the Put above is done
   133  	if err != nil {
   134  		t.Fatalf("Expected no err got %v", err)
   135  	}
   136  	// the retrieved chunk should be the same as what we Put
   137  	if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   138  		t.Fatalf("Different chunk received than what was put")
   139  	}
   140  	// the chunk is already available locally, so there should be no active fetchers waiting for it
   141  	if netStore.fetchers.Len() != 0 {
   142  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   143  	}
   144  
   145  	// A fetcher was created when the Get was called (and the chunk was not available). The chunk
   146  	// was delivered with the Put call, so the fetcher should be cancelled now.
   147  	select {
   148  	case <-fetcher.ctx.Done():
   149  	default:
   150  		t.Fatal("Expected fetcher context to be cancelled")
   151  	}
   152  
   153  }
   154  
   155  // TestNetStoreGetAndPut tests calling NetStore.Put and then NetStore.Get.
   156  // After the Put the chunk is available locally, so the Get can just retrieve it from LocalStore,
   157  // there is no need to create fetchers.
   158  func TestNetStoreGetAfterPut(t *testing.T) {
   159  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   160  
   161  	chunk := GenerateRandomChunk(ch.DefaultSize)
   162  
   163  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   164  	defer cancel()
   165  
   166  	// First we Put the chunk, so the chunk will be available locally
   167  	err := netStore.Put(ctx, chunk)
   168  	if err != nil {
   169  		t.Fatalf("Expected no err got %v", err)
   170  	}
   171  
   172  	// Get should retrieve the chunk from LocalStore, without creating fetcher
   173  	recChunk, err := netStore.Get(ctx, chunk.Address())
   174  	if err != nil {
   175  		t.Fatalf("Expected no err got %v", err)
   176  	}
   177  	// the retrieved chunk should be the same as what we Put
   178  	if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   179  		t.Fatalf("Different chunk received than what was put")
   180  	}
   181  	// no fetcher offer or request should be created for a locally available chunk
   182  	if fetcher.offerCalled || fetcher.requestCalled {
   183  		t.Fatal("NetFetcher.offerCalled or requestCalled not expected to be called")
   184  	}
   185  	// no fetchers should be created for a locally available chunk
   186  	if netStore.fetchers.Len() != 0 {
   187  		t.Fatal("Expected netStore to not have fetcher")
   188  	}
   189  
   190  }
   191  
   192  // TestNetStoreGetTimeout tests a Get call for an unavailable chunk and waits for timeout
   193  func TestNetStoreGetTimeout(t *testing.T) {
   194  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   195  
   196  	chunk := GenerateRandomChunk(ch.DefaultSize)
   197  
   198  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   199  	defer cancel()
   200  
   201  	c := make(chan struct{}) // this channel ensures that the gouroutine does not run earlier than the Get
   202  	go func() {
   203  		<-c                                // wait for the Get to be called
   204  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   205  
   206  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   207  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   208  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   209  		}
   210  	}()
   211  
   212  	close(c)
   213  	// We call Get on this chunk, which is not in LocalStore. We don't Put it at all, so there will
   214  	// be a timeout
   215  	_, err := netStore.Get(ctx, chunk.Address())
   216  
   217  	// Check if the timeout happened
   218  	if err != context.DeadlineExceeded {
   219  		t.Fatalf("Expected context.DeadLineExceeded err got %v", err)
   220  	}
   221  
   222  	// A fetcher was created, check if it has been removed after timeout
   223  	if netStore.fetchers.Len() != 0 {
   224  		t.Fatal("Expected netStore to remove the fetcher after timeout")
   225  	}
   226  
   227  	// Check if the fetcher context has been cancelled after the timeout
   228  	select {
   229  	case <-fetcher.ctx.Done():
   230  	default:
   231  		t.Fatal("Expected fetcher context to be cancelled")
   232  	}
   233  }
   234  
   235  // TestNetStoreGetCancel tests a Get call for an unavailable chunk, then cancels the context and checks
   236  // the errors
   237  func TestNetStoreGetCancel(t *testing.T) {
   238  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   239  
   240  	chunk := GenerateRandomChunk(ch.DefaultSize)
   241  
   242  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   243  
   244  	c := make(chan struct{}) // this channel ensures that the gouroutine with the cancel does not run earlier than the Get
   245  	go func() {
   246  		<-c                                // wait for the Get to be called
   247  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   248  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   249  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   250  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   251  		}
   252  		cancel()
   253  	}()
   254  
   255  	close(c)
   256  	// We call Get with an unavailable chunk, so it will create a fetcher and wait for delivery
   257  	_, err := netStore.Get(ctx, chunk.Address())
   258  
   259  	// After the context is cancelled above Get should return with an error
   260  	if err != context.Canceled {
   261  		t.Fatalf("Expected context.Canceled err got %v", err)
   262  	}
   263  
   264  	// A fetcher was created, check if it has been removed after cancel
   265  	if netStore.fetchers.Len() != 0 {
   266  		t.Fatal("Expected netStore to remove the fetcher after cancel")
   267  	}
   268  
   269  	// Check if the fetcher context has been cancelled after the request context cancel
   270  	select {
   271  	case <-fetcher.ctx.Done():
   272  	default:
   273  		t.Fatal("Expected fetcher context to be cancelled")
   274  	}
   275  }
   276  
   277  // TestNetStoreMultipleGetAndPut tests four Get calls for the same unavailable chunk. The chunk is
   278  // delivered with a Put, we have to make sure all Get calls return, and they use a single fetcher
   279  // for the chunk retrieval
   280  func TestNetStoreMultipleGetAndPut(t *testing.T) {
   281  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   282  
   283  	chunk := GenerateRandomChunk(ch.DefaultSize)
   284  
   285  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   286  	defer cancel()
   287  
   288  	go func() {
   289  		// sleep to make sure Put is called after all the Get
   290  		time.Sleep(500 * time.Millisecond)
   291  		// check if netStore created exactly one fetcher for all Get calls
   292  		if netStore.fetchers.Len() != 1 {
   293  			t.Fatal("Expected netStore to use one fetcher for all Get calls")
   294  		}
   295  		err := netStore.Put(ctx, chunk)
   296  		if err != nil {
   297  			t.Fatalf("Expected no err got %v", err)
   298  		}
   299  	}()
   300  
   301  	// call Get 4 times for the same unavailable chunk. The calls will be blocked until the Put above.
   302  	getWG := sync.WaitGroup{}
   303  	for i := 0; i < 4; i++ {
   304  		getWG.Add(1)
   305  		go func() {
   306  			defer getWG.Done()
   307  			recChunk, err := netStore.Get(ctx, chunk.Address())
   308  			if err != nil {
   309  				t.Fatalf("Expected no err got %v", err)
   310  			}
   311  			if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   312  				t.Fatalf("Different chunk received than what was put")
   313  			}
   314  		}()
   315  	}
   316  
   317  	finishedC := make(chan struct{})
   318  	go func() {
   319  		getWG.Wait()
   320  		close(finishedC)
   321  	}()
   322  
   323  	// The Get calls should return after Put, so no timeout expected
   324  	select {
   325  	case <-finishedC:
   326  	case <-time.After(1 * time.Second):
   327  		t.Fatalf("Timeout waiting for Get calls to return")
   328  	}
   329  
   330  	// A fetcher was created, check if it has been removed after cancel
   331  	if netStore.fetchers.Len() != 0 {
   332  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   333  	}
   334  
   335  	// A fetcher was created, check if it has been removed after delivery
   336  	select {
   337  	case <-fetcher.ctx.Done():
   338  	default:
   339  		t.Fatal("Expected fetcher context to be cancelled")
   340  	}
   341  
   342  }
   343  
   344  // TestNetStoreFetchFuncTimeout tests a FetchFunc call for an unavailable chunk and waits for timeout
   345  func TestNetStoreFetchFuncTimeout(t *testing.T) {
   346  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   347  
   348  	chunk := GenerateRandomChunk(ch.DefaultSize)
   349  
   350  	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
   351  	defer cancel()
   352  
   353  	// FetchFunc is called for an unavaible chunk, so the returned wait function should not be nil
   354  	wait := netStore.FetchFunc(ctx, chunk.Address())
   355  	if wait == nil {
   356  		t.Fatal("Expected wait function to be not nil")
   357  	}
   358  
   359  	// There should an active fetcher for the chunk after the FetchFunc call
   360  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   361  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   362  	}
   363  
   364  	// wait function should timeout because we don't deliver the chunk with a Put
   365  	err := wait(ctx)
   366  	if err != context.DeadlineExceeded {
   367  		t.Fatalf("Expected context.DeadLineExceeded err got %v", err)
   368  	}
   369  
   370  	// the fetcher should be removed after timeout
   371  	if netStore.fetchers.Len() != 0 {
   372  		t.Fatal("Expected netStore to remove the fetcher after timeout")
   373  	}
   374  
   375  	// the fetcher context should be cancelled after timeout
   376  	select {
   377  	case <-fetcher.ctx.Done():
   378  	default:
   379  		t.Fatal("Expected fetcher context to be cancelled")
   380  	}
   381  }
   382  
   383  // TestNetStoreFetchFuncAfterPut tests that the FetchFunc should return nil for a locally available chunk
   384  func TestNetStoreFetchFuncAfterPut(t *testing.T) {
   385  	netStore := mustNewNetStore(t)
   386  
   387  	chunk := GenerateRandomChunk(ch.DefaultSize)
   388  
   389  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   390  	defer cancel()
   391  
   392  	// We deliver the created the chunk with a Put
   393  	err := netStore.Put(ctx, chunk)
   394  	if err != nil {
   395  		t.Fatalf("Expected no err got %v", err)
   396  	}
   397  
   398  	// FetchFunc should return nil, because the chunk is available locally, no need to fetch it
   399  	wait := netStore.FetchFunc(ctx, chunk.Address())
   400  	if wait != nil {
   401  		t.Fatal("Expected wait to be nil")
   402  	}
   403  
   404  	// No fetchers should be created at all
   405  	if netStore.fetchers.Len() != 0 {
   406  		t.Fatal("Expected netStore to not have fetcher")
   407  	}
   408  }
   409  
   410  // TestNetStoreGetCallsRequest tests if Get created a request on the NetFetcher for an unavailable chunk
   411  func TestNetStoreGetCallsRequest(t *testing.T) {
   412  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   413  
   414  	chunk := GenerateRandomChunk(ch.DefaultSize)
   415  
   416  	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
   417  	defer cancel()
   418  
   419  	// We call get for a not available chunk, it will timeout because the chunk is not delivered
   420  	_, err := netStore.Get(ctx, chunk.Address())
   421  
   422  	if err != context.DeadlineExceeded {
   423  		t.Fatalf("Expected context.DeadlineExceeded err got %v", err)
   424  	}
   425  
   426  	// NetStore should call NetFetcher.Request and wait for the chunk
   427  	if !fetcher.requestCalled {
   428  		t.Fatal("Expected NetFetcher.Request to be called")
   429  	}
   430  }
   431  
   432  // TestNetStoreGetCallsOffer tests if Get created a request on the NetFetcher for an unavailable chunk
   433  // in case of a source peer provided in the context.
   434  func TestNetStoreGetCallsOffer(t *testing.T) {
   435  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   436  
   437  	chunk := GenerateRandomChunk(ch.DefaultSize)
   438  
   439  	//  If a source peer is added to the context, NetStore will handle it as an offer
   440  	ctx := context.WithValue(context.Background(), "source", sourcePeerID.String())
   441  	ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
   442  	defer cancel()
   443  
   444  	// We call get for a not available chunk, it will timeout because the chunk is not delivered
   445  	chunk, err := netStore.Get(ctx, chunk.Address())
   446  
   447  	if err != context.DeadlineExceeded {
   448  		t.Fatalf("Expect error %v got %v", context.DeadlineExceeded, err)
   449  	}
   450  
   451  	// NetStore should call NetFetcher.Offer with the source peer
   452  	if !fetcher.offerCalled {
   453  		t.Fatal("Expected NetFetcher.Request to be called")
   454  	}
   455  
   456  	if len(fetcher.sources) != 1 {
   457  		t.Fatalf("Expected fetcher sources length 1 got %v", len(fetcher.sources))
   458  	}
   459  
   460  	if fetcher.sources[0].String() != sourcePeerID.String() {
   461  		t.Fatalf("Expected fetcher source %v got %v", sourcePeerID, fetcher.sources[0])
   462  	}
   463  
   464  }
   465  
   466  // TestNetStoreFetcherCountPeers tests multiple NetStore.Get calls with peer in the context.
   467  // There is no Put call, so the Get calls timeout
   468  func TestNetStoreFetcherCountPeers(t *testing.T) {
   469  
   470  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   471  
   472  	addr := randomAddr()
   473  	peers := []string{randomAddr().Hex(), randomAddr().Hex(), randomAddr().Hex()}
   474  
   475  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   476  	defer cancel()
   477  	errC := make(chan error)
   478  	nrGets := 3
   479  
   480  	// Call Get 3 times with a peer in context
   481  	for i := 0; i < nrGets; i++ {
   482  		peer := peers[i]
   483  		go func() {
   484  			ctx := context.WithValue(ctx, "peer", peer)
   485  			_, err := netStore.Get(ctx, addr)
   486  			errC <- err
   487  		}()
   488  	}
   489  
   490  	// All 3 Get calls should timeout
   491  	for i := 0; i < nrGets; i++ {
   492  		err := <-errC
   493  		if err != context.DeadlineExceeded {
   494  			t.Fatalf("Expected \"%v\" error got \"%v\"", context.DeadlineExceeded, err)
   495  		}
   496  	}
   497  
   498  	// fetcher should be closed after timeout
   499  	select {
   500  	case <-fetcher.quit:
   501  	case <-time.After(3 * time.Second):
   502  		t.Fatalf("mockNetFetcher not closed after timeout")
   503  	}
   504  
   505  	// All 3 peers should be given to NetFetcher after the 3 Get calls
   506  	if len(fetcher.peersPerRequest) != nrGets {
   507  		t.Fatalf("Expected 3 got %v", len(fetcher.peersPerRequest))
   508  	}
   509  
   510  	for i, peers := range fetcher.peersPerRequest {
   511  		if len(peers) < i+1 {
   512  			t.Fatalf("Expected at least %v got %v", i+1, len(peers))
   513  		}
   514  	}
   515  }
   516  
   517  // TestNetStoreFetchFuncCalledMultipleTimes calls the wait function given by FetchFunc three times,
   518  // and checks there is still exactly one fetcher for one chunk. Afthe chunk is delivered, it checks
   519  // if the fetcher is closed.
   520  func TestNetStoreFetchFuncCalledMultipleTimes(t *testing.T) {
   521  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   522  
   523  	chunk := GenerateRandomChunk(ch.DefaultSize)
   524  
   525  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   526  	defer cancel()
   527  
   528  	// FetchFunc should return a non-nil wait function, because the chunk is not available
   529  	wait := netStore.FetchFunc(ctx, chunk.Address())
   530  	if wait == nil {
   531  		t.Fatal("Expected wait function to be not nil")
   532  	}
   533  
   534  	// There should be exactly one fetcher for the chunk
   535  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   536  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   537  	}
   538  
   539  	// Call wait three times parallelly
   540  	wg := sync.WaitGroup{}
   541  	for i := 0; i < 3; i++ {
   542  		wg.Add(1)
   543  		go func() {
   544  			err := wait(ctx)
   545  			if err != nil {
   546  				t.Fatalf("Expected no err got %v", err)
   547  			}
   548  			wg.Done()
   549  		}()
   550  	}
   551  
   552  	// sleep a little so the wait functions are called above
   553  	time.Sleep(100 * time.Millisecond)
   554  
   555  	// there should be still only one fetcher, because all wait calls are for the same chunk
   556  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   557  		t.Fatal("Expected netStore to have one fetcher for the requested chunk")
   558  	}
   559  
   560  	// Deliver the chunk with a Put
   561  	err := netStore.Put(ctx, chunk)
   562  	if err != nil {
   563  		t.Fatalf("Expected no err got %v", err)
   564  	}
   565  
   566  	// wait until all wait calls return (because the chunk is delivered)
   567  	wg.Wait()
   568  
   569  	// There should be no more fetchers for the delivered chunk
   570  	if netStore.fetchers.Len() != 0 {
   571  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   572  	}
   573  
   574  	// The context for the fetcher should be cancelled after delivery
   575  	select {
   576  	case <-fetcher.ctx.Done():
   577  	default:
   578  		t.Fatal("Expected fetcher context to be cancelled")
   579  	}
   580  }
   581  
   582  // TestNetStoreFetcherLifeCycleWithTimeout is similar to TestNetStoreFetchFuncCalledMultipleTimes,
   583  // the only difference is that we don't deilver the chunk, just wait for timeout
   584  func TestNetStoreFetcherLifeCycleWithTimeout(t *testing.T) {
   585  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   586  
   587  	chunk := GenerateRandomChunk(ch.DefaultSize)
   588  
   589  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   590  	defer cancel()
   591  
   592  	// FetchFunc should return a non-nil wait function, because the chunk is not available
   593  	wait := netStore.FetchFunc(ctx, chunk.Address())
   594  	if wait == nil {
   595  		t.Fatal("Expected wait function to be not nil")
   596  	}
   597  
   598  	// There should be exactly one fetcher for the chunk
   599  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   600  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   601  	}
   602  
   603  	// Call wait three times parallelly
   604  	wg := sync.WaitGroup{}
   605  	for i := 0; i < 3; i++ {
   606  		wg.Add(1)
   607  		go func() {
   608  			defer wg.Done()
   609  			rctx, rcancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   610  			defer rcancel()
   611  			err := wait(rctx)
   612  			if err != context.DeadlineExceeded {
   613  				t.Fatalf("Expected err %v got %v", context.DeadlineExceeded, err)
   614  			}
   615  		}()
   616  	}
   617  
   618  	// wait until all wait calls timeout
   619  	wg.Wait()
   620  
   621  	// There should be no more fetchers after timeout
   622  	if netStore.fetchers.Len() != 0 {
   623  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   624  	}
   625  
   626  	// The context for the fetcher should be cancelled after timeout
   627  	select {
   628  	case <-fetcher.ctx.Done():
   629  	default:
   630  		t.Fatal("Expected fetcher context to be cancelled")
   631  	}
   632  }
   633  
   634  func randomAddr() Address {
   635  	addr := make([]byte, 32)
   636  	rand.Read(addr)
   637  	return Address(addr)
   638  }