github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/voteaggregator/vote_aggregator_test.go (about) 1 package voteaggregator 2 3 import ( 4 "context" 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/helper" 13 "github.com/onflow/flow-go/consensus/hotstuff/mocks" 14 "github.com/onflow/flow-go/consensus/hotstuff/model" 15 "github.com/onflow/flow-go/module/irrecoverable" 16 "github.com/onflow/flow-go/module/metrics" 17 "github.com/onflow/flow-go/utils/unittest" 18 ) 19 20 func TestVoteAggregator(t *testing.T) { 21 suite.Run(t, new(VoteAggregatorTestSuite)) 22 } 23 24 // VoteAggregatorTestSuite is a test suite for isolated testing of VoteAggregator. 25 // Contains mocked state which is used to verify correct behavior of VoteAggregator. 26 // Automatically starts and stops module.Startable in SetupTest and TearDownTest respectively. 27 type VoteAggregatorTestSuite struct { 28 suite.Suite 29 30 aggregator *VoteAggregator 31 collectors *mocks.VoteCollectors 32 consumer *mocks.VoteAggregationConsumer 33 stopAggregator context.CancelFunc 34 errs <-chan error 35 } 36 37 func (s *VoteAggregatorTestSuite) SetupTest() { 38 var err error 39 s.collectors = mocks.NewVoteCollectors(s.T()) 40 s.consumer = mocks.NewVoteAggregationConsumer(s.T()) 41 42 s.collectors.On("Start", mock.Anything).Once() 43 unittest.ReadyDoneify(s.collectors) 44 45 metricsCollector := metrics.NewNoopCollector() 46 47 s.aggregator, err = NewVoteAggregator( 48 unittest.Logger(), 49 metricsCollector, 50 metricsCollector, 51 metricsCollector, 52 s.consumer, 53 0, 54 s.collectors, 55 ) 56 require.NoError(s.T(), err) 57 58 ctx, cancel := context.WithCancel(context.Background()) 59 signalerCtx, errs := irrecoverable.WithSignaler(ctx) 60 s.stopAggregator = cancel 61 s.errs = errs 62 s.aggregator.Start(signalerCtx) 63 unittest.RequireCloseBefore(s.T(), s.aggregator.Ready(), 100*time.Millisecond, "should close before timeout") 64 } 65 66 func (s *VoteAggregatorTestSuite) TearDownTest() { 67 s.stopAggregator() 68 unittest.RequireCloseBefore(s.T(), s.aggregator.Done(), 10*time.Second, "should close before timeout") 69 } 70 71 // TestOnFinalizedBlock tests if finalized block gets processed when send through `VoteAggregator`. 72 // Tests the whole processing pipeline. 73 func (s *VoteAggregatorTestSuite) TestOnFinalizedBlock() { 74 finalizedBlock := unittest.BlockHeaderFixture(unittest.HeaderWithView(100)) 75 done := make(chan struct{}) 76 s.collectors.On("PruneUpToView", finalizedBlock.View).Run(func(args mock.Arguments) { 77 close(done) 78 }).Once() 79 s.aggregator.OnFinalizedBlock(model.BlockFromFlow(finalizedBlock)) 80 unittest.AssertClosesBefore(s.T(), done, time.Second) 81 } 82 83 // TestProcessInvalidBlock tests that processing invalid block results in exception, when given as 84 // an input to AddBlock (only expects _valid_ blocks per API contract). 85 // The exception should be propagated to the VoteAggregator's internal `ComponentManager`. 86 func (s *VoteAggregatorTestSuite) TestProcessInvalidBlock() { 87 block := helper.MakeProposal( 88 helper.WithBlock( 89 helper.MakeBlock( 90 helper.WithBlockView(100), 91 ), 92 ), 93 ) 94 processed := make(chan struct{}) 95 collector := mocks.NewVoteCollector(s.T()) 96 collector.On("ProcessBlock", block).Run(func(_ mock.Arguments) { 97 close(processed) 98 }).Return(model.InvalidProposalError{}) 99 s.collectors.On("GetOrCreateCollector", block.Block.View).Return(collector, true, nil).Once() 100 101 // submit block for processing 102 s.aggregator.AddBlock(block) 103 unittest.RequireCloseBefore(s.T(), processed, 100*time.Millisecond, "should close before timeout") 104 105 // expect a thrown error 106 select { 107 case err := <-s.errs: 108 require.Error(s.T(), err) 109 require.False(s.T(), model.IsInvalidProposalError(err)) 110 case <-time.After(100 * time.Millisecond): 111 s.T().Fatalf("expected error but haven't received anything") 112 } 113 }