github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/approvals/assignment_collector_statemachine_test.go (about) 1 package approvals 2 3 import ( 4 "errors" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/gammazero/workerpool" 10 "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 "github.com/stretchr/testify/suite" 13 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/utils/unittest" 16 ) 17 18 // AssignmentCollectorStateMachineTestSuite is a test suite for testing AssignmentCollectorStateMachine. Contains a minimal set of 19 // helper mocks to test the behavior. 20 type AssignmentCollectorStateMachineTestSuite struct { 21 BaseAssignmentCollectorTestSuite 22 collector *AssignmentCollectorStateMachine 23 } 24 25 func TestAssignmentCollectorStateMachine(t *testing.T) { 26 suite.Run(t, new(AssignmentCollectorStateMachineTestSuite)) 27 } 28 29 func (s *AssignmentCollectorStateMachineTestSuite) SetupTest() { 30 s.BaseAssignmentCollectorTestSuite.SetupTest() 31 32 s.collector = NewAssignmentCollectorStateMachine(AssignmentCollectorBase{ 33 workerPool: workerpool.New(4), 34 assigner: s.Assigner, 35 state: s.State, 36 headers: s.Headers, 37 sigHasher: s.SigHasher, 38 seals: s.SealsPL, 39 approvalConduit: s.Conduit, 40 requestTracker: s.RequestTracker, 41 requiredApprovalsForSealConstruction: 5, 42 executedBlock: s.Block, 43 result: s.IncorporatedResult.Result, 44 resultID: s.IncorporatedResult.Result.ID(), 45 }) 46 } 47 48 // TestChangeProcessingStatus_CachingToVerifying tests that state machine correctly performs transition from CachingApprovals to 49 // VerifyingApprovals state. After transition all caches approvals and results need to be applied to new state. 50 func (s *AssignmentCollectorStateMachineTestSuite) TestChangeProcessingStatus_CachingToVerifying() { 51 require.Equal(s.T(), CachingApprovals, s.collector.ProcessingStatus()) 52 results := make([]*flow.IncorporatedResult, 3) 53 54 s.PublicKey.On("Verify", mock.Anything, mock.Anything, mock.Anything).Return(true, nil) 55 56 for i := range results { 57 block := unittest.BlockHeaderWithParentFixture(s.Block) 58 s.Blocks[block.ID()] = block 59 result := unittest.IncorporatedResult.Fixture( 60 unittest.IncorporatedResult.WithIncorporatedBlockID(block.ID()), 61 unittest.IncorporatedResult.WithResult(s.IncorporatedResult.Result), 62 ) 63 results[i] = result 64 } 65 66 approvals := make([]*flow.ResultApproval, s.Chunks.Len()) 67 68 for i := range approvals { 69 approval := unittest.ResultApprovalFixture( 70 unittest.WithExecutionResultID(s.IncorporatedResult.Result.ID()), 71 unittest.WithChunk(uint64(i)), 72 unittest.WithApproverID(s.VerID), 73 unittest.WithBlockID(s.Block.ID()), 74 ) 75 approvals[i] = approval 76 } 77 78 var wg sync.WaitGroup 79 wg.Add(1) 80 go func() { 81 defer wg.Done() 82 for _, result := range results { 83 require.NoError(s.T(), s.collector.ProcessIncorporatedResult(result)) 84 } 85 }() 86 87 wg.Add(1) 88 go func() { 89 defer wg.Done() 90 for _, approval := range approvals { 91 require.NoError(s.T(), s.collector.ProcessApproval(approval)) 92 } 93 }() 94 95 err := s.collector.ChangeProcessingStatus(CachingApprovals, VerifyingApprovals) 96 require.NoError(s.T(), err) 97 require.Equal(s.T(), VerifyingApprovals, s.collector.ProcessingStatus()) 98 99 wg.Wait() 100 101 // give some time to process on worker pool 102 time.Sleep(1 * time.Second) 103 // need to check if collector has processed cached items 104 verifyingCollector, ok := s.collector.atomicLoadCollector().(*VerifyingAssignmentCollector) 105 require.True(s.T(), ok) 106 107 for _, ir := range results { 108 verifyingCollector.lock.Lock() 109 collector, ok := verifyingCollector.collectors[ir.IncorporatedBlockID] 110 verifyingCollector.lock.Unlock() 111 require.True(s.T(), ok) 112 113 for _, approval := range approvals { 114 chunkCollector := collector.chunkCollectors[approval.Body.ChunkIndex] 115 chunkCollector.lock.Lock() 116 signed := chunkCollector.chunkApprovals.HasSigned(approval.Body.ApproverID) 117 chunkCollector.lock.Unlock() 118 require.True(s.T(), signed) 119 } 120 } 121 } 122 123 // TestChangeProcessingStatus_InvalidTransition tries to perform transition from caching to verifying status 124 // but with underlying orphan status. This should result in sentinel error ErrInvalidCollectorStateTransition. 125 func (s *AssignmentCollectorStateMachineTestSuite) TestChangeProcessingStatus_InvalidTransition() { 126 // first change status to orphan 127 err := s.collector.ChangeProcessingStatus(CachingApprovals, Orphaned) 128 require.NoError(s.T(), err) 129 require.Equal(s.T(), Orphaned, s.collector.ProcessingStatus()) 130 // then try to perform transition from caching to verifying 131 err = s.collector.ChangeProcessingStatus(CachingApprovals, VerifyingApprovals) 132 require.Error(s.T(), err) 133 require.True(s.T(), errors.Is(err, ErrDifferentCollectorState)) 134 }