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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, 111 event: scPeerError{peerID: "P2"}, 112 wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, 113 wantNextEvent: noOp, 114 }, 115 }, 116 }, 117 { 118 name: "error for unknown peer", 119 steps: []pcFsmMakeStateValues{ 120 { 121 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, 122 event: scPeerError{peerID: "P3"}, 123 wantState: ¶ms{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: ¶ms{}, event: mBlockResponse("P1", 1), 140 wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp, 141 }, 142 }, 143 }, 144 145 { 146 name: "add two blocks", 147 steps: []pcFsmMakeStateValues{ 148 { 149 currentState: ¶ms{}, event: mBlockResponse("P1", 3), 150 wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp, 151 }, 152 { // use previous wantState as currentState, 153 event: mBlockResponse("P1", 4), 154 wantState: ¶ms{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: ¶ms{}, event: rProcessBlock{}, 170 wantState: ¶ms{}, wantNextEvent: noOp, 171 }, 172 }, 173 }, 174 { 175 name: "noop - high new blocks", 176 steps: []pcFsmMakeStateValues{ 177 { 178 currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: rProcessBlock{}, 179 wantState: ¶ms{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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: rProcessBlock{}, 188 wantState: ¶ms{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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}}, 198 event: scFinishedEv{}, 199 wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true}, 200 wantNextEvent: noOp, 201 }, 202 { 203 event: rProcessBlock{}, 204 wantState: ¶ms{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: ¶ms{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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: rProcessBlock{}, 226 wantState: ¶ms{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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: rProcessBlock{}, 236 wantState: ¶ms{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: ¶ms{height: 0, items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}}, 245 verBL: []int64{1}}, event: rProcessBlock{}, 246 wantState: ¶ms{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: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P2", 3}}, appBL: []int64{1}}, 256 event: rProcessBlock{}, 257 wantState: ¶ms{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: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: scFinishedEv{}, 273 wantState: ¶ms{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: ¶ms{height: 100, items: []pcBlock{ 283 {"P1", 101}}, blocksSynced: 100}, event: scFinishedEv{}, 284 wantState: ¶ms{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: ¶ms{height: 100, items: []pcBlock{ 294 {"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: scFinishedEv{}, 295 wantState: ¶ms{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 }