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  }