github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/approvals/testutil.go (about) 1 package approvals 2 3 import ( 4 "github.com/gammazero/workerpool" 5 "github.com/onflow/crypto/hash" 6 "github.com/stretchr/testify/mock" 7 "github.com/stretchr/testify/suite" 8 9 "github.com/onflow/flow-go/model/chunks" 10 "github.com/onflow/flow-go/model/flow" 11 mempool "github.com/onflow/flow-go/module/mempool/mock" 12 module "github.com/onflow/flow-go/module/mock" 13 msig "github.com/onflow/flow-go/module/signature" 14 "github.com/onflow/flow-go/network/mocknetwork" 15 realproto "github.com/onflow/flow-go/state/protocol" 16 protocol "github.com/onflow/flow-go/state/protocol/mock" 17 realstorage "github.com/onflow/flow-go/storage" 18 storage "github.com/onflow/flow-go/storage/mock" 19 "github.com/onflow/flow-go/utils/unittest" 20 ) 21 22 // BaseApprovalsTestSuite is a base suite for testing approvals processing related functionality 23 // At nutshell generates mock data that can be used to create approvals and provides all needed 24 // data to validate those approvals for respected execution result. 25 type BaseApprovalsTestSuite struct { 26 suite.Suite 27 28 ParentBlock *flow.Header // parent of sealing candidate 29 Block *flow.Header // candidate for sealing 30 IncorporatedBlock *flow.Header // block that incorporated result 31 VerID flow.Identifier // for convenience, node id of first verifier 32 Chunks flow.ChunkList // list of chunks of execution result 33 ChunksAssignment *chunks.Assignment 34 AuthorizedVerifiers map[flow.Identifier]*flow.Identity // map of authorized verifier identities for execution result 35 PublicKey *module.PublicKey // public key used to mock signature verifications 36 SigHasher hash.Hasher // used to verify signatures 37 IncorporatedResult *flow.IncorporatedResult 38 } 39 40 func (s *BaseApprovalsTestSuite) SetupTest() { 41 s.ParentBlock = unittest.BlockHeaderFixture() 42 s.Block = unittest.BlockHeaderWithParentFixture(s.ParentBlock) 43 verifiers := make(flow.IdentifierList, 0) 44 s.AuthorizedVerifiers = make(map[flow.Identifier]*flow.Identity) 45 s.ChunksAssignment = chunks.NewAssignment() 46 s.Chunks = unittest.ChunkListFixture(50, s.Block.ID()) 47 // mock public key to mock signature verifications 48 s.PublicKey = &module.PublicKey{} 49 50 // setup identities 51 for j := 0; j < 5; j++ { 52 identity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 53 verifiers = append(verifiers, identity.NodeID) 54 s.AuthorizedVerifiers[identity.NodeID] = identity 55 // mock all verifier's valid signatures 56 identity.StakingPubKey = s.PublicKey 57 } 58 s.SigHasher = msig.NewBLSHasher("test_tag") 59 60 // create assignment 61 for _, chunk := range s.Chunks { 62 s.ChunksAssignment.Add(chunk, verifiers) 63 } 64 65 s.VerID = verifiers[0] 66 result := unittest.ExecutionResultFixture() 67 result.BlockID = s.Block.ID() 68 result.Chunks = s.Chunks 69 70 s.IncorporatedBlock = unittest.BlockHeaderWithParentFixture(s.Block) 71 72 // compose incorporated result 73 s.IncorporatedResult = unittest.IncorporatedResult.Fixture( 74 unittest.IncorporatedResult.WithResult(result), 75 unittest.IncorporatedResult.WithIncorporatedBlockID(s.IncorporatedBlock.ID())) 76 } 77 78 // BaseAssignmentCollectorTestSuite is a base suite for testing assignment collectors, contains mocks for all 79 // classes that are used in base assignment collector and can be reused in different test suites. 80 type BaseAssignmentCollectorTestSuite struct { 81 BaseApprovalsTestSuite 82 83 WorkerPool *workerpool.WorkerPool 84 Blocks map[flow.Identifier]*flow.Header 85 State *protocol.State 86 Snapshots map[flow.Identifier]*protocol.Snapshot 87 Headers *storage.Headers 88 Assigner *module.ChunkAssigner 89 SealsPL *mempool.IncorporatedResultSeals 90 Conduit *mocknetwork.Conduit 91 FinalizedAtHeight map[uint64]*flow.Header 92 IdentitiesCache map[flow.Identifier]map[flow.Identifier]*flow.Identity // helper map to store identities for given block 93 RequestTracker *RequestTracker 94 } 95 96 func (s *BaseAssignmentCollectorTestSuite) SetupTest() { 97 s.BaseApprovalsTestSuite.SetupTest() 98 99 s.WorkerPool = workerpool.New(4) 100 s.SealsPL = &mempool.IncorporatedResultSeals{} 101 s.State = &protocol.State{} 102 s.Assigner = &module.ChunkAssigner{} 103 s.Conduit = &mocknetwork.Conduit{} 104 s.Headers = &storage.Headers{} 105 106 s.RequestTracker = NewRequestTracker(s.Headers, 1, 3) 107 108 s.FinalizedAtHeight = make(map[uint64]*flow.Header) 109 s.FinalizedAtHeight[s.ParentBlock.Height] = s.ParentBlock 110 s.FinalizedAtHeight[s.Block.Height] = s.Block 111 112 // setup blocks cache for protocol state 113 s.Blocks = make(map[flow.Identifier]*flow.Header) 114 s.Blocks[s.ParentBlock.ID()] = s.ParentBlock 115 s.Blocks[s.Block.ID()] = s.Block 116 s.Blocks[s.IncorporatedBlock.ID()] = s.IncorporatedBlock 117 s.Snapshots = make(map[flow.Identifier]*protocol.Snapshot) 118 119 // setup identities for each block 120 s.IdentitiesCache = make(map[flow.Identifier]map[flow.Identifier]*flow.Identity) 121 s.IdentitiesCache[s.IncorporatedResult.Result.BlockID] = s.AuthorizedVerifiers 122 123 s.Assigner.On("Assign", mock.Anything, mock.Anything).Return(func(result *flow.ExecutionResult, blockID flow.Identifier) *chunks.Assignment { 124 return s.ChunksAssignment 125 }, func(result *flow.ExecutionResult, blockID flow.Identifier) error { return nil }) 126 127 s.Headers.On("ByBlockID", mock.Anything).Return(func(blockID flow.Identifier) *flow.Header { 128 return s.Blocks[blockID] 129 }, func(blockID flow.Identifier) error { 130 _, found := s.Blocks[blockID] 131 if found { 132 return nil 133 } else { 134 return realstorage.ErrNotFound 135 } 136 }) 137 s.Headers.On("BlockIDByHeight", mock.Anything).Return( 138 func(height uint64) (flow.Identifier, error) { 139 if block, found := s.FinalizedAtHeight[height]; found { 140 return block.ID(), nil 141 } else { 142 return flow.ZeroID, realstorage.ErrNotFound 143 } 144 }, 145 ) 146 s.Headers.On("ByHeight", mock.Anything).Return( 147 func(height uint64) (*flow.Header, error) { 148 if block, found := s.FinalizedAtHeight[height]; found { 149 return block, nil 150 } else { 151 return nil, realstorage.ErrNotFound 152 } 153 }, 154 ) 155 156 s.State.On("AtBlockID", mock.Anything).Return( 157 func(blockID flow.Identifier) realproto.Snapshot { 158 if snapshot, found := s.Snapshots[blockID]; found { 159 return snapshot 160 } 161 if block, found := s.Blocks[blockID]; found { 162 snapshot := unittest.StateSnapshotForKnownBlock(block, s.IdentitiesCache[blockID]) 163 s.Snapshots[blockID] = snapshot 164 return snapshot 165 } 166 return unittest.StateSnapshotForUnknownBlock() 167 }, 168 ) 169 170 s.SealsPL.On("Size").Return(uint(0)).Maybe() // for metrics 171 s.SealsPL.On("PruneUpToHeight", mock.Anything).Return(nil).Maybe() // noop on pruning 172 } 173 174 func (s *BaseAssignmentCollectorTestSuite) MarkFinalized(block *flow.Header) { 175 s.FinalizedAtHeight[block.Height] = block 176 } 177 178 func (s *BaseAssignmentCollectorTestSuite) TearDownTest() { 179 // Without this line we are risking running into weird situations where one test has finished but there are active workers 180 // that are executing some work on the shared pool. Need to ensure that all pending work has been executed before 181 // starting next test. 182 s.WorkerPool.StopWait() 183 }