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 }