github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/matching/engine_test.go (about) 1 package matching 2 3 import ( 4 "sync" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/mock" 9 "github.com/stretchr/testify/require" 10 "github.com/stretchr/testify/suite" 11 12 "github.com/onflow/flow-go/consensus/hotstuff/model" 13 "github.com/onflow/flow-go/engine" 14 mockconsensus "github.com/onflow/flow-go/engine/consensus/mock" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/module/metrics" 17 mockmodule "github.com/onflow/flow-go/module/mock" 18 "github.com/onflow/flow-go/network/channels" 19 "github.com/onflow/flow-go/network/mocknetwork" 20 mockprotocol "github.com/onflow/flow-go/state/protocol/mock" 21 mockstorage "github.com/onflow/flow-go/storage/mock" 22 "github.com/onflow/flow-go/utils/unittest" 23 ) 24 25 func TestMatchingEngineContext(t *testing.T) { 26 suite.Run(t, new(MatchingEngineSuite)) 27 } 28 29 type MatchingEngineSuite struct { 30 suite.Suite 31 32 index *mockstorage.Index 33 receipts *mockstorage.ExecutionReceipts 34 core *mockconsensus.MatchingCore 35 state *mockprotocol.State 36 37 // Matching Engine 38 engine *Engine 39 } 40 41 func (s *MatchingEngineSuite) SetupTest() { 42 metrics := metrics.NewNoopCollector() 43 me := &mockmodule.Local{} 44 net := &mocknetwork.Network{} 45 s.core = &mockconsensus.MatchingCore{} 46 s.index = &mockstorage.Index{} 47 s.receipts = &mockstorage.ExecutionReceipts{} 48 s.state = &mockprotocol.State{} 49 50 ourNodeID := unittest.IdentifierFixture() 51 me.On("NodeID").Return(ourNodeID) 52 53 con := &mocknetwork.Conduit{} 54 net.On("Register", mock.Anything, mock.Anything).Return(con, nil).Once() 55 56 var err error 57 s.engine, err = NewEngine(unittest.Logger(), net, me, metrics, metrics, s.state, s.receipts, s.index, s.core) 58 require.NoError(s.T(), err) 59 60 <-s.engine.Ready() 61 } 62 63 // TestOnFinalizedBlock tests if finalized block gets processed when send through `Engine`. 64 // Tests the whole processing pipeline. 65 func (s *MatchingEngineSuite) TestOnFinalizedBlock() { 66 67 finalizedBlock := unittest.BlockHeaderFixture() 68 s.state.On("Final").Return(unittest.StateSnapshotForKnownBlock(finalizedBlock, nil)) 69 s.core.On("OnBlockFinalization").Return(nil).Once() 70 s.engine.OnFinalizedBlock(model.BlockFromFlow(finalizedBlock)) 71 72 // matching engine has at least 100ms ticks for processing events 73 time.Sleep(1 * time.Second) 74 75 s.core.AssertExpectations(s.T()) 76 } 77 78 // TestOnBlockIncorporated tests if incorporated block gets processed when send through `Engine`. 79 // Tests the whole processing pipeline. 80 func (s *MatchingEngineSuite) TestOnBlockIncorporated() { 81 82 incorporatedBlock := unittest.BlockHeaderFixture() 83 incorporatedBlockID := incorporatedBlock.ID() 84 85 payload := unittest.PayloadFixture(unittest.WithAllTheFixins) 86 index := &flow.Index{} 87 resultsByID := payload.Results.Lookup() 88 for _, receipt := range payload.Receipts { 89 index.ReceiptIDs = append(index.ReceiptIDs, receipt.ID()) 90 fullReceipt := flow.ExecutionReceiptFromMeta(*receipt, *resultsByID[receipt.ResultID]) 91 s.receipts.On("ByID", receipt.ID()).Return(fullReceipt, nil).Once() 92 s.core.On("ProcessReceipt", fullReceipt).Return(nil).Once() 93 } 94 s.index.On("ByBlockID", incorporatedBlockID).Return(index, nil) 95 96 s.engine.OnBlockIncorporated(model.BlockFromFlow(incorporatedBlock)) 97 98 // matching engine has at least 100ms ticks for processing events 99 time.Sleep(1 * time.Second) 100 101 s.core.AssertExpectations(s.T()) 102 } 103 104 // TestMultipleProcessingItems tests that the engine queues multiple receipts 105 // and eventually feeds them into matching.Core for processing 106 func (s *MatchingEngineSuite) TestMultipleProcessingItems() { 107 originID := unittest.IdentifierFixture() 108 block := unittest.BlockFixture() 109 110 receipts := make([]*flow.ExecutionReceipt, 20) 111 for i := range receipts { 112 receipt := unittest.ExecutionReceiptFixture( 113 unittest.WithExecutorID(originID), 114 unittest.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))), 115 ) 116 receipts[i] = receipt 117 s.core.On("ProcessReceipt", receipt).Return(nil).Once() 118 } 119 120 var wg sync.WaitGroup 121 wg.Add(1) 122 go func() { 123 defer wg.Done() 124 for _, receipt := range receipts { 125 err := s.engine.Process(channels.ReceiveReceipts, originID, receipt) 126 s.Require().NoError(err, "should add receipt and result to mempool if valid") 127 } 128 }() 129 130 wg.Wait() 131 132 // matching engine has at least 100ms ticks for processing events 133 time.Sleep(1 * time.Second) 134 135 s.core.AssertExpectations(s.T()) 136 } 137 138 // TestProcessUnsupportedMessageType tests that Process and ProcessLocal correctly handle a case where invalid message type 139 // was submitted from network layer. 140 func (s *MatchingEngineSuite) TestProcessUnsupportedMessageType() { 141 invalidEvent := uint64(42) 142 err := s.engine.Process("ch", unittest.IdentifierFixture(), invalidEvent) 143 // shouldn't result in error since byzantine inputs are expected 144 require.NoError(s.T(), err) 145 // in case of local processing error cannot be consumed since all inputs are trusted 146 err = s.engine.ProcessLocal(invalidEvent) 147 require.Error(s.T(), err) 148 require.True(s.T(), engine.IsIncompatibleInputTypeError(err)) 149 }