github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v2/processor_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  
     8  	tmState "github.com/number571/tendermint/state"
     9  	"github.com/number571/tendermint/types"
    10  )
    11  
    12  // pcBlock is a test helper structure with simple types. Its purpose is to help with test readability.
    13  type pcBlock struct {
    14  	pid    string
    15  	height int64
    16  }
    17  
    18  // params is a test structure used to create processor state.
    19  type params struct {
    20  	height       int64
    21  	items        []pcBlock
    22  	blocksSynced int
    23  	verBL        []int64
    24  	appBL        []int64
    25  	draining     bool
    26  }
    27  
    28  // makePcBlock makes an empty block.
    29  func makePcBlock(height int64) *types.Block {
    30  	return &types.Block{Header: types.Header{Height: height}}
    31  }
    32  
    33  // makeState takes test parameters and creates a specific processor state.
    34  func makeState(p *params) *pcState {
    35  	var (
    36  		tmState = tmState.State{LastBlockHeight: p.height}
    37  		context = newMockProcessorContext(tmState, p.verBL, p.appBL)
    38  	)
    39  	state := newPcState(context)
    40  
    41  	for _, item := range p.items {
    42  		state.enqueue(types.NodeID(item.pid), makePcBlock(item.height), item.height)
    43  	}
    44  
    45  	state.blocksSynced = p.blocksSynced
    46  	state.draining = p.draining
    47  	return state
    48  }
    49  
    50  func mBlockResponse(peerID types.NodeID, height int64) scBlockReceived {
    51  	return scBlockReceived{
    52  		peerID: peerID,
    53  		block:  makePcBlock(height),
    54  	}
    55  }
    56  
    57  type pcFsmMakeStateValues struct {
    58  	currentState  *params
    59  	event         Event
    60  	wantState     *params
    61  	wantNextEvent Event
    62  	wantErr       error
    63  	wantPanic     bool
    64  }
    65  
    66  type testFields struct {
    67  	name  string
    68  	steps []pcFsmMakeStateValues
    69  }
    70  
    71  func executeProcessorTests(t *testing.T, tests []testFields) {
    72  	for _, tt := range tests {
    73  		tt := tt
    74  		t.Run(tt.name, func(t *testing.T) {
    75  			var state *pcState
    76  			for _, step := range tt.steps {
    77  				defer func() {
    78  					r := recover()
    79  					if (r != nil) != step.wantPanic {
    80  						t.Errorf("recover = %v, wantPanic = %v", r, step.wantPanic)
    81  					}
    82  				}()
    83  
    84  				// First step must always initialize the currentState as state.
    85  				if step.currentState != nil {
    86  					state = makeState(step.currentState)
    87  				}
    88  				if state == nil {
    89  					panic("Bad (initial?) step")
    90  				}
    91  
    92  				nextEvent, err := state.handle(step.event)
    93  				t.Log(state)
    94  				assert.Equal(t, step.wantErr, err)
    95  				assert.Equal(t, makeState(step.wantState), state)
    96  				assert.Equal(t, step.wantNextEvent, nextEvent)
    97  				// Next step may use the wantedState as their currentState.
    98  				state = makeState(step.wantState)
    99  			}
   100  		})
   101  	}
   102  }
   103  
   104  func TestRProcessPeerError(t *testing.T) {
   105  	tests := []testFields{
   106  		{
   107  			name: "error for existing peer",
   108  			steps: []pcFsmMakeStateValues{
   109  				{
   110  					currentState:  &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
   111  					event:         scPeerError{peerID: "P2"},
   112  					wantState:     &params{items: []pcBlock{{"P1", 1}}},
   113  					wantNextEvent: noOp,
   114  				},
   115  			},
   116  		},
   117  		{
   118  			name: "error for unknown peer",
   119  			steps: []pcFsmMakeStateValues{
   120  				{
   121  					currentState:  &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
   122  					event:         scPeerError{peerID: "P3"},
   123  					wantState:     &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
   124  					wantNextEvent: noOp,
   125  				},
   126  			},
   127  		},
   128  	}
   129  
   130  	executeProcessorTests(t, tests)
   131  }
   132  
   133  func TestPcBlockResponse(t *testing.T) {
   134  	tests := []testFields{
   135  		{
   136  			name: "add one block",
   137  			steps: []pcFsmMakeStateValues{
   138  				{
   139  					currentState: &params{}, event: mBlockResponse("P1", 1),
   140  					wantState: &params{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp,
   141  				},
   142  			},
   143  		},
   144  
   145  		{
   146  			name: "add two blocks",
   147  			steps: []pcFsmMakeStateValues{
   148  				{
   149  					currentState: &params{}, event: mBlockResponse("P1", 3),
   150  					wantState: &params{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp,
   151  				},
   152  				{ // use previous wantState as currentState,
   153  					event:     mBlockResponse("P1", 4),
   154  					wantState: &params{items: []pcBlock{{"P1", 3}, {"P1", 4}}}, wantNextEvent: noOp,
   155  				},
   156  			},
   157  		},
   158  	}
   159  
   160  	executeProcessorTests(t, tests)
   161  }
   162  
   163  func TestRProcessBlockSuccess(t *testing.T) {
   164  	tests := []testFields{
   165  		{
   166  			name: "noop - no blocks over current height",
   167  			steps: []pcFsmMakeStateValues{
   168  				{
   169  					currentState: &params{}, event: rProcessBlock{},
   170  					wantState: &params{}, wantNextEvent: noOp,
   171  				},
   172  			},
   173  		},
   174  		{
   175  			name: "noop - high new blocks",
   176  			steps: []pcFsmMakeStateValues{
   177  				{
   178  					currentState: &params{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: rProcessBlock{},
   179  					wantState: &params{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, wantNextEvent: noOp,
   180  				},
   181  			},
   182  		},
   183  		{
   184  			name: "blocks H+1 and H+2 present",
   185  			steps: []pcFsmMakeStateValues{
   186  				{
   187  					currentState: &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: rProcessBlock{},
   188  					wantState:     &params{height: 1, items: []pcBlock{{"P2", 2}}, blocksSynced: 1},
   189  					wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"},
   190  				},
   191  			},
   192  		},
   193  		{
   194  			name: "blocks H+1 and H+2 present after draining",
   195  			steps: []pcFsmMakeStateValues{
   196  				{ // some contiguous blocks - on stop check draining is set
   197  					currentState:  &params{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}},
   198  					event:         scFinishedEv{},
   199  					wantState:     &params{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true},
   200  					wantNextEvent: noOp,
   201  				},
   202  				{
   203  					event:         rProcessBlock{},
   204  					wantState:     &params{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true},
   205  					wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"},
   206  				},
   207  				{ // finish when H+1 or/and H+2 are missing
   208  					event:         rProcessBlock{},
   209  					wantState:     &params{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true},
   210  					wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 1}, blocksSynced: 1},
   211  				},
   212  			},
   213  		},
   214  	}
   215  
   216  	executeProcessorTests(t, tests)
   217  }
   218  
   219  func TestRProcessBlockFailures(t *testing.T) {
   220  	tests := []testFields{
   221  		{
   222  			name: "blocks H+1 and H+2 present from different peers - H+1 verification fails ",
   223  			steps: []pcFsmMakeStateValues{
   224  				{
   225  					currentState: &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: rProcessBlock{},
   226  					wantState:     &params{items: []pcBlock{}, verBL: []int64{1}},
   227  					wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P2"},
   228  				},
   229  			},
   230  		},
   231  		{
   232  			name: "blocks H+1 and H+2 present from same peer - H+1 applyBlock fails ",
   233  			steps: []pcFsmMakeStateValues{
   234  				{
   235  					currentState: &params{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: rProcessBlock{},
   236  					wantState: &params{items: []pcBlock{}, appBL: []int64{1}}, wantPanic: true,
   237  				},
   238  			},
   239  		},
   240  		{
   241  			name: "blocks H+1 and H+2 present from same peers - H+1 verification fails ",
   242  			steps: []pcFsmMakeStateValues{
   243  				{
   244  					currentState: &params{height: 0, items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}},
   245  						verBL: []int64{1}}, event: rProcessBlock{},
   246  					wantState:     &params{height: 0, items: []pcBlock{{"P2", 3}}, verBL: []int64{1}},
   247  					wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"},
   248  				},
   249  			},
   250  		},
   251  		{
   252  			name: "blocks H+1 and H+2 present from different peers - H+1 applyBlock fails ",
   253  			steps: []pcFsmMakeStateValues{
   254  				{
   255  					currentState: &params{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P2", 3}}, appBL: []int64{1}},
   256  					event:        rProcessBlock{},
   257  					wantState:    &params{items: []pcBlock{{"P2", 3}}, appBL: []int64{1}}, wantPanic: true,
   258  				},
   259  			},
   260  		},
   261  	}
   262  
   263  	executeProcessorTests(t, tests)
   264  }
   265  
   266  func TestScFinishedEv(t *testing.T) {
   267  	tests := []testFields{
   268  		{
   269  			name: "no blocks",
   270  			steps: []pcFsmMakeStateValues{
   271  				{
   272  					currentState: &params{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: scFinishedEv{},
   273  					wantState:     &params{height: 100, items: []pcBlock{}, blocksSynced: 100},
   274  					wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100},
   275  				},
   276  			},
   277  		},
   278  		{
   279  			name: "maxHeight+1 block present",
   280  			steps: []pcFsmMakeStateValues{
   281  				{
   282  					currentState: &params{height: 100, items: []pcBlock{
   283  						{"P1", 101}}, blocksSynced: 100}, event: scFinishedEv{},
   284  					wantState:     &params{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100},
   285  					wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100},
   286  				},
   287  			},
   288  		},
   289  		{
   290  			name: "more blocks present",
   291  			steps: []pcFsmMakeStateValues{
   292  				{
   293  					currentState: &params{height: 100, items: []pcBlock{
   294  						{"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: scFinishedEv{},
   295  					wantState: &params{height: 100, items: []pcBlock{
   296  						{"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true},
   297  					wantNextEvent: noOp,
   298  					wantErr:       nil,
   299  				},
   300  			},
   301  		},
   302  	}
   303  
   304  	executeProcessorTests(t, tests)
   305  }