github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/state_synchronization/requester/unittest/unittest.go (about)

     1  package unittest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/ipfs/boxo/blockstore"
     9  	"github.com/ipfs/go-cid"
    10  	"github.com/stretchr/testify/mock"
    11  
    12  	"github.com/onflow/flow-go/model/flow"
    13  	"github.com/onflow/flow-go/module"
    14  	"github.com/onflow/flow-go/module/blobs"
    15  	"github.com/onflow/flow-go/network/mocknetwork"
    16  	statemock "github.com/onflow/flow-go/state/protocol/mock"
    17  	"github.com/onflow/flow-go/storage"
    18  	storagemock "github.com/onflow/flow-go/storage/mock"
    19  )
    20  
    21  func MockBlobService(bs blockstore.Blockstore) *mocknetwork.BlobService {
    22  	bex := new(mocknetwork.BlobService)
    23  
    24  	bex.On("GetBlobs", mock.Anything, mock.AnythingOfType("[]cid.Cid")).
    25  		Return(func(ctx context.Context, cids []cid.Cid) <-chan blobs.Blob {
    26  			ch := make(chan blobs.Blob)
    27  
    28  			var wg sync.WaitGroup
    29  			wg.Add(len(cids))
    30  
    31  			for _, c := range cids {
    32  				c := c
    33  				go func() {
    34  					defer wg.Done()
    35  
    36  					blob, err := bs.Get(ctx, c)
    37  
    38  					if err != nil {
    39  						// In the real implementation, Bitswap would keep trying to get the blob from
    40  						// the network indefinitely, sending requests to more and more peers until it
    41  						// eventually finds the blob, or the context is canceled. Here, we know that
    42  						// if the blob is not already in the blobstore, then we will never appear, so
    43  						// we just wait for the context to be canceled.
    44  						<-ctx.Done()
    45  
    46  						return
    47  					}
    48  
    49  					ch <- blob
    50  				}()
    51  			}
    52  
    53  			go func() {
    54  				wg.Wait()
    55  				close(ch)
    56  			}()
    57  
    58  			return ch
    59  		}).Maybe()
    60  
    61  	bex.On("AddBlobs", mock.Anything, mock.AnythingOfType("[]blocks.Block")).Return(bs.PutMany).Maybe()
    62  	bex.On("DeleteBlob", mock.Anything, mock.AnythingOfType("cid.Cid")).Return(bs.DeleteBlock).Maybe()
    63  
    64  	noop := module.NoopReadyDoneAware{}
    65  	bex.On("Ready").Return(func() <-chan struct{} { return noop.Ready() }).Maybe()
    66  
    67  	return bex
    68  }
    69  
    70  type SnapshotMockOptions func(*statemock.Snapshot)
    71  
    72  func WithHead(head *flow.Header) SnapshotMockOptions {
    73  	return func(snapshot *statemock.Snapshot) {
    74  		snapshot.On("Head").Return(head, nil)
    75  	}
    76  }
    77  
    78  func WithSeal(seal *flow.Seal) SnapshotMockOptions {
    79  	return func(snapshot *statemock.Snapshot) {
    80  		snapshot.On("Seal").Return(seal, nil)
    81  	}
    82  }
    83  
    84  func MockProtocolStateSnapshot(opts ...SnapshotMockOptions) *statemock.Snapshot {
    85  	snapshot := new(statemock.Snapshot)
    86  
    87  	for _, opt := range opts {
    88  		opt(snapshot)
    89  	}
    90  
    91  	return snapshot
    92  }
    93  
    94  type StateMockOptions func(*statemock.State)
    95  
    96  func WithSealedSnapshot(snapshot *statemock.Snapshot) StateMockOptions {
    97  	return func(state *statemock.State) {
    98  		state.On("Sealed").Return(snapshot)
    99  	}
   100  }
   101  
   102  func MockProtocolState(opts ...StateMockOptions) *statemock.State {
   103  	state := new(statemock.State)
   104  
   105  	for _, opt := range opts {
   106  		opt(state)
   107  	}
   108  
   109  	return state
   110  }
   111  
   112  type BlockHeaderMockOptions func(*storagemock.Headers)
   113  
   114  func WithByHeight(blocksByHeight map[uint64]*flow.Block) BlockHeaderMockOptions {
   115  	return func(blocks *storagemock.Headers) {
   116  		blocks.On("ByHeight", mock.AnythingOfType("uint64")).Return(
   117  			func(height uint64) *flow.Header {
   118  				if _, has := blocksByHeight[height]; !has {
   119  					return nil
   120  				}
   121  				return blocksByHeight[height].Header
   122  			},
   123  			func(height uint64) error {
   124  				if _, has := blocksByHeight[height]; !has {
   125  					return fmt.Errorf("block %d not found: %w", height, storage.ErrNotFound)
   126  				}
   127  				return nil
   128  			},
   129  		)
   130  	}
   131  }
   132  
   133  func WithByID(blocksByID map[flow.Identifier]*flow.Block) BlockHeaderMockOptions {
   134  	return func(blocks *storagemock.Headers) {
   135  		blocks.On("ByBlockID", mock.AnythingOfType("flow.Identifier")).Return(
   136  			func(blockID flow.Identifier) *flow.Header {
   137  				if _, has := blocksByID[blockID]; !has {
   138  					return nil
   139  				}
   140  				return blocksByID[blockID].Header
   141  			},
   142  			func(blockID flow.Identifier) error {
   143  				if _, has := blocksByID[blockID]; !has {
   144  					return fmt.Errorf("block %s not found: %w", blockID, storage.ErrNotFound)
   145  				}
   146  				return nil
   147  			},
   148  		)
   149  	}
   150  }
   151  
   152  func WithBlockIDByHeight(blocksByHeight map[uint64]*flow.Block) BlockHeaderMockOptions {
   153  	return func(blocks *storagemock.Headers) {
   154  		blocks.On("BlockIDByHeight", mock.AnythingOfType("uint64")).Return(
   155  			func(height uint64) flow.Identifier {
   156  				if _, has := blocksByHeight[height]; !has {
   157  					return flow.ZeroID
   158  				}
   159  				return blocksByHeight[height].Header.ID()
   160  			},
   161  			func(height uint64) error {
   162  				if _, has := blocksByHeight[height]; !has {
   163  					return fmt.Errorf("block %d not found: %w", height, storage.ErrNotFound)
   164  				}
   165  				return nil
   166  			},
   167  		)
   168  	}
   169  }
   170  
   171  func MockBlockHeaderStorage(opts ...BlockHeaderMockOptions) *storagemock.Headers {
   172  	headers := new(storagemock.Headers)
   173  
   174  	for _, opt := range opts {
   175  		opt(headers)
   176  	}
   177  
   178  	return headers
   179  }
   180  
   181  type ResultsMockOptions func(*storagemock.ExecutionResults)
   182  
   183  func WithByBlockID(resultsByID map[flow.Identifier]*flow.ExecutionResult) ResultsMockOptions {
   184  	return func(results *storagemock.ExecutionResults) {
   185  		results.On("ByBlockID", mock.AnythingOfType("flow.Identifier")).Return(
   186  			func(blockID flow.Identifier) *flow.ExecutionResult {
   187  				if _, has := resultsByID[blockID]; !has {
   188  					return nil
   189  				}
   190  				return resultsByID[blockID]
   191  			},
   192  			func(blockID flow.Identifier) error {
   193  				if _, has := resultsByID[blockID]; !has {
   194  					return fmt.Errorf("result %s not found: %w", blockID, storage.ErrNotFound)
   195  				}
   196  				return nil
   197  			},
   198  		)
   199  	}
   200  }
   201  
   202  func WithResultByID(resultsByID map[flow.Identifier]*flow.ExecutionResult) ResultsMockOptions {
   203  	return func(results *storagemock.ExecutionResults) {
   204  		results.On("ByID", mock.AnythingOfType("flow.Identifier")).Return(
   205  			func(resultID flow.Identifier) *flow.ExecutionResult {
   206  				if _, has := resultsByID[resultID]; !has {
   207  					return nil
   208  				}
   209  				return resultsByID[resultID]
   210  			},
   211  			func(resultID flow.Identifier) error {
   212  				if _, has := resultsByID[resultID]; !has {
   213  					return fmt.Errorf("result %s not found: %w", resultID, storage.ErrNotFound)
   214  				}
   215  				return nil
   216  			},
   217  		)
   218  	}
   219  }
   220  
   221  func MockResultsStorage(opts ...ResultsMockOptions) *storagemock.ExecutionResults {
   222  	results := new(storagemock.ExecutionResults)
   223  
   224  	for _, opt := range opts {
   225  		opt(results)
   226  	}
   227  
   228  	return results
   229  }
   230  
   231  type SealsMockOptions func(*storagemock.Seals)
   232  
   233  func WithSealsByBlockID(sealsByBlockID map[flow.Identifier]*flow.Seal) SealsMockOptions {
   234  	return func(seals *storagemock.Seals) {
   235  		seals.On("FinalizedSealForBlock", mock.AnythingOfType("flow.Identifier")).Return(
   236  			func(blockID flow.Identifier) *flow.Seal {
   237  				if _, has := sealsByBlockID[blockID]; !has {
   238  					return nil
   239  				}
   240  				return sealsByBlockID[blockID]
   241  			},
   242  			func(blockID flow.Identifier) error {
   243  				if _, has := sealsByBlockID[blockID]; !has {
   244  					return fmt.Errorf("seal for block %s not found: %w", blockID, storage.ErrNotFound)
   245  				}
   246  				return nil
   247  			},
   248  		)
   249  	}
   250  }
   251  
   252  func MockSealsStorage(opts ...SealsMockOptions) *storagemock.Seals {
   253  	seals := new(storagemock.Seals)
   254  
   255  	for _, opt := range opts {
   256  		opt(seals)
   257  	}
   258  
   259  	return seals
   260  }
   261  
   262  func RemoveExpectedCall(method string, expectedCalls []*mock.Call) []*mock.Call {
   263  	for i, call := range expectedCalls {
   264  		if call.Method == method {
   265  			expectedCalls = append(expectedCalls[:i], expectedCalls[i+1:]...)
   266  		}
   267  	}
   268  	return expectedCalls
   269  }