github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowman/bootstrapper/minority_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package bootstrapper
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils/logging"
    14  	"github.com/MetalBlockchain/metalgo/utils/set"
    15  )
    16  
    17  func TestNewMinority(t *testing.T) {
    18  	minority := NewMinority(
    19  		logging.NoLog{}, // log
    20  		set.Of(nodeID0), // frontierNodes
    21  		2,               // maxOutstanding
    22  	)
    23  
    24  	expectedMinority := &Minority{
    25  		requests: requests{
    26  			maxOutstanding: 2,
    27  			pendingSend:    set.Of(nodeID0),
    28  		},
    29  		log: logging.NoLog{},
    30  	}
    31  	require.Equal(t, expectedMinority, minority)
    32  }
    33  
    34  func TestMinorityGetPeers(t *testing.T) {
    35  	tests := []struct {
    36  		name          string
    37  		minority      Poll
    38  		expectedState Poll
    39  		expectedPeers set.Set[ids.NodeID]
    40  	}{
    41  		{
    42  			name: "max outstanding",
    43  			minority: &Minority{
    44  				requests: requests{
    45  					maxOutstanding: 1,
    46  					pendingSend:    set.Of(nodeID0),
    47  					outstanding:    set.Of(nodeID1),
    48  				},
    49  				log: logging.NoLog{},
    50  			},
    51  			expectedState: &Minority{
    52  				requests: requests{
    53  					maxOutstanding: 1,
    54  					pendingSend:    set.Of(nodeID0),
    55  					outstanding:    set.Of(nodeID1),
    56  				},
    57  				log: logging.NoLog{},
    58  			},
    59  			expectedPeers: nil,
    60  		},
    61  		{
    62  			name: "send until max outstanding",
    63  			minority: &Minority{
    64  				requests: requests{
    65  					maxOutstanding: 2,
    66  					pendingSend:    set.Of(nodeID0, nodeID1),
    67  				},
    68  				log: logging.NoLog{},
    69  			},
    70  			expectedState: &Minority{
    71  				requests: requests{
    72  					maxOutstanding: 2,
    73  					pendingSend:    set.Set[ids.NodeID]{},
    74  					outstanding:    set.Of(nodeID0, nodeID1),
    75  				},
    76  				log: logging.NoLog{},
    77  			},
    78  			expectedPeers: set.Of(nodeID0, nodeID1),
    79  		},
    80  		{
    81  			name: "send until no more to send",
    82  			minority: &Minority{
    83  				requests: requests{
    84  					maxOutstanding: 2,
    85  					pendingSend:    set.Of(nodeID0),
    86  				},
    87  				log: logging.NoLog{},
    88  			},
    89  			expectedState: &Minority{
    90  				requests: requests{
    91  					maxOutstanding: 2,
    92  					pendingSend:    set.Set[ids.NodeID]{},
    93  					outstanding:    set.Of(nodeID0),
    94  				},
    95  				log: logging.NoLog{},
    96  			},
    97  			expectedPeers: set.Of(nodeID0),
    98  		},
    99  	}
   100  	for _, test := range tests {
   101  		t.Run(test.name, func(t *testing.T) {
   102  			require := require.New(t)
   103  
   104  			peers := test.minority.GetPeers(context.Background())
   105  			require.Equal(test.expectedState, test.minority)
   106  			require.Equal(test.expectedPeers, peers)
   107  		})
   108  	}
   109  }
   110  
   111  func TestMinorityRecordOpinion(t *testing.T) {
   112  	tests := []struct {
   113  		name          string
   114  		minority      Poll
   115  		nodeID        ids.NodeID
   116  		blkIDs        set.Set[ids.ID]
   117  		expectedState Poll
   118  		expectedErr   error
   119  	}{
   120  		{
   121  			name: "unexpected response",
   122  			minority: &Minority{
   123  				requests: requests{
   124  					maxOutstanding: 1,
   125  					pendingSend:    set.Of(nodeID0),
   126  					outstanding:    set.Of(nodeID1),
   127  				},
   128  				log: logging.NoLog{},
   129  			},
   130  			nodeID: nodeID0,
   131  			blkIDs: nil,
   132  			expectedState: &Minority{
   133  				requests: requests{
   134  					maxOutstanding: 1,
   135  					pendingSend:    set.Of(nodeID0),
   136  					outstanding:    set.Of(nodeID1),
   137  				},
   138  				log: logging.NoLog{},
   139  			},
   140  			expectedErr: nil,
   141  		},
   142  		{
   143  			name: "unfinished after response",
   144  			minority: &Minority{
   145  				requests: requests{
   146  					maxOutstanding: 1,
   147  					pendingSend:    set.Of(nodeID0),
   148  					outstanding:    set.Of(nodeID1),
   149  				},
   150  				log: logging.NoLog{},
   151  			},
   152  			nodeID: nodeID1,
   153  			blkIDs: set.Of(blkID0),
   154  			expectedState: &Minority{
   155  				requests: requests{
   156  					maxOutstanding: 1,
   157  					pendingSend:    set.Of(nodeID0),
   158  					outstanding:    set.Set[ids.NodeID]{},
   159  				},
   160  				log:         logging.NoLog{},
   161  				receivedSet: set.Of(blkID0),
   162  			},
   163  			expectedErr: nil,
   164  		},
   165  		{
   166  			name: "finished after response",
   167  			minority: &Minority{
   168  				requests: requests{
   169  					maxOutstanding: 1,
   170  					outstanding:    set.Of(nodeID2),
   171  				},
   172  				log: logging.NoLog{},
   173  			},
   174  			nodeID: nodeID2,
   175  			blkIDs: set.Of(blkID1),
   176  			expectedState: &Minority{
   177  				requests: requests{
   178  					maxOutstanding: 1,
   179  					outstanding:    set.Set[ids.NodeID]{},
   180  				},
   181  				log:         logging.NoLog{},
   182  				receivedSet: set.Of(blkID1),
   183  				received:    []ids.ID{blkID1},
   184  			},
   185  			expectedErr: nil,
   186  		},
   187  	}
   188  	for _, test := range tests {
   189  		t.Run(test.name, func(t *testing.T) {
   190  			require := require.New(t)
   191  
   192  			err := test.minority.RecordOpinion(context.Background(), test.nodeID, test.blkIDs)
   193  			require.Equal(test.expectedState, test.minority)
   194  			require.ErrorIs(err, test.expectedErr)
   195  		})
   196  	}
   197  }
   198  
   199  func TestMinorityResult(t *testing.T) {
   200  	tests := []struct {
   201  		name              string
   202  		minority          Poll
   203  		expectedAccepted  []ids.ID
   204  		expectedFinalized bool
   205  	}{
   206  		{
   207  			name: "not finalized",
   208  			minority: &Minority{
   209  				requests: requests{
   210  					maxOutstanding: 1,
   211  					outstanding:    set.Of(nodeID1),
   212  				},
   213  				log:      logging.NoLog{},
   214  				received: nil,
   215  			},
   216  			expectedAccepted:  nil,
   217  			expectedFinalized: false,
   218  		},
   219  		{
   220  			name: "finalized",
   221  			minority: &Minority{
   222  				requests: requests{
   223  					maxOutstanding: 1,
   224  				},
   225  				log:         logging.NoLog{},
   226  				receivedSet: set.Of(blkID0),
   227  				received:    []ids.ID{blkID0},
   228  			},
   229  			expectedAccepted:  []ids.ID{blkID0},
   230  			expectedFinalized: true,
   231  		},
   232  	}
   233  	for _, test := range tests {
   234  		t.Run(test.name, func(t *testing.T) {
   235  			require := require.New(t)
   236  
   237  			accepted, finalized := test.minority.Result(context.Background())
   238  			require.Equal(test.expectedAccepted, accepted)
   239  			require.Equal(test.expectedFinalized, finalized)
   240  		})
   241  	}
   242  }