github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/kafka/chain_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kafka 8 9 import ( 10 "context" 11 "errors" 12 "fmt" 13 "testing" 14 "time" 15 16 "github.com/Shopify/sarama" 17 "github.com/Shopify/sarama/mocks" 18 "github.com/golang/protobuf/proto" 19 "github.com/hechain20/hechain/common/channelconfig" 20 "github.com/hechain20/hechain/common/metrics/disabled" 21 "github.com/hechain20/hechain/orderer/common/blockcutter" 22 "github.com/hechain20/hechain/orderer/common/msgprocessor" 23 mockkafka "github.com/hechain20/hechain/orderer/consensus/kafka/mock" 24 mockblockcutter "github.com/hechain20/hechain/orderer/mocks/common/blockcutter" 25 mockmultichannel "github.com/hechain20/hechain/orderer/mocks/common/multichannel" 26 "github.com/hechain20/hechain/protoutil" 27 cb "github.com/hyperledger/fabric-protos-go/common" 28 ab "github.com/hyperledger/fabric-protos-go/orderer" 29 . "github.com/onsi/gomega" 30 "github.com/stretchr/testify/mock" 31 "github.com/stretchr/testify/require" 32 ) 33 34 //go:generate counterfeiter -o mock/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities 35 36 type ordererCapabilities interface { 37 channelconfig.OrdererCapabilities 38 } 39 40 //go:generate counterfeiter -o mock/channel_capabilities.go --fake-name ChannelCapabilities . channelCapabilities 41 42 type channelCapabilities interface { 43 channelconfig.ChannelCapabilities 44 } 45 46 //go:generate counterfeiter -o mock/channel_config.go --fake-name ChannelConfig . channelConfig 47 48 type channelConfig interface { 49 channelconfig.Channel 50 } 51 52 func newMockOrderer(batchTimeout time.Duration, brokers []string, resubmission bool) *mockkafka.OrdererConfig { 53 mockCapabilities := &mockkafka.OrdererCapabilities{} 54 mockCapabilities.ResubmissionReturns(resubmission) 55 mockOrderer := &mockkafka.OrdererConfig{} 56 mockOrderer.CapabilitiesReturns(mockCapabilities) 57 mockOrderer.BatchTimeoutReturns(batchTimeout) 58 mockOrderer.KafkaBrokersReturns(brokers) 59 return mockOrderer 60 } 61 62 func newMockChannel() *mockkafka.ChannelConfig { 63 mockCapabilities := &mockkafka.ChannelCapabilities{} 64 mockCapabilities.ConsensusTypeMigrationReturns(false) 65 mockChannel := &mockkafka.ChannelConfig{} 66 mockChannel.CapabilitiesReturns(mockCapabilities) 67 return mockChannel 68 } 69 70 const ( 71 extraShortTimeout = time.Millisecond 72 shortTimeout = time.Second 73 longTimeout = time.Hour 74 75 hitBranch = 50 * time.Millisecond 76 ) 77 78 func TestChain(t *testing.T) { 79 oldestOffset := int64(0) 80 newestOffset := int64(5) 81 lastOriginalOffsetProcessed := int64(0) 82 lastResubmittedConfigOffset := int64(0) 83 84 message := sarama.StringEncoder("messageFoo") 85 86 newMocks := func(t *testing.T) (mockChannel channel, mockBroker *sarama.MockBroker, mockSupport *mockmultichannel.ConsenterSupport) { 87 mockChannel = newChannel(channelNameForTest(t), defaultPartition) 88 mockBroker = sarama.NewMockBroker(t, 0) 89 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 90 "MetadataRequest": sarama.NewMockMetadataResponse(t). 91 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 92 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 93 "ProduceRequest": sarama.NewMockProduceResponse(t). 94 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 95 "OffsetRequest": sarama.NewMockOffsetResponse(t). 96 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 97 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 98 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 99 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 100 }) 101 mockSupport = &mockmultichannel.ConsenterSupport{ 102 ChannelIDVal: mockChannel.topic(), 103 HeightVal: uint64(3), 104 SharedConfigVal: newMockOrderer(0, []string{mockBroker.Addr()}, false), 105 ChannelConfigVal: newMockChannel(), 106 } 107 return 108 } 109 110 t.Run("New", func(t *testing.T) { 111 _, mockBroker, mockSupport := newMocks(t) 112 defer func() { mockBroker.Close() }() 113 fakeLastOffsetPersisted := &mockkafka.MetricsGauge{} 114 fakeLastOffsetPersisted.WithReturns(fakeLastOffsetPersisted) 115 mockConsenter.(*consenterImpl).metrics.LastOffsetPersisted = fakeLastOffsetPersisted 116 chain, err := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 117 118 require.NoError(t, err, "Expected newChain to return without errors") 119 select { 120 case <-chain.Errored(): 121 logger.Debug("Errored() returned a closed channel as expected") 122 default: 123 t.Fatal("Errored() should have returned a closed channel") 124 } 125 126 select { 127 case <-chain.haltChan: 128 t.Fatal("haltChan should have been open") 129 default: 130 logger.Debug("haltChan is open as it should be") 131 } 132 133 select { 134 case <-chain.startChan: 135 t.Fatal("startChan should have been open") 136 default: 137 logger.Debug("startChan is open as it should be") 138 } 139 140 require.Equal(t, fakeLastOffsetPersisted.WithCallCount(), 1) 141 require.Equal(t, fakeLastOffsetPersisted.WithArgsForCall(0), []string{"channel", channelNameForTest(t)}) 142 require.Equal(t, fakeLastOffsetPersisted.SetCallCount(), 1) 143 require.Equal(t, fakeLastOffsetPersisted.SetArgsForCall(0), float64(newestOffset-1)) 144 }) 145 146 t.Run("Start", func(t *testing.T) { 147 _, mockBroker, mockSupport := newMocks(t) 148 defer func() { mockBroker.Close() }() 149 // Set to -1 because we haven't sent the CONNECT message yet 150 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 151 152 chain.Start() 153 select { 154 case <-chain.startChan: 155 logger.Debug("startChan is closed as it should be") 156 case <-time.After(shortTimeout): 157 t.Fatal("startChan should have been closed by now") 158 } 159 160 // Trigger the haltChan clause in the processMessagesToBlocks goroutine 161 close(chain.haltChan) 162 }) 163 164 t.Run("Halt", func(t *testing.T) { 165 _, mockBroker, mockSupport := newMocks(t) 166 defer func() { mockBroker.Close() }() 167 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 168 169 chain.Start() 170 select { 171 case <-chain.startChan: 172 logger.Debug("startChan is closed as it should be") 173 case <-time.After(shortTimeout): 174 t.Fatal("startChan should have been closed by now") 175 } 176 177 // Wait till the start phase has completed, then: 178 chain.Halt() 179 180 select { 181 case <-chain.haltChan: 182 logger.Debug("haltChan is closed as it should be") 183 case <-time.After(shortTimeout): 184 t.Fatal("haltChan should have been closed") 185 } 186 187 select { 188 case <-chain.errorChan: 189 logger.Debug("errorChan is closed as it should be") 190 case <-time.After(shortTimeout): 191 t.Fatal("errorChan should have been closed") 192 } 193 }) 194 195 t.Run("DoubleHalt", func(t *testing.T) { 196 _, mockBroker, mockSupport := newMocks(t) 197 defer func() { mockBroker.Close() }() 198 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 199 200 chain.Start() 201 select { 202 case <-chain.startChan: 203 logger.Debug("startChan is closed as it should be") 204 case <-time.After(shortTimeout): 205 t.Fatal("startChan should have been closed by now") 206 } 207 208 chain.Halt() 209 210 require.NotPanics(t, func() { chain.Halt() }, "Calling Halt() more than once shouldn't panic") 211 }) 212 213 t.Run("StartWithProducerForChannelError", func(t *testing.T) { 214 _, mockBroker, mockSupport := newMocks(t) 215 defer func() { mockBroker.Close() }() 216 // Point to an empty brokers list 217 mockSupportCopy := *mockSupport 218 mockSupportCopy.SharedConfigVal = newMockOrderer(longTimeout, []string{}, false) 219 220 chain, _ := newChain(mockConsenter, &mockSupportCopy, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 221 222 // The production path will actually call chain.Start(). This is 223 // functionally equivalent and allows us to run assertions on it. 224 require.Panics(t, func() { startThread(chain) }, "Expected the Start() call to panic") 225 }) 226 227 t.Run("StartWithConnectMessageError", func(t *testing.T) { 228 // Note that this test is affected by the following parameters: 229 // - Net.ReadTimeout 230 // - Consumer.Retry.Backoff 231 // - Metadata.Retry.Max 232 mockChannel, mockBroker, mockSupport := newMocks(t) 233 defer func() { mockBroker.Close() }() 234 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 235 236 // Have the broker return an ErrNotLeaderForPartition error 237 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 238 "MetadataRequest": sarama.NewMockMetadataResponse(t). 239 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 240 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 241 "ProduceRequest": sarama.NewMockProduceResponse(t). 242 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotLeaderForPartition), 243 "OffsetRequest": sarama.NewMockOffsetResponse(t). 244 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 245 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 246 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 247 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 248 }) 249 250 require.Panics(t, func() { startThread(chain) }, "Expected the Start() call to panic") 251 }) 252 253 t.Run("enqueueIfNotStarted", func(t *testing.T) { 254 mockChannel, mockBroker, mockSupport := newMocks(t) 255 defer func() { mockBroker.Close() }() 256 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 257 258 // As in StartWithConnectMessageError, have the broker return an 259 // ErrNotLeaderForPartition error, i.e. cause an error in the 260 // 'post connect message' step. 261 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 262 "MetadataRequest": sarama.NewMockMetadataResponse(t). 263 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 264 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 265 "ProduceRequest": sarama.NewMockProduceResponse(t). 266 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotLeaderForPartition), 267 "OffsetRequest": sarama.NewMockOffsetResponse(t). 268 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 269 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 270 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 271 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 272 }) 273 274 // We don't need to create a legit envelope here as it's not inspected during this test 275 require.False(t, chain.enqueue(newRegularMessage([]byte("fooMessage"))), "Expected enqueue call to return false") 276 }) 277 278 t.Run("StartWithConsumerForChannelError", func(t *testing.T) { 279 // Note that this test is affected by the following parameters: 280 // - Net.ReadTimeout 281 // - Consumer.Retry.Backoff 282 // - Metadata.Retry.Max 283 284 mockChannel, mockBroker, mockSupport := newMocks(t) 285 defer func() { mockBroker.Close() }() 286 287 // Provide an out-of-range offset 288 chain, _ := newChain(mockConsenter, mockSupport, newestOffset, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 289 290 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 291 "MetadataRequest": sarama.NewMockMetadataResponse(t). 292 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 293 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 294 "ProduceRequest": sarama.NewMockProduceResponse(t). 295 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 296 "OffsetRequest": sarama.NewMockOffsetResponse(t). 297 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 298 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 299 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 300 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 301 }) 302 303 require.Panics(t, func() { startThread(chain) }, "Expected the Start() call to panic") 304 }) 305 306 t.Run("enqueueProper", func(t *testing.T) { 307 mockChannel, mockBroker, mockSupport := newMocks(t) 308 defer func() { mockBroker.Close() }() 309 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 310 311 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 312 "MetadataRequest": sarama.NewMockMetadataResponse(t). 313 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 314 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 315 "ProduceRequest": sarama.NewMockProduceResponse(t). 316 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 317 "OffsetRequest": sarama.NewMockOffsetResponse(t). 318 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 319 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 320 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 321 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 322 }) 323 324 chain.Start() 325 select { 326 case <-chain.startChan: 327 logger.Debug("startChan is closed as it should be") 328 case <-time.After(shortTimeout): 329 t.Fatal("startChan should have been closed by now") 330 } 331 332 // enqueue should have access to the post path, and its ProduceRequest should go by without error. 333 // We don't need to create a legit envelope here as it's not inspected during this test 334 require.True(t, chain.enqueue(newRegularMessage([]byte("fooMessage"))), "Expected enqueue call to return true") 335 336 chain.Halt() 337 }) 338 339 t.Run("enqueueIfHalted", func(t *testing.T) { 340 mockChannel, mockBroker, mockSupport := newMocks(t) 341 defer func() { mockBroker.Close() }() 342 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 343 344 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 345 "MetadataRequest": sarama.NewMockMetadataResponse(t). 346 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 347 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 348 "ProduceRequest": sarama.NewMockProduceResponse(t). 349 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 350 "OffsetRequest": sarama.NewMockOffsetResponse(t). 351 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 352 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 353 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 354 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 355 }) 356 357 chain.Start() 358 select { 359 case <-chain.startChan: 360 logger.Debug("startChan is closed as it should be") 361 case <-time.After(shortTimeout): 362 t.Fatal("startChan should have been closed by now") 363 } 364 chain.Halt() 365 366 // haltChan should close access to the post path. 367 // We don't need to create a legit envelope here as it's not inspected during this test 368 require.False(t, chain.enqueue(newRegularMessage([]byte("fooMessage"))), "Expected enqueue call to return false") 369 }) 370 371 t.Run("enqueueError", func(t *testing.T) { 372 mockChannel, mockBroker, mockSupport := newMocks(t) 373 defer func() { mockBroker.Close() }() 374 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 375 376 // Use the "good" handler map that allows the Stage to complete without 377 // issues 378 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 379 "MetadataRequest": sarama.NewMockMetadataResponse(t). 380 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 381 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 382 "ProduceRequest": sarama.NewMockProduceResponse(t). 383 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 384 "OffsetRequest": sarama.NewMockOffsetResponse(t). 385 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 386 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 387 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 388 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 389 }) 390 391 chain.Start() 392 select { 393 case <-chain.startChan: 394 logger.Debug("startChan is closed as it should be") 395 case <-time.After(shortTimeout): 396 t.Fatal("startChan should have been closed by now") 397 } 398 defer chain.Halt() 399 400 // Now make it so that the next ProduceRequest is met with an error 401 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 402 "ProduceRequest": sarama.NewMockProduceResponse(t). 403 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotLeaderForPartition), 404 }) 405 406 // We don't need to create a legit envelope here as it's not inspected during this test 407 require.False(t, chain.enqueue(newRegularMessage([]byte("fooMessage"))), "Expected enqueue call to return false") 408 }) 409 410 t.Run("Order", func(t *testing.T) { 411 t.Run("ErrorIfNotStarted", func(t *testing.T) { 412 _, mockBroker, mockSupport := newMocks(t) 413 defer func() { mockBroker.Close() }() 414 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 415 416 // We don't need to create a legit envelope here as it's not inspected during this test 417 require.Error(t, chain.Order(&cb.Envelope{}, uint64(0))) 418 }) 419 420 t.Run("Proper", func(t *testing.T) { 421 mockChannel, mockBroker, mockSupport := newMocks(t) 422 defer func() { mockBroker.Close() }() 423 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 424 425 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 426 "MetadataRequest": sarama.NewMockMetadataResponse(t). 427 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 428 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 429 "ProduceRequest": sarama.NewMockProduceResponse(t). 430 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 431 "OffsetRequest": sarama.NewMockOffsetResponse(t). 432 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 433 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 434 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 435 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 436 }) 437 438 chain.Start() 439 defer chain.Halt() 440 441 select { 442 case <-chain.startChan: 443 logger.Debug("startChan is closed as it should be") 444 case <-time.After(shortTimeout): 445 t.Fatal("startChan should have been closed by now") 446 } 447 448 // We don't need to create a legit envelope here as it's not inspected during this test 449 require.NoError(t, chain.Order(&cb.Envelope{}, uint64(0)), "Expect Order successfully") 450 }) 451 }) 452 453 t.Run("Configure", func(t *testing.T) { 454 t.Run("ErrorIfNotStarted", func(t *testing.T) { 455 _, mockBroker, mockSupport := newMocks(t) 456 defer func() { mockBroker.Close() }() 457 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 458 459 // We don't need to create a legit envelope here as it's not inspected during this test 460 require.Error(t, chain.Configure(&cb.Envelope{}, uint64(0))) 461 }) 462 463 t.Run("Proper", func(t *testing.T) { 464 mockChannel, mockBroker, mockSupport := newMocks(t) 465 defer func() { mockBroker.Close() }() 466 chain, _ := newChain(mockConsenter, mockSupport, newestOffset-1, lastOriginalOffsetProcessed, lastResubmittedConfigOffset) 467 468 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 469 "MetadataRequest": sarama.NewMockMetadataResponse(t). 470 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 471 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 472 "ProduceRequest": sarama.NewMockProduceResponse(t). 473 SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError), 474 "OffsetRequest": sarama.NewMockOffsetResponse(t). 475 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 476 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 477 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 478 SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message), 479 }) 480 481 chain.Start() 482 defer chain.Halt() 483 484 select { 485 case <-chain.startChan: 486 logger.Debug("startChan is closed as it should be") 487 case <-time.After(shortTimeout): 488 t.Fatal("startChan should have been closed by now") 489 } 490 491 // We don't need to create a legit envelope here as it's not inspected during this test 492 require.NoError(t, chain.Configure(&cb.Envelope{}, uint64(0)), "Expect Configure successfully") 493 }) 494 }) 495 } 496 497 func TestSetupTopicForChannel(t *testing.T) { 498 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 499 haltChan := make(chan struct{}) 500 501 mockBrokerNoError := sarama.NewMockBroker(t, 0) 502 defer mockBrokerNoError.Close() 503 metadataResponse := sarama.NewMockMetadataResponse(t) 504 metadataResponse.SetBroker(mockBrokerNoError.Addr(), 505 mockBrokerNoError.BrokerID()) 506 metadataResponse.SetController(mockBrokerNoError.BrokerID()) 507 508 mdrUnknownTopicOrPartition := &sarama.MetadataResponse{ 509 Version: 1, 510 Brokers: []*sarama.Broker{sarama.NewBroker(mockBrokerNoError.Addr())}, 511 ControllerID: -1, 512 Topics: []*sarama.TopicMetadata{ 513 { 514 Err: sarama.ErrUnknownTopicOrPartition, 515 Name: mockChannel.topic(), 516 }, 517 }, 518 } 519 520 mockBrokerNoError.SetHandlerByMap(map[string]sarama.MockResponse{ 521 "CreateTopicsRequest": sarama.NewMockWrapper( 522 &sarama.CreateTopicsResponse{ 523 TopicErrors: map[string]*sarama.TopicError{ 524 mockChannel.topic(): { 525 Err: sarama.ErrNoError, 526 }, 527 }, 528 }), 529 "MetadataRequest": sarama.NewMockWrapper(mdrUnknownTopicOrPartition), 530 }) 531 532 mockBrokerTopicExists := sarama.NewMockBroker(t, 1) 533 defer mockBrokerTopicExists.Close() 534 mockBrokerTopicExists.SetHandlerByMap(map[string]sarama.MockResponse{ 535 "CreateTopicsRequest": sarama.NewMockWrapper( 536 &sarama.CreateTopicsResponse{ 537 TopicErrors: map[string]*sarama.TopicError{ 538 mockChannel.topic(): { 539 Err: sarama.ErrTopicAlreadyExists, 540 }, 541 }, 542 }), 543 "MetadataRequest": sarama.NewMockWrapper(&sarama.MetadataResponse{ 544 Version: 1, 545 Topics: []*sarama.TopicMetadata{ 546 { 547 Name: channelNameForTest(t), 548 Err: sarama.ErrNoError, 549 }, 550 }, 551 }), 552 }) 553 554 mockBrokerInvalidTopic := sarama.NewMockBroker(t, 2) 555 defer mockBrokerInvalidTopic.Close() 556 metadataResponse = sarama.NewMockMetadataResponse(t) 557 metadataResponse.SetBroker(mockBrokerInvalidTopic.Addr(), 558 mockBrokerInvalidTopic.BrokerID()) 559 metadataResponse.SetController(mockBrokerInvalidTopic.BrokerID()) 560 mockBrokerInvalidTopic.SetHandlerByMap(map[string]sarama.MockResponse{ 561 "CreateTopicsRequest": sarama.NewMockWrapper( 562 &sarama.CreateTopicsResponse{ 563 TopicErrors: map[string]*sarama.TopicError{ 564 mockChannel.topic(): { 565 Err: sarama.ErrInvalidTopic, 566 }, 567 }, 568 }), 569 "MetadataRequest": metadataResponse, 570 }) 571 572 mockBrokerInvalidTopic2 := sarama.NewMockBroker(t, 3) 573 defer mockBrokerInvalidTopic2.Close() 574 mockBrokerInvalidTopic2.SetHandlerByMap(map[string]sarama.MockResponse{ 575 "CreateTopicsRequest": sarama.NewMockWrapper( 576 &sarama.CreateTopicsResponse{ 577 TopicErrors: map[string]*sarama.TopicError{ 578 mockChannel.topic(): { 579 Err: sarama.ErrInvalidTopic, 580 }, 581 }, 582 }), 583 "MetadataRequest": sarama.NewMockWrapper(&sarama.MetadataResponse{ 584 Version: 1, 585 Brokers: []*sarama.Broker{sarama.NewBroker(mockBrokerInvalidTopic2.Addr())}, 586 ControllerID: mockBrokerInvalidTopic2.BrokerID(), 587 }), 588 }) 589 590 closedBroker := sarama.NewMockBroker(t, 99) 591 badAddress := closedBroker.Addr() 592 closedBroker.Close() 593 594 tests := []struct { 595 name string 596 brokers []string 597 brokerConfig *sarama.Config 598 version sarama.KafkaVersion 599 expectErr bool 600 errorMsg string 601 }{ 602 { 603 name: "Unsupported Version", 604 brokers: []string{mockBrokerNoError.Addr()}, 605 brokerConfig: sarama.NewConfig(), 606 version: sarama.V0_9_0_0, 607 expectErr: false, 608 }, 609 { 610 name: "No Error", 611 brokers: []string{mockBrokerNoError.Addr()}, 612 brokerConfig: sarama.NewConfig(), 613 version: sarama.V0_10_2_0, 614 expectErr: false, 615 }, 616 { 617 name: "Topic Exists", 618 brokers: []string{mockBrokerTopicExists.Addr()}, 619 brokerConfig: sarama.NewConfig(), 620 version: sarama.V0_10_2_0, 621 expectErr: false, 622 }, 623 { 624 name: "Invalid Topic", 625 brokers: []string{mockBrokerInvalidTopic.Addr()}, 626 brokerConfig: sarama.NewConfig(), 627 version: sarama.V0_10_2_0, 628 expectErr: true, 629 errorMsg: "process asked to exit", 630 }, 631 { 632 name: "Multiple Brokers - One No Error", 633 brokers: []string{badAddress, mockBrokerNoError.Addr()}, 634 brokerConfig: sarama.NewConfig(), 635 version: sarama.V0_10_2_0, 636 expectErr: false, 637 }, 638 { 639 name: "Multiple Brokers - All Errors", 640 brokers: []string{badAddress, badAddress}, 641 brokerConfig: sarama.NewConfig(), 642 version: sarama.V0_10_2_0, 643 expectErr: true, 644 errorMsg: "failed to retrieve metadata", 645 }, 646 } 647 648 for _, test := range tests { 649 test := test 650 t.Run(test.name, func(t *testing.T) { 651 test.brokerConfig.Version = test.version 652 err := setupTopicForChannel( 653 mockRetryOptions, 654 haltChan, 655 test.brokers, 656 test.brokerConfig, 657 &sarama.TopicDetail{ 658 NumPartitions: 1, 659 ReplicationFactor: 2, 660 }, 661 mockChannel) 662 if test.expectErr { 663 require.Contains(t, err.Error(), test.errorMsg) 664 } else { 665 require.NoError(t, err) 666 } 667 }) 668 } 669 } 670 671 func TestSetupProducerForChannel(t *testing.T) { 672 if testing.Short() { 673 t.Skip("Skipping test in short mode") 674 } 675 676 mockBroker := sarama.NewMockBroker(t, 0) 677 defer mockBroker.Close() 678 679 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 680 681 haltChan := make(chan struct{}) 682 683 t.Run("Proper", func(t *testing.T) { 684 metadataResponse := new(sarama.MetadataResponse) 685 metadataResponse.AddBroker(mockBroker.Addr(), mockBroker.BrokerID()) 686 metadataResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID(), nil, nil, sarama.ErrNoError) 687 mockBroker.Returns(metadataResponse) 688 689 producer, err := setupProducerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 690 require.NoError(t, err, "Expected the setupProducerForChannel call to return without errors") 691 require.NoError(t, producer.Close(), "Expected to close the producer without errors") 692 }) 693 694 t.Run("WithError", func(t *testing.T) { 695 _, err := setupProducerForChannel(mockConsenter.retryOptions(), haltChan, []string{}, mockBrokerConfig, mockChannel) 696 require.Error(t, err, "Expected the setupProducerForChannel call to return an error") 697 }) 698 } 699 700 func TestGetHealthyClusterReplicaInfo(t *testing.T) { 701 mockBroker := sarama.NewMockBroker(t, 0) 702 defer mockBroker.Close() 703 704 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 705 706 haltChan := make(chan struct{}) 707 708 t.Run("Proper", func(t *testing.T) { 709 ids := []int32{int32(1), int32(2)} 710 metadataResponse := new(sarama.MetadataResponse) 711 metadataResponse.AddBroker(mockBroker.Addr(), mockBroker.BrokerID()) 712 metadataResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID(), ids, nil, sarama.ErrNoError) 713 mockBroker.Returns(metadataResponse) 714 715 replicaIDs, err := getHealthyClusterReplicaInfo(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 716 require.NoError(t, err, "Expected the getHealthyClusterReplicaInfo call to return without errors") 717 require.Equal(t, replicaIDs, ids) 718 }) 719 720 t.Run("WithError", func(t *testing.T) { 721 _, err := getHealthyClusterReplicaInfo(mockConsenter.retryOptions(), haltChan, []string{}, mockBrokerConfig, mockChannel) 722 require.Error(t, err, "Expected the getHealthyClusterReplicaInfo call to return an error") 723 }) 724 } 725 726 func TestSetupConsumerForChannel(t *testing.T) { 727 mockBroker := sarama.NewMockBroker(t, 0) 728 defer func() { mockBroker.Close() }() 729 730 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 731 732 oldestOffset := int64(0) 733 newestOffset := int64(5) 734 735 startFrom := int64(3) 736 message := sarama.StringEncoder("messageFoo") 737 738 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 739 "MetadataRequest": sarama.NewMockMetadataResponse(t). 740 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 741 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 742 "OffsetRequest": sarama.NewMockOffsetResponse(t). 743 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 744 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 745 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 746 SetMessage(mockChannel.topic(), mockChannel.partition(), startFrom, message), 747 }) 748 749 haltChan := make(chan struct{}) 750 751 t.Run("ProperParent", func(t *testing.T) { 752 parentConsumer, err := setupParentConsumerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 753 require.NoError(t, err, "Expected the setupParentConsumerForChannel call to return without errors") 754 require.NoError(t, parentConsumer.Close(), "Expected to close the parentConsumer without errors") 755 }) 756 757 t.Run("ProperChannel", func(t *testing.T) { 758 parentConsumer, _ := setupParentConsumerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 759 defer func() { parentConsumer.Close() }() 760 channelConsumer, err := setupChannelConsumerForChannel(mockConsenter.retryOptions(), haltChan, parentConsumer, mockChannel, newestOffset) 761 require.NoError(t, err, "Expected the setupChannelConsumerForChannel call to return without errors") 762 require.NoError(t, channelConsumer.Close(), "Expected to close the channelConsumer without errors") 763 }) 764 765 t.Run("WithParentConsumerError", func(t *testing.T) { 766 // Provide an empty brokers list 767 _, err := setupParentConsumerForChannel(mockConsenter.retryOptions(), haltChan, []string{}, mockBrokerConfig, mockChannel) 768 require.Error(t, err, "Expected the setupParentConsumerForChannel call to return an error") 769 }) 770 771 t.Run("WithChannelConsumerError", func(t *testing.T) { 772 // Provide an out-of-range offset 773 parentConsumer, _ := setupParentConsumerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 774 _, err := setupChannelConsumerForChannel(mockConsenter.retryOptions(), haltChan, parentConsumer, mockChannel, newestOffset+1) 775 defer func() { parentConsumer.Close() }() 776 require.Error(t, err, "Expected the setupChannelConsumerForChannel call to return an error") 777 }) 778 } 779 780 func TestCloseKafkaObjects(t *testing.T) { 781 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 782 783 mockSupport := &mockmultichannel.ConsenterSupport{ 784 ChannelIDVal: mockChannel.topic(), 785 } 786 787 oldestOffset := int64(0) 788 newestOffset := int64(5) 789 790 startFrom := int64(3) 791 message := sarama.StringEncoder("messageFoo") 792 793 mockBroker := sarama.NewMockBroker(t, 0) 794 defer func() { mockBroker.Close() }() 795 796 mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{ 797 "MetadataRequest": sarama.NewMockMetadataResponse(t). 798 SetBroker(mockBroker.Addr(), mockBroker.BrokerID()). 799 SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()), 800 "OffsetRequest": sarama.NewMockOffsetResponse(t). 801 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset). 802 SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset), 803 "FetchRequest": sarama.NewMockFetchResponse(t, 1). 804 SetMessage(mockChannel.topic(), mockChannel.partition(), startFrom, message), 805 }) 806 807 haltChan := make(chan struct{}) 808 809 t.Run("Proper", func(t *testing.T) { 810 producer, _ := setupProducerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 811 parentConsumer, _ := setupParentConsumerForChannel(mockConsenter.retryOptions(), haltChan, []string{mockBroker.Addr()}, mockBrokerConfig, mockChannel) 812 channelConsumer, _ := setupChannelConsumerForChannel(mockConsenter.retryOptions(), haltChan, parentConsumer, mockChannel, startFrom) 813 814 // Set up a chain with just the minimum necessary fields instantiated so 815 // as to test the function 816 bareMinimumChain := &chainImpl{ 817 ConsenterSupport: mockSupport, 818 producer: producer, 819 parentConsumer: parentConsumer, 820 channelConsumer: channelConsumer, 821 } 822 823 errs := bareMinimumChain.closeKafkaObjects() 824 825 require.Len(t, errs, 0, "Expected zero errors") 826 827 require.NotPanics(t, func() { 828 channelConsumer.Close() 829 }) 830 831 require.NotPanics(t, func() { 832 parentConsumer.Close() 833 }) 834 835 // TODO For some reason this panic cannot be captured by the `assert` 836 // test framework. Not a dealbreaker but need to investigate further. 837 /* assert.Panics(t, func() { 838 producer.Close() 839 }) */ 840 }) 841 842 t.Run("ChannelConsumerError", func(t *testing.T) { 843 producer, _ := sarama.NewSyncProducer([]string{mockBroker.Addr()}, mockBrokerConfig) 844 845 // Unlike all other tests in this file, forcing an error on the 846 // channelConsumer.Close() call is more easily achieved using the mock 847 // Consumer. Thus we bypass the call to `setup*Consumer`. 848 849 // Have the consumer receive an ErrOutOfBrokers error. 850 mockParentConsumer := mocks.NewConsumer(t, nil) 851 mockParentConsumer.ExpectConsumePartition(mockChannel.topic(), mockChannel.partition(), startFrom).YieldError(sarama.ErrOutOfBrokers) 852 mockChannelConsumer, err := mockParentConsumer.ConsumePartition(mockChannel.topic(), mockChannel.partition(), startFrom) 853 require.NoError(t, err, "Expected no error when setting up the mock partition consumer") 854 855 bareMinimumChain := &chainImpl{ 856 ConsenterSupport: mockSupport, 857 producer: producer, 858 parentConsumer: mockParentConsumer, 859 channelConsumer: mockChannelConsumer, 860 } 861 862 errs := bareMinimumChain.closeKafkaObjects() 863 864 require.Len(t, errs, 1, "Expected 1 error returned") 865 866 require.NotPanics(t, func() { 867 mockChannelConsumer.Close() 868 }) 869 870 require.NotPanics(t, func() { 871 mockParentConsumer.Close() 872 }) 873 }) 874 } 875 876 func TestGetLastCutBlockNumber(t *testing.T) { 877 testCases := []struct { 878 name string 879 input uint64 880 expected uint64 881 }{ 882 {"Proper", uint64(2), uint64(1)}, 883 {"Zero", uint64(1), uint64(0)}, 884 } 885 for _, tc := range testCases { 886 t.Run(tc.name, func(t *testing.T) { 887 require.Equal(t, tc.expected, getLastCutBlockNumber(tc.input)) 888 }) 889 } 890 } 891 892 func TestGetLastOffsetPersisted(t *testing.T) { 893 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 894 mockMetadata := &cb.Metadata{Value: protoutil.MarshalOrPanic(&ab.KafkaMetadata{ 895 LastOffsetPersisted: int64(5), 896 LastOriginalOffsetProcessed: int64(3), 897 LastResubmittedConfigOffset: int64(4), 898 })} 899 900 testCases := []struct { 901 name string 902 md []byte 903 expectedPersisted int64 904 expectedProcessed int64 905 expectedResubmitted int64 906 panics bool 907 }{ 908 {"Proper", mockMetadata.Value, int64(5), int64(3), int64(4), false}, 909 {"Empty", nil, sarama.OffsetOldest - 1, int64(0), int64(0), false}, 910 {"Panics", tamperBytes(mockMetadata.Value), sarama.OffsetOldest - 1, int64(0), int64(0), true}, 911 } 912 913 for _, tc := range testCases { 914 t.Run(tc.name, func(t *testing.T) { 915 if !tc.panics { 916 persisted, processed, resubmitted := getOffsets(tc.md, mockChannel.String()) 917 require.Equal(t, tc.expectedPersisted, persisted) 918 require.Equal(t, tc.expectedProcessed, processed) 919 require.Equal(t, tc.expectedResubmitted, resubmitted) 920 } else { 921 require.Panics(t, func() { 922 getOffsets(tc.md, mockChannel.String()) 923 }, "Expected getOffsets call to panic") 924 } 925 }) 926 } 927 } 928 929 func TestSendConnectMessage(t *testing.T) { 930 mockBroker := sarama.NewMockBroker(t, 0) 931 defer func() { mockBroker.Close() }() 932 933 mockChannel := newChannel("mockChannelFoo", defaultPartition) 934 935 metadataResponse := new(sarama.MetadataResponse) 936 metadataResponse.AddBroker(mockBroker.Addr(), mockBroker.BrokerID()) 937 metadataResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID(), nil, nil, sarama.ErrNoError) 938 mockBroker.Returns(metadataResponse) 939 940 producer, _ := sarama.NewSyncProducer([]string{mockBroker.Addr()}, mockBrokerConfig) 941 defer func() { producer.Close() }() 942 943 haltChan := make(chan struct{}) 944 945 t.Run("Proper", func(t *testing.T) { 946 successResponse := new(sarama.ProduceResponse) 947 successResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError) 948 mockBroker.Returns(successResponse) 949 950 require.NoError(t, sendConnectMessage(mockConsenter.retryOptions(), haltChan, producer, mockChannel), "Expected the sendConnectMessage call to return without errors") 951 }) 952 953 t.Run("WithError", func(t *testing.T) { 954 // Note that this test is affected by the following parameters: 955 // - Net.ReadTimeout 956 // - Consumer.Retry.Backoff 957 // - Metadata.Retry.Max 958 959 // Have the broker return an ErrNotEnoughReplicas error 960 failureResponse := new(sarama.ProduceResponse) 961 failureResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotEnoughReplicas) 962 mockBroker.Returns(failureResponse) 963 964 require.Error(t, sendConnectMessage(mockConsenter.retryOptions(), haltChan, producer, mockChannel), "Expected the sendConnectMessage call to return an error") 965 }) 966 } 967 968 func TestSendTimeToCut(t *testing.T) { 969 mockBroker := sarama.NewMockBroker(t, 0) 970 defer func() { mockBroker.Close() }() 971 972 mockChannel := newChannel("mockChannelFoo", defaultPartition) 973 974 metadataResponse := new(sarama.MetadataResponse) 975 metadataResponse.AddBroker(mockBroker.Addr(), mockBroker.BrokerID()) 976 metadataResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID(), nil, nil, sarama.ErrNoError) 977 mockBroker.Returns(metadataResponse) 978 979 producer, err := sarama.NewSyncProducer([]string{mockBroker.Addr()}, mockBrokerConfig) 980 require.NoError(t, err, "Expected no error when setting up the sarama SyncProducer") 981 defer func() { producer.Close() }() 982 983 timeToCutBlockNumber := uint64(3) 984 var timer <-chan time.Time 985 986 t.Run("Proper", func(t *testing.T) { 987 successResponse := new(sarama.ProduceResponse) 988 successResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError) 989 mockBroker.Returns(successResponse) 990 991 timer = time.After(longTimeout) 992 993 require.NoError(t, sendTimeToCut(producer, mockChannel, timeToCutBlockNumber, &timer), "Expected the sendTimeToCut call to return without errors") 994 require.Nil(t, timer, "Expected the sendTimeToCut call to nil the timer") 995 }) 996 997 t.Run("WithError", func(t *testing.T) { 998 // Note that this test is affected by the following parameters: 999 // - Net.ReadTimeout 1000 // - Consumer.Retry.Backoff 1001 // - Metadata.Retry.Max 1002 failureResponse := new(sarama.ProduceResponse) 1003 failureResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotEnoughReplicas) 1004 mockBroker.Returns(failureResponse) 1005 1006 timer = time.After(longTimeout) 1007 1008 require.Error(t, sendTimeToCut(producer, mockChannel, timeToCutBlockNumber, &timer), "Expected the sendTimeToCut call to return an error") 1009 require.Nil(t, timer, "Expected the sendTimeToCut call to nil the timer") 1010 }) 1011 } 1012 1013 func TestProcessMessagesToBlocks(t *testing.T) { 1014 mockBroker := sarama.NewMockBroker(t, 0) 1015 defer func() { mockBroker.Close() }() 1016 1017 mockChannel := newChannel("mockChannelFoo", defaultPartition) 1018 1019 metadataResponse := new(sarama.MetadataResponse) 1020 metadataResponse.AddBroker(mockBroker.Addr(), mockBroker.BrokerID()) 1021 metadataResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID(), nil, nil, sarama.ErrNoError) 1022 mockBroker.Returns(metadataResponse) 1023 1024 producer, _ := sarama.NewSyncProducer([]string{mockBroker.Addr()}, mockBrokerConfig) 1025 1026 mockBrokerConfigCopy := *mockBrokerConfig 1027 mockBrokerConfigCopy.ChannelBufferSize = 0 1028 1029 mockParentConsumer := mocks.NewConsumer(t, &mockBrokerConfigCopy) 1030 mpc := mockParentConsumer.ExpectConsumePartition(mockChannel.topic(), mockChannel.partition(), int64(0)) 1031 mockChannelConsumer, err := mockParentConsumer.ConsumePartition(mockChannel.topic(), mockChannel.partition(), int64(0)) 1032 require.NoError(t, err, "Expected no error when setting up the mock partition consumer") 1033 1034 t.Run("TimeToCut", func(t *testing.T) { 1035 t.Run("PendingMsgToCutProper", func(t *testing.T) { 1036 errorChan := make(chan struct{}) 1037 close(errorChan) 1038 haltChan := make(chan struct{}) 1039 1040 lastCutBlockNumber := uint64(3) 1041 1042 mockSupport := &mockmultichannel.ConsenterSupport{ 1043 Blocks: make(chan *cb.Block), // WriteBlock will post here 1044 BlockCutterVal: mockblockcutter.NewReceiver(), 1045 ChannelIDVal: mockChannel.topic(), 1046 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1047 SharedConfigVal: newMockOrderer(shortTimeout/2, []string{mockBroker.Addr()}, false), 1048 } 1049 defer close(mockSupport.BlockCutterVal.Block) 1050 1051 bareMinimumChain := &chainImpl{ 1052 producer: producer, 1053 parentConsumer: mockParentConsumer, 1054 channelConsumer: mockChannelConsumer, 1055 1056 consenter: mockConsenter, 1057 channel: mockChannel, 1058 ConsenterSupport: mockSupport, 1059 lastCutBlockNumber: lastCutBlockNumber, 1060 1061 errorChan: errorChan, 1062 haltChan: haltChan, 1063 doneProcessingMessagesToBlocks: make(chan struct{}), 1064 } 1065 1066 // We need the mock blockcutter to deliver a non-empty batch 1067 go func() { 1068 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call below return 1069 logger.Debugf("Mock blockcutter's Ordered call has returned") 1070 }() 1071 // We are "planting" a message directly to the mock blockcutter 1072 mockSupport.BlockCutterVal.Ordered(newMockEnvelope("fooMessage")) 1073 1074 done := make(chan struct{}) 1075 1076 go func() { 1077 bareMinimumChain.processMessagesToBlocks() 1078 done <- struct{}{} 1079 }() 1080 1081 // Cut ancestors 1082 mockSupport.BlockCutterVal.CutAncestors = true 1083 1084 // This envelope will be added into pending list, waiting to be cut when timer fires 1085 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1086 1087 go func() { 1088 mockSupport.BlockCutterVal.Block <- struct{}{} 1089 logger.Debugf("Mock blockcutter's Ordered call has returned") 1090 }() 1091 1092 <-mockSupport.Blocks // Wait for the first block 1093 1094 logger.Debug("Closing haltChan to exit the infinite for-loop") 1095 close(haltChan) // Identical to chain.Halt() 1096 logger.Debug("haltChan closed") 1097 <-done 1098 1099 if bareMinimumChain.timer != nil { 1100 go func() { 1101 <-bareMinimumChain.timer // Fire the timer for garbage collection 1102 }() 1103 } 1104 1105 require.NotEmpty(t, mockSupport.BlockCutterVal.CurBatch, "Expected the blockCutter to be non-empty") 1106 require.NotNil(t, bareMinimumChain.timer, "Expected the cutTimer to be non-nil when there are pending envelopes") 1107 }) 1108 1109 t.Run("ReceiveTimeToCutProper", func(t *testing.T) { 1110 errorChan := make(chan struct{}) 1111 close(errorChan) 1112 haltChan := make(chan struct{}) 1113 1114 lastCutBlockNumber := uint64(3) 1115 1116 mockSupport := &mockmultichannel.ConsenterSupport{ 1117 Blocks: make(chan *cb.Block), // WriteBlock will post here 1118 BlockCutterVal: mockblockcutter.NewReceiver(), 1119 ChannelIDVal: mockChannel.topic(), 1120 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1121 } 1122 defer close(mockSupport.BlockCutterVal.Block) 1123 1124 bareMinimumChain := &chainImpl{ 1125 parentConsumer: mockParentConsumer, 1126 channelConsumer: mockChannelConsumer, 1127 1128 consenter: mockConsenter, 1129 channel: mockChannel, 1130 ConsenterSupport: mockSupport, 1131 lastCutBlockNumber: lastCutBlockNumber, 1132 1133 errorChan: errorChan, 1134 haltChan: haltChan, 1135 doneProcessingMessagesToBlocks: make(chan struct{}), 1136 } 1137 1138 // We need the mock blockcutter to deliver a non-empty batch 1139 go func() { 1140 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call below return 1141 logger.Debugf("Mock blockcutter's Ordered call has returned") 1142 }() 1143 // We are "planting" a message directly to the mock blockcutter 1144 mockSupport.BlockCutterVal.Ordered(newMockEnvelope("fooMessage")) 1145 1146 var counts []uint64 1147 done := make(chan struct{}) 1148 1149 go func() { 1150 counts, err = bareMinimumChain.processMessagesToBlocks() 1151 done <- struct{}{} 1152 }() 1153 1154 // This is the wrappedMessage that the for-loop will process 1155 mpc.YieldMessage(newMockConsumerMessage(newTimeToCutMessage(lastCutBlockNumber + 1))) 1156 1157 <-mockSupport.Blocks // Let the `mockConsenterSupport.WriteBlock` proceed 1158 1159 logger.Debug("Closing haltChan to exit the infinite for-loop") 1160 close(haltChan) // Identical to chain.Halt() 1161 logger.Debug("haltChan closed") 1162 <-done 1163 1164 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1165 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1166 require.Equal(t, uint64(1), counts[indexProcessTimeToCutPass], "Expected 1 TIMETOCUT message processed") 1167 require.Equal(t, lastCutBlockNumber+1, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be bumped up by one") 1168 }) 1169 1170 t.Run("ReceiveTimeToCutZeroBatch", func(t *testing.T) { 1171 errorChan := make(chan struct{}) 1172 close(errorChan) 1173 haltChan := make(chan struct{}) 1174 1175 lastCutBlockNumber := uint64(3) 1176 1177 mockSupport := &mockmultichannel.ConsenterSupport{ 1178 Blocks: make(chan *cb.Block), // WriteBlock will post here 1179 BlockCutterVal: mockblockcutter.NewReceiver(), 1180 ChannelIDVal: mockChannel.topic(), 1181 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1182 } 1183 defer close(mockSupport.BlockCutterVal.Block) 1184 1185 bareMinimumChain := &chainImpl{ 1186 parentConsumer: mockParentConsumer, 1187 channelConsumer: mockChannelConsumer, 1188 1189 channel: mockChannel, 1190 ConsenterSupport: mockSupport, 1191 lastCutBlockNumber: lastCutBlockNumber, 1192 1193 errorChan: errorChan, 1194 haltChan: haltChan, 1195 doneProcessingMessagesToBlocks: make(chan struct{}), 1196 } 1197 1198 var counts []uint64 1199 done := make(chan struct{}) 1200 1201 go func() { 1202 counts, err = bareMinimumChain.processMessagesToBlocks() 1203 done <- struct{}{} 1204 }() 1205 1206 // This is the wrappedMessage that the for-loop will process 1207 mpc.YieldMessage(newMockConsumerMessage(newTimeToCutMessage(lastCutBlockNumber + 1))) 1208 1209 logger.Debug("Closing haltChan to exit the infinite for-loop") 1210 close(haltChan) // Identical to chain.Halt() 1211 logger.Debug("haltChan closed") 1212 <-done 1213 1214 require.Error(t, err, "Expected the processMessagesToBlocks call to return an error") 1215 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1216 require.Equal(t, uint64(1), counts[indexProcessTimeToCutError], "Expected 1 faulty TIMETOCUT message processed") 1217 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to stay the same") 1218 }) 1219 1220 t.Run("ReceiveTimeToCutLargerThanExpected", func(t *testing.T) { 1221 errorChan := make(chan struct{}) 1222 close(errorChan) 1223 haltChan := make(chan struct{}) 1224 1225 lastCutBlockNumber := uint64(3) 1226 1227 mockSupport := &mockmultichannel.ConsenterSupport{ 1228 Blocks: make(chan *cb.Block), // WriteBlock will post here 1229 BlockCutterVal: mockblockcutter.NewReceiver(), 1230 ChannelIDVal: mockChannel.topic(), 1231 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1232 } 1233 defer close(mockSupport.BlockCutterVal.Block) 1234 1235 bareMinimumChain := &chainImpl{ 1236 parentConsumer: mockParentConsumer, 1237 channelConsumer: mockChannelConsumer, 1238 1239 channel: mockChannel, 1240 ConsenterSupport: mockSupport, 1241 lastCutBlockNumber: lastCutBlockNumber, 1242 1243 errorChan: errorChan, 1244 haltChan: haltChan, 1245 doneProcessingMessagesToBlocks: make(chan struct{}), 1246 } 1247 1248 var counts []uint64 1249 done := make(chan struct{}) 1250 1251 go func() { 1252 counts, err = bareMinimumChain.processMessagesToBlocks() 1253 done <- struct{}{} 1254 }() 1255 1256 // This is the wrappedMessage that the for-loop will process 1257 mpc.YieldMessage(newMockConsumerMessage(newTimeToCutMessage(lastCutBlockNumber + 2))) 1258 1259 logger.Debug("Closing haltChan to exit the infinite for-loop") 1260 close(haltChan) // Identical to chain.Halt() 1261 logger.Debug("haltChan closed") 1262 <-done 1263 1264 require.Error(t, err, "Expected the processMessagesToBlocks call to return an error") 1265 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1266 require.Equal(t, uint64(1), counts[indexProcessTimeToCutError], "Expected 1 faulty TIMETOCUT message processed") 1267 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to stay the same") 1268 }) 1269 1270 t.Run("ReceiveTimeToCutStale", func(t *testing.T) { 1271 errorChan := make(chan struct{}) 1272 close(errorChan) 1273 haltChan := make(chan struct{}) 1274 1275 lastCutBlockNumber := uint64(3) 1276 1277 mockSupport := &mockmultichannel.ConsenterSupport{ 1278 Blocks: make(chan *cb.Block), // WriteBlock will post here 1279 BlockCutterVal: mockblockcutter.NewReceiver(), 1280 ChannelIDVal: mockChannel.topic(), 1281 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1282 } 1283 defer close(mockSupport.BlockCutterVal.Block) 1284 1285 bareMinimumChain := &chainImpl{ 1286 parentConsumer: mockParentConsumer, 1287 channelConsumer: mockChannelConsumer, 1288 1289 channel: mockChannel, 1290 ConsenterSupport: mockSupport, 1291 lastCutBlockNumber: lastCutBlockNumber, 1292 1293 errorChan: errorChan, 1294 haltChan: haltChan, 1295 doneProcessingMessagesToBlocks: make(chan struct{}), 1296 } 1297 1298 var counts []uint64 1299 done := make(chan struct{}) 1300 1301 go func() { 1302 counts, err = bareMinimumChain.processMessagesToBlocks() 1303 done <- struct{}{} 1304 }() 1305 1306 // This is the wrappedMessage that the for-loop will process 1307 mpc.YieldMessage(newMockConsumerMessage(newTimeToCutMessage(lastCutBlockNumber))) 1308 1309 logger.Debug("Closing haltChan to exit the infinite for-loop") 1310 close(haltChan) // Identical to chain.Halt() 1311 logger.Debug("haltChan closed") 1312 <-done 1313 1314 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1315 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1316 require.Equal(t, uint64(1), counts[indexProcessTimeToCutPass], "Expected 1 TIMETOCUT message processed") 1317 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to stay the same") 1318 }) 1319 }) 1320 1321 t.Run("Connect", func(t *testing.T) { 1322 t.Run("ReceiveConnect", func(t *testing.T) { 1323 errorChan := make(chan struct{}) 1324 close(errorChan) 1325 haltChan := make(chan struct{}) 1326 1327 mockSupport := &mockmultichannel.ConsenterSupport{ 1328 ChannelIDVal: mockChannel.topic(), 1329 } 1330 1331 bareMinimumChain := &chainImpl{ 1332 parentConsumer: mockParentConsumer, 1333 channelConsumer: mockChannelConsumer, 1334 1335 channel: mockChannel, 1336 ConsenterSupport: mockSupport, 1337 1338 errorChan: errorChan, 1339 haltChan: haltChan, 1340 doneProcessingMessagesToBlocks: make(chan struct{}), 1341 } 1342 1343 var counts []uint64 1344 done := make(chan struct{}) 1345 1346 go func() { 1347 counts, err = bareMinimumChain.processMessagesToBlocks() 1348 done <- struct{}{} 1349 }() 1350 1351 // This is the wrappedMessage that the for-loop will process 1352 mpc.YieldMessage(newMockConsumerMessage(newConnectMessage())) 1353 1354 logger.Debug("Closing haltChan to exit the infinite for-loop") 1355 close(haltChan) // Identical to chain.Halt() 1356 logger.Debug("haltChan closed") 1357 <-done 1358 1359 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1360 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1361 require.Equal(t, uint64(1), counts[indexProcessConnectPass], "Expected 1 CONNECT message processed") 1362 }) 1363 }) 1364 1365 t.Run("Regular", func(t *testing.T) { 1366 t.Run("Error", func(t *testing.T) { 1367 errorChan := make(chan struct{}) 1368 close(errorChan) 1369 haltChan := make(chan struct{}) 1370 1371 mockSupport := &mockmultichannel.ConsenterSupport{ 1372 ChannelIDVal: mockChannel.topic(), 1373 } 1374 1375 bareMinimumChain := &chainImpl{ 1376 parentConsumer: mockParentConsumer, 1377 channelConsumer: mockChannelConsumer, 1378 1379 channel: mockChannel, 1380 ConsenterSupport: mockSupport, 1381 1382 errorChan: errorChan, 1383 haltChan: haltChan, 1384 doneProcessingMessagesToBlocks: make(chan struct{}), 1385 } 1386 1387 var counts []uint64 1388 done := make(chan struct{}) 1389 1390 go func() { 1391 counts, err = bareMinimumChain.processMessagesToBlocks() 1392 done <- struct{}{} 1393 }() 1394 1395 // This is the wrappedMessage that the for-loop will process 1396 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(tamperBytes(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")))))) 1397 1398 logger.Debug("Closing haltChan to exit the infinite for-loop") 1399 close(haltChan) // Identical to chain.Halt() 1400 logger.Debug("haltChan closed") 1401 <-done 1402 1403 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1404 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1405 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 damaged REGULAR message processed") 1406 }) 1407 1408 // This ensures regular kafka messages of type UNKNOWN are handled properly 1409 t.Run("Unknown", func(t *testing.T) { 1410 t.Run("Enqueue", func(t *testing.T) { 1411 errorChan := make(chan struct{}) 1412 close(errorChan) 1413 haltChan := make(chan struct{}) 1414 1415 lastCutBlockNumber := uint64(3) 1416 1417 mockSupport := &mockmultichannel.ConsenterSupport{ 1418 Blocks: make(chan *cb.Block), // WriteBlock will post here 1419 BlockCutterVal: mockblockcutter.NewReceiver(), 1420 ChannelIDVal: mockChannel.topic(), 1421 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1422 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1423 } 1424 defer close(mockSupport.BlockCutterVal.Block) 1425 1426 bareMinimumChain := &chainImpl{ 1427 parentConsumer: mockParentConsumer, 1428 channelConsumer: mockChannelConsumer, 1429 1430 channel: mockChannel, 1431 ConsenterSupport: mockSupport, 1432 lastCutBlockNumber: lastCutBlockNumber, 1433 1434 errorChan: errorChan, 1435 haltChan: haltChan, 1436 doneProcessingMessagesToBlocks: make(chan struct{}), 1437 } 1438 1439 var counts []uint64 1440 done := make(chan struct{}) 1441 1442 go func() { 1443 counts, err = bareMinimumChain.processMessagesToBlocks() 1444 done <- struct{}{} 1445 }() 1446 1447 // This is the wrappedMessage that the for-loop will process 1448 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1449 1450 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 1451 logger.Debugf("Mock blockcutter's Ordered call has returned") 1452 1453 logger.Debug("Closing haltChan to exit the infinite for-loop") 1454 // We are guaranteed to hit the haltChan branch after hitting the REGULAR branch at least once 1455 close(haltChan) // Identical to chain.Halt() 1456 logger.Debug("haltChan closed") 1457 <-done 1458 1459 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1460 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1461 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 1462 }) 1463 1464 t.Run("CutBlock", func(t *testing.T) { 1465 errorChan := make(chan struct{}) 1466 close(errorChan) 1467 haltChan := make(chan struct{}) 1468 1469 lastCutBlockNumber := uint64(3) 1470 1471 mockSupport := &mockmultichannel.ConsenterSupport{ 1472 Blocks: make(chan *cb.Block), // WriteBlock will post here 1473 BlockCutterVal: mockblockcutter.NewReceiver(), 1474 ChannelIDVal: mockChannel.topic(), 1475 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1476 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1477 } 1478 defer close(mockSupport.BlockCutterVal.Block) 1479 1480 fakeLastOffsetPersisted := &mockkafka.MetricsGauge{} 1481 fakeLastOffsetPersisted.WithReturns(fakeLastOffsetPersisted) 1482 mockConsenter.(*consenterImpl).metrics.LastOffsetPersisted = fakeLastOffsetPersisted 1483 1484 bareMinimumChain := &chainImpl{ 1485 parentConsumer: mockParentConsumer, 1486 channelConsumer: mockChannelConsumer, 1487 1488 consenter: mockConsenter, 1489 channel: mockChannel, 1490 ConsenterSupport: mockSupport, 1491 lastCutBlockNumber: lastCutBlockNumber, 1492 1493 errorChan: errorChan, 1494 haltChan: haltChan, 1495 doneProcessingMessagesToBlocks: make(chan struct{}), 1496 } 1497 1498 var counts []uint64 1499 done := make(chan struct{}) 1500 1501 go func() { 1502 counts, err = bareMinimumChain.processMessagesToBlocks() 1503 done <- struct{}{} 1504 }() 1505 1506 mockSupport.BlockCutterVal.CutNext = true 1507 1508 // This is the wrappedMessage that the for-loop will process 1509 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1510 1511 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 1512 logger.Debugf("Mock blockcutter's Ordered call has returned") 1513 <-mockSupport.Blocks // Let the `mockConsenterSupport.WriteBlock` proceed 1514 1515 logger.Debug("Closing haltChan to exit the infinite for-loop") 1516 close(haltChan) // Identical to chain.Halt() 1517 logger.Debug("haltChan closed") 1518 <-done 1519 1520 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1521 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1522 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 1523 require.Equal(t, lastCutBlockNumber+1, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be bumped up by one") 1524 1525 require.Equal(t, fakeLastOffsetPersisted.WithCallCount(), 1) 1526 require.Equal(t, fakeLastOffsetPersisted.WithArgsForCall(0), []string{"channel", "mockChannelFoo"}) 1527 require.Equal(t, fakeLastOffsetPersisted.SetCallCount(), 1) 1528 require.Equal(t, fakeLastOffsetPersisted.SetArgsForCall(0), float64(9)) 1529 }) 1530 1531 // This test ensures the corner case in FAB-5709 is taken care of 1532 t.Run("SecondTxOverflows", func(t *testing.T) { 1533 if testing.Short() { 1534 t.Skip("Skipping test in short mode") 1535 } 1536 1537 errorChan := make(chan struct{}) 1538 close(errorChan) 1539 haltChan := make(chan struct{}) 1540 1541 lastCutBlockNumber := uint64(3) 1542 1543 mockSupport := &mockmultichannel.ConsenterSupport{ 1544 Blocks: make(chan *cb.Block), // WriteBlock will post here 1545 BlockCutterVal: mockblockcutter.NewReceiver(), 1546 ChannelIDVal: mockChannel.topic(), 1547 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1548 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1549 } 1550 defer close(mockSupport.BlockCutterVal.Block) 1551 1552 bareMinimumChain := &chainImpl{ 1553 parentConsumer: mockParentConsumer, 1554 channelConsumer: mockChannelConsumer, 1555 1556 consenter: mockConsenter, 1557 channel: mockChannel, 1558 ConsenterSupport: mockSupport, 1559 lastCutBlockNumber: lastCutBlockNumber, 1560 1561 errorChan: errorChan, 1562 haltChan: haltChan, 1563 doneProcessingMessagesToBlocks: make(chan struct{}), 1564 } 1565 1566 var counts []uint64 1567 done := make(chan struct{}) 1568 1569 go func() { 1570 counts, err = bareMinimumChain.processMessagesToBlocks() 1571 done <- struct{}{} 1572 }() 1573 1574 var block1, block2 *cb.Block 1575 1576 block1LastOffset := mpc.HighWaterMarkOffset() 1577 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1578 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 1579 1580 // Set CutAncestors to true so that second message overflows receiver batch 1581 mockSupport.BlockCutterVal.CutAncestors = true 1582 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1583 mockSupport.BlockCutterVal.Block <- struct{}{} 1584 1585 select { 1586 case block1 = <-mockSupport.Blocks: // Let the `mockConsenterSupport.WriteBlock` proceed 1587 case <-time.After(shortTimeout): 1588 logger.Fatalf("Did not receive a block from the blockcutter as expected") 1589 } 1590 1591 // Set CutNext to true to flush all pending messages 1592 mockSupport.BlockCutterVal.CutAncestors = false 1593 mockSupport.BlockCutterVal.CutNext = true 1594 block2LastOffset := mpc.HighWaterMarkOffset() 1595 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1596 mockSupport.BlockCutterVal.Block <- struct{}{} 1597 1598 select { 1599 case block2 = <-mockSupport.Blocks: 1600 case <-time.After(shortTimeout): 1601 logger.Fatalf("Did not receive a block from the blockcutter as expected") 1602 } 1603 1604 logger.Debug("Closing haltChan to exit the infinite for-loop") 1605 close(haltChan) // Identical to chain.Halt() 1606 logger.Debug("haltChan closed") 1607 <-done 1608 1609 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1610 require.Equal(t, uint64(3), counts[indexRecvPass], "Expected 2 messages received and unmarshaled") 1611 require.Equal(t, uint64(3), counts[indexProcessRegularPass], "Expected 2 REGULAR messages processed") 1612 require.Equal(t, lastCutBlockNumber+2, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be bumped up by two") 1613 require.Equal(t, block1LastOffset, extractEncodedOffset(block1.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in first block to be %d", block1LastOffset) 1614 require.Equal(t, block2LastOffset, extractEncodedOffset(block2.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in second block to be %d", block2LastOffset) 1615 }) 1616 1617 t.Run("InvalidConfigEnv", func(t *testing.T) { 1618 errorChan := make(chan struct{}) 1619 close(errorChan) 1620 haltChan := make(chan struct{}) 1621 1622 lastCutBlockNumber := uint64(3) 1623 1624 mockSupport := &mockmultichannel.ConsenterSupport{ 1625 Blocks: make(chan *cb.Block), // WriteBlock will post here 1626 BlockCutterVal: mockblockcutter.NewReceiver(), 1627 ChannelIDVal: mockChannel.topic(), 1628 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1629 ClassifyMsgVal: msgprocessor.ConfigMsg, 1630 ProcessConfigMsgErr: fmt.Errorf("Invalid config message"), 1631 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1632 } 1633 defer close(mockSupport.BlockCutterVal.Block) 1634 1635 bareMinimumChain := &chainImpl{ 1636 parentConsumer: mockParentConsumer, 1637 channelConsumer: mockChannelConsumer, 1638 1639 channel: mockChannel, 1640 ConsenterSupport: mockSupport, 1641 lastCutBlockNumber: lastCutBlockNumber, 1642 1643 errorChan: errorChan, 1644 haltChan: haltChan, 1645 doneProcessingMessagesToBlocks: make(chan struct{}), 1646 } 1647 1648 var counts []uint64 1649 done := make(chan struct{}) 1650 1651 go func() { 1652 counts, err = bareMinimumChain.processMessagesToBlocks() 1653 done <- struct{}{} 1654 }() 1655 1656 // This is the config wrappedMessage that the for-loop will process. 1657 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockConfigEnvelope())))) 1658 1659 logger.Debug("Closing haltChan to exit the infinite for-loop") 1660 // We are guaranteed to hit the haltChan branch after hitting the REGULAR branch at least once 1661 close(haltChan) // Identical to chain.Halt() 1662 logger.Debug("haltChan closed") 1663 <-done 1664 1665 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1666 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1667 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message error") 1668 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber not to be incremented") 1669 }) 1670 1671 t.Run("InvalidOrdererTxEnv", func(t *testing.T) { 1672 errorChan := make(chan struct{}) 1673 close(errorChan) 1674 haltChan := make(chan struct{}) 1675 1676 lastCutBlockNumber := uint64(3) 1677 1678 mockSupport := &mockmultichannel.ConsenterSupport{ 1679 Blocks: make(chan *cb.Block), // WriteBlock will post here 1680 BlockCutterVal: mockblockcutter.NewReceiver(), 1681 ChannelIDVal: mockChannel.topic(), 1682 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1683 ClassifyMsgVal: msgprocessor.ConfigMsg, 1684 ProcessConfigMsgErr: fmt.Errorf("Invalid config message"), 1685 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1686 } 1687 defer close(mockSupport.BlockCutterVal.Block) 1688 1689 bareMinimumChain := &chainImpl{ 1690 parentConsumer: mockParentConsumer, 1691 channelConsumer: mockChannelConsumer, 1692 1693 channel: mockChannel, 1694 ConsenterSupport: mockSupport, 1695 lastCutBlockNumber: lastCutBlockNumber, 1696 1697 errorChan: errorChan, 1698 haltChan: haltChan, 1699 doneProcessingMessagesToBlocks: make(chan struct{}), 1700 } 1701 1702 var counts []uint64 1703 done := make(chan struct{}) 1704 1705 go func() { 1706 counts, err = bareMinimumChain.processMessagesToBlocks() 1707 done <- struct{}{} 1708 }() 1709 1710 // This is the config wrappedMessage that the for-loop will process. 1711 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockOrdererTxEnvelope())))) 1712 1713 logger.Debug("Closing haltChan to exit the infinite for-loop") 1714 // We are guaranteed to hit the haltChan branch after hitting the REGULAR branch at least once 1715 close(haltChan) // Identical to chain.Halt() 1716 logger.Debug("haltChan closed") 1717 <-done 1718 1719 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1720 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1721 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message error") 1722 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber not to be incremented") 1723 }) 1724 1725 t.Run("InvalidNormalEnv", func(t *testing.T) { 1726 errorChan := make(chan struct{}) 1727 close(errorChan) 1728 haltChan := make(chan struct{}) 1729 1730 lastCutBlockNumber := uint64(3) 1731 1732 mockSupport := &mockmultichannel.ConsenterSupport{ 1733 Blocks: make(chan *cb.Block), // WriteBlock will post here 1734 BlockCutterVal: mockblockcutter.NewReceiver(), 1735 ChannelIDVal: mockChannel.topic(), 1736 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1737 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1738 ProcessNormalMsgErr: fmt.Errorf("Invalid normal message"), 1739 } 1740 defer close(mockSupport.BlockCutterVal.Block) 1741 1742 bareMinimumChain := &chainImpl{ 1743 parentConsumer: mockParentConsumer, 1744 channelConsumer: mockChannelConsumer, 1745 1746 channel: mockChannel, 1747 ConsenterSupport: mockSupport, 1748 lastCutBlockNumber: lastCutBlockNumber, 1749 1750 errorChan: errorChan, 1751 haltChan: haltChan, 1752 doneProcessingMessagesToBlocks: make(chan struct{}), 1753 } 1754 1755 var counts []uint64 1756 done := make(chan struct{}) 1757 1758 go func() { 1759 counts, err = bareMinimumChain.processMessagesToBlocks() 1760 done <- struct{}{} 1761 }() 1762 1763 // This is the wrappedMessage that the for-loop will process 1764 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1765 1766 close(haltChan) // Identical to chain.Halt() 1767 <-done 1768 1769 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1770 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1771 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message processed") 1772 }) 1773 1774 t.Run("CutConfigEnv", func(t *testing.T) { 1775 errorChan := make(chan struct{}) 1776 close(errorChan) 1777 haltChan := make(chan struct{}) 1778 1779 lastCutBlockNumber := uint64(3) 1780 1781 mockSupport := &mockmultichannel.ConsenterSupport{ 1782 Blocks: make(chan *cb.Block), // WriteBlock will post here 1783 BlockCutterVal: mockblockcutter.NewReceiver(), 1784 ChannelIDVal: mockChannel.topic(), 1785 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1786 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1787 ClassifyMsgVal: msgprocessor.ConfigMsg, 1788 } 1789 defer close(mockSupport.BlockCutterVal.Block) 1790 1791 bareMinimumChain := &chainImpl{ 1792 parentConsumer: mockParentConsumer, 1793 channelConsumer: mockChannelConsumer, 1794 1795 consenter: mockConsenter, 1796 channel: mockChannel, 1797 ConsenterSupport: mockSupport, 1798 lastCutBlockNumber: lastCutBlockNumber, 1799 1800 errorChan: errorChan, 1801 haltChan: haltChan, 1802 doneProcessingMessagesToBlocks: make(chan struct{}), 1803 } 1804 1805 var counts []uint64 1806 done := make(chan struct{}) 1807 1808 go func() { 1809 counts, err = bareMinimumChain.processMessagesToBlocks() 1810 done <- struct{}{} 1811 }() 1812 1813 configBlkOffset := mpc.HighWaterMarkOffset() 1814 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockConfigEnvelope())))) 1815 1816 var configBlk *cb.Block 1817 1818 select { 1819 case configBlk = <-mockSupport.Blocks: 1820 case <-time.After(shortTimeout): 1821 logger.Fatalf("Did not receive a config block from the blockcutter as expected") 1822 } 1823 1824 close(haltChan) // Identical to chain.Halt() 1825 <-done 1826 1827 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1828 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1829 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 1830 require.Equal(t, lastCutBlockNumber+1, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be incremented by 1") 1831 require.Equal(t, configBlkOffset, extractEncodedOffset(configBlk.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in second block to be %d", configBlkOffset) 1832 }) 1833 1834 // We are not expecting this type of message from Kafka 1835 t.Run("ConfigUpdateEnv", func(t *testing.T) { 1836 errorChan := make(chan struct{}) 1837 close(errorChan) 1838 haltChan := make(chan struct{}) 1839 1840 lastCutBlockNumber := uint64(3) 1841 1842 mockSupport := &mockmultichannel.ConsenterSupport{ 1843 Blocks: make(chan *cb.Block), // WriteBlock will post here 1844 BlockCutterVal: mockblockcutter.NewReceiver(), 1845 ChannelIDVal: mockChannel.topic(), 1846 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1847 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 1848 ClassifyMsgVal: msgprocessor.ConfigUpdateMsg, 1849 } 1850 defer close(mockSupport.BlockCutterVal.Block) 1851 1852 bareMinimumChain := &chainImpl{ 1853 parentConsumer: mockParentConsumer, 1854 channelConsumer: mockChannelConsumer, 1855 1856 channel: mockChannel, 1857 ConsenterSupport: mockSupport, 1858 lastCutBlockNumber: lastCutBlockNumber, 1859 1860 errorChan: errorChan, 1861 haltChan: haltChan, 1862 doneProcessingMessagesToBlocks: make(chan struct{}), 1863 } 1864 1865 var counts []uint64 1866 done := make(chan struct{}) 1867 1868 go func() { 1869 counts, err = bareMinimumChain.processMessagesToBlocks() 1870 done <- struct{}{} 1871 }() 1872 1873 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("FooMessage"))))) 1874 1875 close(haltChan) // Identical to chain.Halt() 1876 <-done 1877 1878 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1879 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1880 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message processed") 1881 }) 1882 1883 t.Run("SendTimeToCut", func(t *testing.T) { 1884 t.Skip("Skipping test as it introduces a race condition") 1885 1886 // NB We haven't set a handlermap for the mock broker so we need to set 1887 // the ProduceResponse 1888 successResponse := new(sarama.ProduceResponse) 1889 successResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError) 1890 mockBroker.Returns(successResponse) 1891 1892 errorChan := make(chan struct{}) 1893 close(errorChan) 1894 haltChan := make(chan struct{}) 1895 1896 lastCutBlockNumber := uint64(3) 1897 1898 mockSupport := &mockmultichannel.ConsenterSupport{ 1899 Blocks: make(chan *cb.Block), // WriteBlock will post here 1900 BlockCutterVal: mockblockcutter.NewReceiver(), 1901 ChannelIDVal: mockChannel.topic(), 1902 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1903 SharedConfigVal: newMockOrderer(extraShortTimeout, []string{mockBroker.Addr()}, false), // ATTN 1904 } 1905 defer close(mockSupport.BlockCutterVal.Block) 1906 1907 bareMinimumChain := &chainImpl{ 1908 producer: producer, 1909 parentConsumer: mockParentConsumer, 1910 channelConsumer: mockChannelConsumer, 1911 1912 channel: mockChannel, 1913 ConsenterSupport: mockSupport, 1914 lastCutBlockNumber: lastCutBlockNumber, 1915 1916 errorChan: errorChan, 1917 haltChan: haltChan, 1918 doneProcessingMessagesToBlocks: make(chan struct{}), 1919 } 1920 1921 var counts []uint64 1922 done := make(chan struct{}) 1923 1924 go func() { 1925 counts, err = bareMinimumChain.processMessagesToBlocks() 1926 done <- struct{}{} 1927 }() 1928 1929 // This is the wrappedMessage that the for-loop will process 1930 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 1931 1932 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 1933 logger.Debugf("Mock blockcutter's Ordered call has returned") 1934 1935 // Sleep so that the timer branch is activated before the exitChan one. 1936 // TODO This is a race condition, will fix in follow-up changeset 1937 time.Sleep(hitBranch) 1938 1939 logger.Debug("Closing haltChan to exit the infinite for-loop") 1940 close(haltChan) // Identical to chain.Halt() 1941 logger.Debug("haltChan closed") 1942 <-done 1943 1944 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 1945 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 1946 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 1947 require.Equal(t, uint64(1), counts[indexSendTimeToCutPass], "Expected 1 TIMER event processed") 1948 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to stay the same") 1949 }) 1950 1951 t.Run("SendTimeToCutError", func(t *testing.T) { 1952 // Note that this test is affected by the following parameters: 1953 // - Net.ReadTimeout 1954 // - Consumer.Retry.Backoff 1955 // - Metadata.Retry.Max 1956 1957 t.Skip("Skipping test as it introduces a race condition") 1958 1959 // Exact same test as ReceiveRegularAndSendTimeToCut. 1960 // Only difference is that the producer's attempt to send a TTC will 1961 // fail with an ErrNotEnoughReplicas error. 1962 failureResponse := new(sarama.ProduceResponse) 1963 failureResponse.AddTopicPartition(mockChannel.topic(), mockChannel.partition(), sarama.ErrNotEnoughReplicas) 1964 mockBroker.Returns(failureResponse) 1965 1966 errorChan := make(chan struct{}) 1967 close(errorChan) 1968 haltChan := make(chan struct{}) 1969 1970 lastCutBlockNumber := uint64(3) 1971 1972 mockSupport := &mockmultichannel.ConsenterSupport{ 1973 Blocks: make(chan *cb.Block), // WriteBlock will post here 1974 BlockCutterVal: mockblockcutter.NewReceiver(), 1975 ChannelIDVal: mockChannel.topic(), 1976 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 1977 SharedConfigVal: newMockOrderer(extraShortTimeout, []string{mockBroker.Addr()}, false), // ATTN 1978 } 1979 defer close(mockSupport.BlockCutterVal.Block) 1980 1981 bareMinimumChain := &chainImpl{ 1982 producer: producer, 1983 parentConsumer: mockParentConsumer, 1984 channelConsumer: mockChannelConsumer, 1985 1986 channel: mockChannel, 1987 ConsenterSupport: mockSupport, 1988 lastCutBlockNumber: lastCutBlockNumber, 1989 1990 errorChan: errorChan, 1991 haltChan: haltChan, 1992 doneProcessingMessagesToBlocks: make(chan struct{}), 1993 } 1994 1995 var counts []uint64 1996 done := make(chan struct{}) 1997 1998 go func() { 1999 counts, err = bareMinimumChain.processMessagesToBlocks() 2000 done <- struct{}{} 2001 }() 2002 2003 // This is the wrappedMessage that the for-loop will process 2004 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage"))))) 2005 2006 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 2007 logger.Debugf("Mock blockcutter's Ordered call has returned") 2008 2009 // Sleep so that the timer branch is activated before the exitChan one. 2010 // TODO This is a race condition, will fix in follow-up changeset 2011 time.Sleep(hitBranch) 2012 2013 logger.Debug("Closing haltChan to exit the infinite for-loop") 2014 close(haltChan) // Identical to chain.Halt() 2015 logger.Debug("haltChan closed") 2016 <-done 2017 2018 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2019 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 2020 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 2021 require.Equal(t, uint64(1), counts[indexSendTimeToCutError], "Expected 1 faulty TIMER event processed") 2022 require.Equal(t, lastCutBlockNumber, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to stay the same") 2023 }) 2024 }) 2025 2026 // This ensures regular kafka messages of type NORMAL are handled properly 2027 t.Run("Normal", func(t *testing.T) { 2028 lastOriginalOffsetProcessed := int64(3) 2029 2030 t.Run("ReceiveTwoRegularAndCutTwoBlocks", func(t *testing.T) { 2031 if testing.Short() { 2032 t.Skip("Skipping test in short mode") 2033 } 2034 2035 errorChan := make(chan struct{}) 2036 close(errorChan) 2037 haltChan := make(chan struct{}) 2038 2039 lastCutBlockNumber := uint64(3) 2040 2041 mockSupport := &mockmultichannel.ConsenterSupport{ 2042 Blocks: make(chan *cb.Block), // WriteBlock will post here 2043 BlockCutterVal: mockblockcutter.NewReceiver(), 2044 ChannelIDVal: mockChannel.topic(), 2045 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2046 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 2047 SequenceVal: uint64(0), 2048 } 2049 defer close(mockSupport.BlockCutterVal.Block) 2050 2051 bareMinimumChain := &chainImpl{ 2052 parentConsumer: mockParentConsumer, 2053 channelConsumer: mockChannelConsumer, 2054 2055 consenter: mockConsenter, 2056 channel: mockChannel, 2057 ConsenterSupport: mockSupport, 2058 lastCutBlockNumber: lastCutBlockNumber, 2059 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 2060 2061 errorChan: errorChan, 2062 haltChan: haltChan, 2063 doneProcessingMessagesToBlocks: make(chan struct{}), 2064 } 2065 2066 var counts []uint64 2067 done := make(chan struct{}) 2068 2069 go func() { 2070 counts, err = bareMinimumChain.processMessagesToBlocks() 2071 done <- struct{}{} 2072 }() 2073 2074 var block1, block2 *cb.Block 2075 2076 // This is the first wrappedMessage that the for-loop will process 2077 block1LastOffset := mpc.HighWaterMarkOffset() 2078 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(0)))) 2079 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 2080 logger.Debugf("Mock blockcutter's Ordered call has returned") 2081 2082 mockSupport.BlockCutterVal.IsolatedTx = true 2083 2084 // This is the first wrappedMessage that the for-loop will process 2085 block2LastOffset := mpc.HighWaterMarkOffset() 2086 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(0)))) 2087 mockSupport.BlockCutterVal.Block <- struct{}{} 2088 logger.Debugf("Mock blockcutter's Ordered call has returned for the second time") 2089 2090 select { 2091 case block1 = <-mockSupport.Blocks: // Let the `mockConsenterSupport.WriteBlock` proceed 2092 case <-time.After(shortTimeout): 2093 logger.Fatalf("Did not receive a block from the blockcutter as expected") 2094 } 2095 2096 select { 2097 case block2 = <-mockSupport.Blocks: 2098 case <-time.After(shortTimeout): 2099 logger.Fatalf("Did not receive a block from the blockcutter as expected") 2100 } 2101 2102 logger.Debug("Closing haltChan to exit the infinite for-loop") 2103 close(haltChan) // Identical to chain.Halt() 2104 logger.Debug("haltChan closed") 2105 <-done 2106 2107 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2108 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 2 messages received and unmarshaled") 2109 require.Equal(t, uint64(2), counts[indexProcessRegularPass], "Expected 2 REGULAR messages processed") 2110 require.Equal(t, lastCutBlockNumber+2, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be bumped up by two") 2111 require.Equal(t, block1LastOffset, extractEncodedOffset(block1.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in first block to be %d", block1LastOffset) 2112 require.Equal(t, block2LastOffset, extractEncodedOffset(block2.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in second block to be %d", block2LastOffset) 2113 }) 2114 2115 t.Run("ReceiveRegularAndQueue", func(t *testing.T) { 2116 if testing.Short() { 2117 t.Skip("Skipping test in short mode") 2118 } 2119 2120 errorChan := make(chan struct{}) 2121 close(errorChan) 2122 haltChan := make(chan struct{}) 2123 2124 lastCutBlockNumber := uint64(3) 2125 2126 mockSupport := &mockmultichannel.ConsenterSupport{ 2127 Blocks: make(chan *cb.Block), // WriteBlock will post here 2128 BlockCutterVal: mockblockcutter.NewReceiver(), 2129 ChannelIDVal: mockChannel.topic(), 2130 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2131 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 2132 } 2133 defer close(mockSupport.BlockCutterVal.Block) 2134 2135 bareMinimumChain := &chainImpl{ 2136 parentConsumer: mockParentConsumer, 2137 channelConsumer: mockChannelConsumer, 2138 2139 consenter: mockConsenter, 2140 channel: mockChannel, 2141 ConsenterSupport: mockSupport, 2142 lastCutBlockNumber: lastCutBlockNumber, 2143 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 2144 2145 errorChan: errorChan, 2146 haltChan: haltChan, 2147 doneProcessingMessagesToBlocks: make(chan struct{}), 2148 } 2149 2150 var counts []uint64 2151 done := make(chan struct{}) 2152 2153 go func() { 2154 counts, err = bareMinimumChain.processMessagesToBlocks() 2155 done <- struct{}{} 2156 }() 2157 2158 mockSupport.BlockCutterVal.CutNext = true 2159 2160 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(0)))) 2161 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 2162 <-mockSupport.Blocks 2163 2164 close(haltChan) 2165 logger.Debug("haltChan closed") 2166 <-done 2167 2168 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2169 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 2170 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 2171 }) 2172 }) 2173 2174 // This ensures regular kafka messages of type CONFIG are handled properly 2175 t.Run("Config", func(t *testing.T) { 2176 // This test sends a normal tx, followed by a config tx. It should 2177 // immediately cut them into two blocks. 2178 t.Run("ReceiveConfigEnvelopeAndCut", func(t *testing.T) { 2179 errorChan := make(chan struct{}) 2180 close(errorChan) 2181 haltChan := make(chan struct{}) 2182 2183 lastCutBlockNumber := uint64(3) 2184 2185 mockSupport := &mockmultichannel.ConsenterSupport{ 2186 Blocks: make(chan *cb.Block), // WriteBlock will post here 2187 BlockCutterVal: mockblockcutter.NewReceiver(), 2188 ChannelIDVal: mockChannel.topic(), 2189 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2190 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 2191 } 2192 defer close(mockSupport.BlockCutterVal.Block) 2193 2194 fakeLastOffsetPersisted := &mockkafka.MetricsGauge{} 2195 fakeLastOffsetPersisted.WithReturns(fakeLastOffsetPersisted) 2196 mockConsenter.(*consenterImpl).metrics.LastOffsetPersisted = fakeLastOffsetPersisted 2197 2198 bareMinimumChain := &chainImpl{ 2199 parentConsumer: mockParentConsumer, 2200 channelConsumer: mockChannelConsumer, 2201 2202 consenter: mockConsenter, 2203 channel: mockChannel, 2204 ConsenterSupport: mockSupport, 2205 lastCutBlockNumber: lastCutBlockNumber, 2206 2207 errorChan: errorChan, 2208 haltChan: haltChan, 2209 doneProcessingMessagesToBlocks: make(chan struct{}), 2210 } 2211 2212 var counts []uint64 2213 done := make(chan struct{}) 2214 2215 go func() { 2216 counts, err = bareMinimumChain.processMessagesToBlocks() 2217 done <- struct{}{} 2218 }() 2219 2220 normalBlkOffset := mpc.HighWaterMarkOffset() 2221 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(0)))) 2222 mockSupport.BlockCutterVal.Block <- struct{}{} // Let the `mockblockcutter.Ordered` call return 2223 2224 configBlkOffset := mpc.HighWaterMarkOffset() 2225 mockSupport.ClassifyMsgVal = msgprocessor.ConfigMsg 2226 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 2227 protoutil.MarshalOrPanic(newMockConfigEnvelope()), 2228 uint64(0), 2229 int64(0)))) 2230 2231 var normalBlk, configBlk *cb.Block 2232 select { 2233 case normalBlk = <-mockSupport.Blocks: 2234 case <-time.After(shortTimeout): 2235 logger.Fatalf("Did not receive a normal block from the blockcutter as expected") 2236 } 2237 2238 select { 2239 case configBlk = <-mockSupport.Blocks: 2240 case <-time.After(shortTimeout): 2241 logger.Fatalf("Did not receive a config block from the blockcutter as expected") 2242 } 2243 2244 close(haltChan) // Identical to chain.Halt() 2245 <-done 2246 2247 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2248 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 2249 require.Equal(t, uint64(2), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 2250 require.Equal(t, lastCutBlockNumber+2, bareMinimumChain.lastCutBlockNumber, "Expected lastCutBlockNumber to be incremented by 2") 2251 require.Equal(t, normalBlkOffset, extractEncodedOffset(normalBlk.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in first block to be %d", normalBlkOffset) 2252 require.Equal(t, configBlkOffset, extractEncodedOffset(configBlk.GetMetadata().Metadata[cb.BlockMetadataIndex_ORDERER]), "Expected encoded offset in second block to be %d", configBlkOffset) 2253 2254 require.Equal(t, fakeLastOffsetPersisted.WithCallCount(), 2) 2255 require.Equal(t, fakeLastOffsetPersisted.WithArgsForCall(0), []string{"channel", "mockChannelFoo"}) 2256 require.Equal(t, fakeLastOffsetPersisted.WithArgsForCall(1), []string{"channel", "mockChannelFoo"}) 2257 require.Equal(t, fakeLastOffsetPersisted.SetCallCount(), 2) 2258 require.Equal(t, fakeLastOffsetPersisted.SetArgsForCall(0), float64(normalBlkOffset)) 2259 require.Equal(t, fakeLastOffsetPersisted.SetArgsForCall(1), float64(configBlkOffset)) 2260 }) 2261 2262 // This ensures config message is re-validated if config seq has advanced 2263 t.Run("RevalidateConfigEnvInvalid", func(t *testing.T) { 2264 if testing.Short() { 2265 t.Skip("Skipping test in short mode") 2266 } 2267 2268 errorChan := make(chan struct{}) 2269 close(errorChan) 2270 haltChan := make(chan struct{}) 2271 2272 lastCutBlockNumber := uint64(3) 2273 2274 mockSupport := &mockmultichannel.ConsenterSupport{ 2275 Blocks: make(chan *cb.Block), // WriteBlock will post here 2276 BlockCutterVal: mockblockcutter.NewReceiver(), 2277 ChannelIDVal: mockChannel.topic(), 2278 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2279 ClassifyMsgVal: msgprocessor.ConfigMsg, 2280 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, false), 2281 SequenceVal: uint64(1), 2282 ProcessConfigMsgErr: fmt.Errorf("Invalid config message"), 2283 } 2284 defer close(mockSupport.BlockCutterVal.Block) 2285 2286 bareMinimumChain := &chainImpl{ 2287 parentConsumer: mockParentConsumer, 2288 channelConsumer: mockChannelConsumer, 2289 2290 channel: mockChannel, 2291 ConsenterSupport: mockSupport, 2292 lastCutBlockNumber: lastCutBlockNumber, 2293 2294 errorChan: errorChan, 2295 haltChan: haltChan, 2296 doneProcessingMessagesToBlocks: make(chan struct{}), 2297 } 2298 2299 var counts []uint64 2300 done := make(chan struct{}) 2301 2302 go func() { 2303 counts, err = bareMinimumChain.processMessagesToBlocks() 2304 done <- struct{}{} 2305 }() 2306 2307 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 2308 protoutil.MarshalOrPanic(newMockConfigEnvelope()), 2309 uint64(0), 2310 int64(0)))) 2311 select { 2312 case <-mockSupport.Blocks: 2313 t.Fatalf("Expected no block being cut given invalid config message") 2314 case <-time.After(shortTimeout): 2315 } 2316 2317 close(haltChan) // Identical to chain.Halt() 2318 <-done 2319 2320 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2321 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 2322 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message error") 2323 }) 2324 }) 2325 }) 2326 2327 t.Run("KafkaError", func(t *testing.T) { 2328 t.Run("ReceiveKafkaErrorAndCloseErrorChan", func(t *testing.T) { 2329 // If we set up the mock broker so that it returns a response, if the 2330 // test finishes before the sendConnectMessage goroutine has received 2331 // this response, we will get a failure ("not all expectations were 2332 // satisfied") from the mock broker. So we sabotage the producer. 2333 failedProducer, _ := sarama.NewSyncProducer([]string{}, mockBrokerConfig) 2334 2335 // We need to have the sendConnectMessage goroutine die instantaneously, 2336 // otherwise we'll get a nil pointer dereference panic. We are 2337 // exploiting the admittedly hacky shortcut where a retriable process 2338 // returns immediately when given the nil time.Duration value for its 2339 // ticker. 2340 zeroRetryConsenter := &consenterImpl{} 2341 2342 // Let's assume an open errorChan, i.e. a healthy link between the 2343 // consumer and the Kafka partition corresponding to the channel 2344 errorChan := make(chan struct{}) 2345 2346 haltChan := make(chan struct{}) 2347 2348 mockSupport := &mockmultichannel.ConsenterSupport{ 2349 ChannelIDVal: mockChannel.topic(), 2350 } 2351 2352 bareMinimumChain := &chainImpl{ 2353 consenter: zeroRetryConsenter, // For sendConnectMessage 2354 producer: failedProducer, // For sendConnectMessage 2355 parentConsumer: mockParentConsumer, 2356 channelConsumer: mockChannelConsumer, 2357 2358 channel: mockChannel, 2359 ConsenterSupport: mockSupport, 2360 2361 errorChan: errorChan, 2362 haltChan: haltChan, 2363 doneProcessingMessagesToBlocks: make(chan struct{}), 2364 } 2365 2366 var counts []uint64 2367 done := make(chan struct{}) 2368 2369 go func() { 2370 counts, err = bareMinimumChain.processMessagesToBlocks() 2371 done <- struct{}{} 2372 }() 2373 2374 // This is what the for-loop will process 2375 mpc.YieldError(fmt.Errorf("fooError")) 2376 2377 logger.Debug("Closing haltChan to exit the infinite for-loop") 2378 close(haltChan) // Identical to chain.Halt() 2379 logger.Debug("haltChan closed") 2380 <-done 2381 2382 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2383 require.Equal(t, uint64(1), counts[indexRecvError], "Expected 1 Kafka error received") 2384 2385 select { 2386 case <-bareMinimumChain.errorChan: 2387 logger.Debug("errorChan is closed as it should be") 2388 default: 2389 t.Fatal("errorChan should have been closed") 2390 } 2391 }) 2392 2393 t.Run("ReceiveKafkaErrorAndThenReceiveRegularMessage", func(t *testing.T) { 2394 t.Skip("Skipping test as it introduces a race condition") 2395 2396 // If we set up the mock broker so that it returns a response, if the 2397 // test finishes before the sendConnectMessage goroutine has received 2398 // this response, we will get a failure ("not all expectations were 2399 // satisfied") from the mock broker. So we sabotage the producer. 2400 failedProducer, _ := sarama.NewSyncProducer([]string{}, mockBrokerConfig) 2401 2402 // We need to have the sendConnectMessage goroutine die instantaneously, 2403 // otherwise we'll get a nil pointer dereference panic. We are 2404 // exploiting the admittedly hacky shortcut where a retriable process 2405 // returns immediately when given the nil time.Duration value for its 2406 // ticker. 2407 zeroRetryConsenter := &consenterImpl{} 2408 2409 // If the errorChan is closed already, the kafkaErr branch shouldn't 2410 // touch it 2411 errorChan := make(chan struct{}) 2412 close(errorChan) 2413 2414 haltChan := make(chan struct{}) 2415 2416 mockSupport := &mockmultichannel.ConsenterSupport{ 2417 ChannelIDVal: mockChannel.topic(), 2418 } 2419 2420 bareMinimumChain := &chainImpl{ 2421 consenter: zeroRetryConsenter, // For sendConnectMessage 2422 producer: failedProducer, // For sendConnectMessage 2423 parentConsumer: mockParentConsumer, 2424 channelConsumer: mockChannelConsumer, 2425 2426 channel: mockChannel, 2427 ConsenterSupport: mockSupport, 2428 2429 errorChan: errorChan, 2430 haltChan: haltChan, 2431 doneProcessingMessagesToBlocks: make(chan struct{}), 2432 } 2433 2434 done := make(chan struct{}) 2435 2436 go func() { 2437 _, err = bareMinimumChain.processMessagesToBlocks() 2438 done <- struct{}{} 2439 }() 2440 2441 // This is what the for-loop will process 2442 mpc.YieldError(fmt.Errorf("foo")) 2443 2444 // We tested this in ReceiveKafkaErrorAndCloseErrorChan, so this check 2445 // is redundant in that regard. We use it however to ensure the 2446 // kafkaErrBranch has been activated before proceeding with pushing the 2447 // regular message. 2448 select { 2449 case <-bareMinimumChain.errorChan: 2450 logger.Debug("errorChan is closed as it should be") 2451 case <-time.After(shortTimeout): 2452 t.Fatal("errorChan should have been closed by now") 2453 } 2454 2455 // This is the wrappedMessage that the for-loop will process. We use 2456 // a broken regular message here on purpose since this is the shortest 2457 // path and it allows us to test what we want. 2458 mpc.YieldMessage(newMockConsumerMessage(newRegularMessage(tamperBytes(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")))))) 2459 2460 // Sleep so that the Messages/errorChan branch is activated. 2461 // TODO Hacky approach, will need to revise eventually 2462 time.Sleep(hitBranch) 2463 2464 // Check that the errorChan was recreated 2465 select { 2466 case <-bareMinimumChain.errorChan: 2467 t.Fatal("errorChan should have been open") 2468 default: 2469 logger.Debug("errorChan is open as it should be") 2470 } 2471 2472 logger.Debug("Closing haltChan to exit the infinite for-loop") 2473 close(haltChan) // Identical to chain.Halt() 2474 logger.Debug("haltChan closed") 2475 <-done 2476 }) 2477 }) 2478 } 2479 2480 // This ensures message is re-validated if config seq has advanced 2481 func TestResubmission(t *testing.T) { 2482 blockIngressMsg := func(t *testing.T, block bool, fn func() error) { 2483 wait := make(chan struct{}) 2484 go func() { 2485 fn() 2486 wait <- struct{}{} 2487 }() 2488 2489 select { 2490 case <-wait: 2491 if block { 2492 t.Fatalf("Expected WaitReady to block") 2493 } 2494 case <-time.After(100 * time.Millisecond): 2495 if !block { 2496 t.Fatalf("Expected WaitReady not to block") 2497 } 2498 } 2499 } 2500 2501 mockBroker := sarama.NewMockBroker(t, 0) 2502 defer func() { mockBroker.Close() }() 2503 2504 mockChannel := newChannel(channelNameForTest(t), defaultPartition) 2505 mockBrokerConfigCopy := *mockBrokerConfig 2506 mockBrokerConfigCopy.ChannelBufferSize = 0 2507 2508 mockParentConsumer := mocks.NewConsumer(t, &mockBrokerConfigCopy) 2509 mpc := mockParentConsumer.ExpectConsumePartition(mockChannel.topic(), mockChannel.partition(), int64(0)) 2510 mockChannelConsumer, err := mockParentConsumer.ConsumePartition(mockChannel.topic(), mockChannel.partition(), int64(0)) 2511 require.NoError(t, err, "Expected no error when setting up the mock partition consumer") 2512 2513 t.Run("Normal", func(t *testing.T) { 2514 // This test lets kafka emit a mock re-submitted message that does not require reprocessing 2515 // (by setting OriginalOffset <= lastOriginalOffsetProcessed) 2516 t.Run("AlreadyProcessedDiscard", func(t *testing.T) { 2517 if testing.Short() { 2518 t.Skip("Skipping test in short mode") 2519 } 2520 2521 errorChan := make(chan struct{}) 2522 close(errorChan) 2523 haltChan := make(chan struct{}) 2524 2525 lastCutBlockNumber := uint64(3) 2526 lastOriginalOffsetProcessed := int64(3) 2527 2528 mockSupport := &mockmultichannel.ConsenterSupport{ 2529 Blocks: make(chan *cb.Block), // WriteBlock will post here 2530 BlockCutterVal: mockblockcutter.NewReceiver(), 2531 ChannelIDVal: mockChannel.topic(), 2532 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2533 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2534 } 2535 defer close(mockSupport.BlockCutterVal.Block) 2536 2537 bareMinimumChain := &chainImpl{ 2538 parentConsumer: mockParentConsumer, 2539 channelConsumer: mockChannelConsumer, 2540 2541 channel: mockChannel, 2542 ConsenterSupport: mockSupport, 2543 lastCutBlockNumber: lastCutBlockNumber, 2544 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 2545 2546 errorChan: errorChan, 2547 haltChan: haltChan, 2548 doneProcessingMessagesToBlocks: make(chan struct{}), 2549 } 2550 2551 var counts []uint64 2552 done := make(chan struct{}) 2553 2554 go func() { 2555 counts, err = bareMinimumChain.processMessagesToBlocks() 2556 done <- struct{}{} 2557 }() 2558 2559 mockSupport.BlockCutterVal.CutNext = true 2560 2561 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(2)))) 2562 2563 select { 2564 case <-mockSupport.Blocks: 2565 t.Fatalf("Expected no block being cut") 2566 case <-time.After(shortTimeout): 2567 } 2568 2569 close(haltChan) 2570 logger.Debug("haltChan closed") 2571 <-done 2572 2573 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2574 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 2575 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 2576 }) 2577 2578 // This test lets kafka emit a mock re-submitted message that requires reprocessing 2579 // (by setting OriginalOffset > lastOriginalOffsetProcessed) 2580 // Two normal messages are enqueued in this test case: reprossed normal message where 2581 // `originalOffset` is not 0, followed by a normal msg where `originalOffset` is 0. 2582 // It tests the case that even no block is cut, `lastOriginalOffsetProcessed` is still 2583 // updated. We inspect the block to verify correct `LastOriginalOffsetProcessed` in the 2584 // kafka metadata. 2585 t.Run("ResubmittedMsgEnqueue", func(t *testing.T) { 2586 if testing.Short() { 2587 t.Skip("Skipping test in short mode") 2588 } 2589 2590 errorChan := make(chan struct{}) 2591 close(errorChan) 2592 haltChan := make(chan struct{}) 2593 2594 lastCutBlockNumber := uint64(3) 2595 lastOriginalOffsetProcessed := int64(3) 2596 2597 mockSupport := &mockmultichannel.ConsenterSupport{ 2598 Blocks: make(chan *cb.Block), // WriteBlock will post here 2599 BlockCutterVal: mockblockcutter.NewReceiver(), 2600 ChannelIDVal: mockChannel.topic(), 2601 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2602 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2603 SequenceVal: uint64(0), 2604 } 2605 defer close(mockSupport.BlockCutterVal.Block) 2606 2607 bareMinimumChain := &chainImpl{ 2608 parentConsumer: mockParentConsumer, 2609 channelConsumer: mockChannelConsumer, 2610 2611 consenter: mockConsenter, 2612 channel: mockChannel, 2613 ConsenterSupport: mockSupport, 2614 lastCutBlockNumber: lastCutBlockNumber, 2615 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 2616 2617 errorChan: errorChan, 2618 haltChan: haltChan, 2619 doneProcessingMessagesToBlocks: make(chan struct{}), 2620 } 2621 2622 var counts []uint64 2623 done := make(chan struct{}) 2624 2625 go func() { 2626 counts, err = bareMinimumChain.processMessagesToBlocks() 2627 done <- struct{}{} 2628 }() 2629 2630 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(4)))) 2631 mockSupport.BlockCutterVal.Block <- struct{}{} 2632 2633 select { 2634 case <-mockSupport.Blocks: 2635 t.Fatalf("Expected no block to be cut") 2636 case <-time.After(shortTimeout): 2637 } 2638 2639 mockSupport.BlockCutterVal.CutNext = true 2640 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(0)))) 2641 mockSupport.BlockCutterVal.Block <- struct{}{} 2642 2643 select { 2644 case block := <-mockSupport.Blocks: 2645 metadata := &cb.Metadata{} 2646 proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER], metadata) 2647 kafkaMetadata := &ab.KafkaMetadata{} 2648 proto.Unmarshal(metadata.Value, kafkaMetadata) 2649 require.Equal(t, kafkaMetadata.LastOriginalOffsetProcessed, int64(4)) 2650 case <-time.After(shortTimeout): 2651 t.Fatalf("Expected one block being cut") 2652 } 2653 2654 close(haltChan) 2655 logger.Debug("haltChan closed") 2656 <-done 2657 2658 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2659 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 2660 require.Equal(t, uint64(2), counts[indexProcessRegularPass], "Expected 2 REGULAR message processed") 2661 }) 2662 2663 t.Run("InvalidDiscard", func(t *testing.T) { 2664 if testing.Short() { 2665 t.Skip("Skipping test in short mode") 2666 } 2667 2668 errorChan := make(chan struct{}) 2669 close(errorChan) 2670 haltChan := make(chan struct{}) 2671 2672 lastCutBlockNumber := uint64(3) 2673 2674 mockSupport := &mockmultichannel.ConsenterSupport{ 2675 Blocks: make(chan *cb.Block), // WriteBlock will post here 2676 BlockCutterVal: mockblockcutter.NewReceiver(), 2677 ChannelIDVal: mockChannel.topic(), 2678 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2679 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2680 SequenceVal: uint64(1), 2681 ProcessNormalMsgErr: fmt.Errorf("Invalid normal message"), 2682 } 2683 defer close(mockSupport.BlockCutterVal.Block) 2684 2685 bareMinimumChain := &chainImpl{ 2686 parentConsumer: mockParentConsumer, 2687 channelConsumer: mockChannelConsumer, 2688 2689 channel: mockChannel, 2690 ConsenterSupport: mockSupport, 2691 lastCutBlockNumber: lastCutBlockNumber, 2692 2693 errorChan: errorChan, 2694 haltChan: haltChan, 2695 doneProcessingMessagesToBlocks: make(chan struct{}), 2696 } 2697 2698 var counts []uint64 2699 done := make(chan struct{}) 2700 2701 go func() { 2702 counts, err = bareMinimumChain.processMessagesToBlocks() 2703 done <- struct{}{} 2704 }() 2705 2706 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage( 2707 protoutil.MarshalOrPanic(newMockNormalEnvelope(t)), 2708 uint64(0), 2709 int64(0)))) 2710 select { 2711 case <-mockSupport.Blocks: 2712 t.Fatalf("Expected no block being cut given invalid config message") 2713 case <-time.After(shortTimeout): 2714 } 2715 2716 close(haltChan) // Identical to chain.Halt() 2717 <-done 2718 2719 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2720 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 2721 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message error") 2722 }) 2723 2724 // This tests resubmission path with following steps: 2725 // 1) Kafka emits a message with lagged config seq, consenter is expected to re-process and 2726 // re-submit the message. However, `WaitReady` shouldn't be blocked for a normal message 2727 // 2) Kafka is expected to receive a producer message where config seq is advanced to catch 2728 // up with current config seq, and OriginalOffset is not nil to capture the offset that 2729 // consenter previously received from Kafka 2730 // 3) when consenter receives Kafka message submitted in 2), where config seq is in sync, 2731 // it cuts a block for it. 2732 t.Run("ValidResubmit", func(t *testing.T) { 2733 if testing.Short() { 2734 t.Skip("Skipping test in short mode") 2735 } 2736 2737 startChan := make(chan struct{}) 2738 close(startChan) 2739 errorChan := make(chan struct{}) 2740 close(errorChan) 2741 haltChan := make(chan struct{}) 2742 doneReprocessing := make(chan struct{}) 2743 close(doneReprocessing) 2744 2745 lastCutBlockNumber := uint64(3) 2746 2747 mockSupport := &mockmultichannel.ConsenterSupport{ 2748 Blocks: make(chan *cb.Block), // WriteBlock will post here 2749 BlockCutterVal: mockblockcutter.NewReceiver(), 2750 ChannelIDVal: mockChannel.topic(), 2751 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2752 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2753 SequenceVal: uint64(1), 2754 ConfigSeqVal: uint64(1), 2755 } 2756 defer close(mockSupport.BlockCutterVal.Block) 2757 2758 expectedKafkaMsgCh := make(chan *ab.KafkaMessage, 1) 2759 producer := mocks.NewSyncProducer(t, mockBrokerConfig) 2760 producer.ExpectSendMessageWithCheckerFunctionAndSucceed(func(val []byte) error { 2761 defer close(expectedKafkaMsgCh) 2762 2763 expectedKafkaMsg := &ab.KafkaMessage{} 2764 if err := proto.Unmarshal(val, expectedKafkaMsg); err != nil { 2765 return err 2766 } 2767 2768 regular := expectedKafkaMsg.GetRegular() 2769 if regular == nil { 2770 return fmt.Errorf("Expect message type to be regular") 2771 } 2772 2773 if regular.ConfigSeq != mockSupport.Sequence() { 2774 return fmt.Errorf("Expect new config seq to be %d, got %d", mockSupport.Sequence(), regular.ConfigSeq) 2775 } 2776 2777 if regular.OriginalOffset == 0 { 2778 return fmt.Errorf("Expect Original Offset to be non-zero if resubmission") 2779 } 2780 2781 expectedKafkaMsgCh <- expectedKafkaMsg 2782 return nil 2783 }) 2784 2785 bareMinimumChain := &chainImpl{ 2786 producer: producer, 2787 parentConsumer: mockParentConsumer, 2788 channelConsumer: mockChannelConsumer, 2789 2790 consenter: mockConsenter, 2791 channel: mockChannel, 2792 ConsenterSupport: mockSupport, 2793 lastCutBlockNumber: lastCutBlockNumber, 2794 2795 startChan: startChan, 2796 errorChan: errorChan, 2797 haltChan: haltChan, 2798 doneProcessingMessagesToBlocks: make(chan struct{}), 2799 doneReprocessingMsgInFlight: doneReprocessing, 2800 } 2801 2802 var counts []uint64 2803 done := make(chan struct{}) 2804 2805 go func() { 2806 counts, err = bareMinimumChain.processMessagesToBlocks() 2807 done <- struct{}{} 2808 }() 2809 2810 mockSupport.BlockCutterVal.CutNext = true 2811 2812 mpc.YieldMessage(newMockConsumerMessage(newNormalMessage( 2813 protoutil.MarshalOrPanic(newMockNormalEnvelope(t)), 2814 uint64(0), 2815 int64(0)))) 2816 select { 2817 case <-mockSupport.Blocks: 2818 t.Fatalf("Expected no block being cut given invalid config message") 2819 case <-time.After(shortTimeout): 2820 } 2821 2822 // check that WaitReady is not blocked for a in-flight reprocessed messages of type NORMAL 2823 waitReady := make(chan struct{}) 2824 go func() { 2825 bareMinimumChain.WaitReady() 2826 waitReady <- struct{}{} 2827 }() 2828 2829 select { 2830 case <-waitReady: 2831 case <-time.After(100 * time.Millisecond): 2832 t.Fatalf("Expected WaitReady call to be unblock because all reprocessed messages are consumed") 2833 } 2834 2835 // Emits the kafka message produced by consenter 2836 select { 2837 case expectedKafkaMsg := <-expectedKafkaMsgCh: 2838 require.NotNil(t, expectedKafkaMsg) 2839 mpc.YieldMessage(newMockConsumerMessage(expectedKafkaMsg)) 2840 mockSupport.BlockCutterVal.Block <- struct{}{} 2841 case <-time.After(shortTimeout): 2842 t.Fatalf("Expected to receive kafka message") 2843 } 2844 2845 select { 2846 case <-mockSupport.Blocks: 2847 case <-time.After(shortTimeout): 2848 t.Fatalf("Expected one block being cut") 2849 } 2850 2851 close(haltChan) // Identical to chain.Halt() 2852 <-done 2853 2854 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2855 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 2856 require.Equal(t, uint64(2), counts[indexProcessRegularPass], "Expected 2 REGULAR message error") 2857 }) 2858 }) 2859 2860 t.Run("Config", func(t *testing.T) { 2861 // This test lets kafka emit a mock re-submitted message that does not require reprocessing 2862 // (by setting OriginalOffset <= lastOriginalOffsetProcessed) 2863 t.Run("AlreadyProcessedDiscard", func(t *testing.T) { 2864 if testing.Short() { 2865 t.Skip("Skipping test in short mode") 2866 } 2867 2868 errorChan := make(chan struct{}) 2869 close(errorChan) 2870 haltChan := make(chan struct{}) 2871 2872 lastCutBlockNumber := uint64(3) 2873 lastOriginalOffsetProcessed := int64(3) 2874 2875 mockSupport := &mockmultichannel.ConsenterSupport{ 2876 Blocks: make(chan *cb.Block), // WriteBlock will post here 2877 BlockCutterVal: mockblockcutter.NewReceiver(), 2878 ChannelIDVal: mockChannel.topic(), 2879 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2880 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2881 } 2882 defer close(mockSupport.BlockCutterVal.Block) 2883 2884 bareMinimumChain := &chainImpl{ 2885 parentConsumer: mockParentConsumer, 2886 channelConsumer: mockChannelConsumer, 2887 2888 channel: mockChannel, 2889 ConsenterSupport: mockSupport, 2890 lastCutBlockNumber: lastCutBlockNumber, 2891 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 2892 2893 errorChan: errorChan, 2894 haltChan: haltChan, 2895 doneProcessingMessagesToBlocks: make(chan struct{}), 2896 } 2897 2898 var counts []uint64 2899 done := make(chan struct{}) 2900 2901 go func() { 2902 counts, err = bareMinimumChain.processMessagesToBlocks() 2903 done <- struct{}{} 2904 }() 2905 2906 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(0), int64(2)))) 2907 2908 select { 2909 case <-mockSupport.Blocks: 2910 t.Fatalf("Expected no block being cut") 2911 case <-time.After(shortTimeout): 2912 } 2913 2914 close(haltChan) 2915 logger.Debug("haltChan closed") 2916 <-done 2917 2918 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 2919 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 2920 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 2921 }) 2922 2923 // This test simulated the non-deterministic case, where somebody resubmitted message at offset X, 2924 // whereas we didn't. That message was considered invalid by us during revalidation, however somebody 2925 // else deemed it to be valid, and resubmitted it. 2926 t.Run("Non-determinism", func(t *testing.T) { 2927 if testing.Short() { 2928 t.Skip("Skipping test in short mode") 2929 } 2930 2931 startChan := make(chan struct{}) 2932 close(startChan) 2933 errorChan := make(chan struct{}) 2934 close(errorChan) 2935 haltChan := make(chan struct{}) 2936 doneReprocessing := make(chan struct{}) 2937 close(doneReprocessing) 2938 2939 lastCutBlockNumber := uint64(3) 2940 2941 mockSupport := &mockmultichannel.ConsenterSupport{ 2942 Blocks: make(chan *cb.Block), // WriteBlock will post here 2943 BlockCutterVal: mockblockcutter.NewReceiver(), 2944 ChannelIDVal: mockChannel.topic(), 2945 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 2946 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 2947 SequenceVal: uint64(1), 2948 ConfigSeqVal: uint64(1), 2949 ProcessConfigMsgVal: newMockConfigEnvelope(), 2950 } 2951 defer close(mockSupport.BlockCutterVal.Block) 2952 2953 bareMinimumChain := &chainImpl{ 2954 parentConsumer: mockParentConsumer, 2955 channelConsumer: mockChannelConsumer, 2956 2957 consenter: mockConsenter, 2958 channel: mockChannel, 2959 ConsenterSupport: mockSupport, 2960 lastCutBlockNumber: lastCutBlockNumber, 2961 2962 lastResubmittedConfigOffset: int64(0), 2963 2964 startChan: startChan, 2965 errorChan: errorChan, 2966 haltChan: haltChan, 2967 doneProcessingMessagesToBlocks: make(chan struct{}), 2968 doneReprocessingMsgInFlight: doneReprocessing, 2969 } 2970 2971 var counts []uint64 2972 done := make(chan struct{}) 2973 2974 go func() { 2975 counts, err = bareMinimumChain.processMessagesToBlocks() 2976 done <- struct{}{} 2977 }() 2978 2979 // check that WaitReady is not blocked at beginning 2980 blockIngressMsg(t, false, bareMinimumChain.WaitReady) 2981 2982 // Message should be revalidated but considered invalid, so we don't resubmit it 2983 mockSupport.ProcessConfigMsgErr = fmt.Errorf("invalid message found during revalidation") 2984 2985 // Emits a config message with lagged config sequence 2986 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 2987 protoutil.MarshalOrPanic(newMockConfigEnvelope()), 2988 uint64(0), 2989 int64(0)))) 2990 select { 2991 case <-mockSupport.Blocks: 2992 t.Fatalf("Expected no block being cut") 2993 case <-time.After(shortTimeout): 2994 } 2995 2996 // check that WaitReady is still not blocked because we haven't resubmitted anything 2997 blockIngressMsg(t, false, bareMinimumChain.WaitReady) 2998 2999 // Somebody else resubmitted the message which we deemed to be invalid 3000 // We deliberately keep ProcessConfigMsgErr unchanged, so we could be 3001 // certain that we are not running into revalidation path. 3002 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 3003 protoutil.MarshalOrPanic(newMockConfigEnvelope()), 3004 uint64(1), 3005 int64(5)))) 3006 3007 select { 3008 case block := <-mockSupport.Blocks: 3009 metadata, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER) 3010 require.NoError(t, err, "Failed to get metadata from block") 3011 kafkaMetadata := &ab.KafkaMetadata{} 3012 err = proto.Unmarshal(metadata.Value, kafkaMetadata) 3013 require.NoError(t, err, "Failed to unmarshal metadata") 3014 3015 require.Equal(t, kafkaMetadata.LastResubmittedConfigOffset, int64(5), "LastResubmittedConfigOffset didn't catch up") 3016 require.Equal(t, kafkaMetadata.LastOriginalOffsetProcessed, int64(5), "LastOriginalOffsetProcessed doesn't match") 3017 case <-time.After(shortTimeout): 3018 t.Fatalf("Expected one block being cut") 3019 } 3020 3021 close(haltChan) // Identical to chain.Halt() 3022 <-done 3023 3024 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 3025 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 3026 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 2 REGULAR message error") 3027 }) 3028 3029 // This test lets kafka emit a mock re-submitted message whose config seq is still behind 3030 t.Run("ResubmittedMsgStillBehind", func(t *testing.T) { 3031 if testing.Short() { 3032 t.Skip("Skipping test in short mode") 3033 } 3034 3035 startChan := make(chan struct{}) 3036 close(startChan) 3037 errorChan := make(chan struct{}) 3038 close(errorChan) 3039 haltChan := make(chan struct{}) 3040 3041 lastCutBlockNumber := uint64(3) 3042 lastOriginalOffsetProcessed := int64(3) 3043 3044 mockSupport := &mockmultichannel.ConsenterSupport{ 3045 Blocks: make(chan *cb.Block), // WriteBlock will post here 3046 BlockCutterVal: mockblockcutter.NewReceiver(), 3047 ChannelIDVal: mockChannel.topic(), 3048 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 3049 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 3050 ChannelConfigVal: newMockChannel(), 3051 SequenceVal: uint64(2), 3052 ProcessConfigMsgVal: newMockConfigEnvelope(), 3053 } 3054 defer close(mockSupport.BlockCutterVal.Block) 3055 3056 producer := mocks.NewSyncProducer(t, mockBrokerConfig) 3057 producer.ExpectSendMessageWithCheckerFunctionAndSucceed(func(val []byte) error { 3058 return nil 3059 }) 3060 3061 bareMinimumChain := &chainImpl{ 3062 producer: producer, 3063 parentConsumer: mockParentConsumer, 3064 channelConsumer: mockChannelConsumer, 3065 3066 channel: mockChannel, 3067 ConsenterSupport: mockSupport, 3068 lastCutBlockNumber: lastCutBlockNumber, 3069 lastOriginalOffsetProcessed: lastOriginalOffsetProcessed, 3070 3071 startChan: startChan, 3072 errorChan: errorChan, 3073 haltChan: haltChan, 3074 doneProcessingMessagesToBlocks: make(chan struct{}), 3075 doneReprocessingMsgInFlight: make(chan struct{}), 3076 } 3077 3078 // WaitReady should block at beginning since we are in the middle of reprocessing 3079 blockIngressMsg(t, true, bareMinimumChain.WaitReady) 3080 3081 var counts []uint64 3082 done := make(chan struct{}) 3083 3084 go func() { 3085 counts, err = bareMinimumChain.processMessagesToBlocks() 3086 done <- struct{}{} 3087 }() 3088 3089 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage(protoutil.MarshalOrPanic(newMockEnvelope("fooMessage")), uint64(1), int64(4)))) 3090 select { 3091 case <-mockSupport.Blocks: 3092 t.Fatalf("Expected no block being cut") 3093 case <-time.After(shortTimeout): 3094 } 3095 3096 // WaitReady should still block as resubmitted config message is still behind current config seq 3097 blockIngressMsg(t, true, bareMinimumChain.WaitReady) 3098 3099 close(haltChan) 3100 logger.Debug("haltChan closed") 3101 <-done 3102 3103 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 3104 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 3105 require.Equal(t, uint64(1), counts[indexProcessRegularPass], "Expected 1 REGULAR message processed") 3106 }) 3107 3108 t.Run("InvalidDiscard", func(t *testing.T) { 3109 if testing.Short() { 3110 t.Skip("Skipping test in short mode") 3111 } 3112 3113 errorChan := make(chan struct{}) 3114 close(errorChan) 3115 haltChan := make(chan struct{}) 3116 3117 lastCutBlockNumber := uint64(3) 3118 3119 mockSupport := &mockmultichannel.ConsenterSupport{ 3120 Blocks: make(chan *cb.Block), // WriteBlock will post here 3121 BlockCutterVal: mockblockcutter.NewReceiver(), 3122 ChannelIDVal: mockChannel.topic(), 3123 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 3124 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 3125 SequenceVal: uint64(1), 3126 ProcessConfigMsgErr: fmt.Errorf("Invalid config message"), 3127 } 3128 defer close(mockSupport.BlockCutterVal.Block) 3129 3130 bareMinimumChain := &chainImpl{ 3131 parentConsumer: mockParentConsumer, 3132 channelConsumer: mockChannelConsumer, 3133 3134 channel: mockChannel, 3135 ConsenterSupport: mockSupport, 3136 lastCutBlockNumber: lastCutBlockNumber, 3137 3138 errorChan: errorChan, 3139 haltChan: haltChan, 3140 doneProcessingMessagesToBlocks: make(chan struct{}), 3141 } 3142 3143 var counts []uint64 3144 done := make(chan struct{}) 3145 3146 go func() { 3147 counts, err = bareMinimumChain.processMessagesToBlocks() 3148 done <- struct{}{} 3149 }() 3150 3151 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 3152 protoutil.MarshalOrPanic(newMockNormalEnvelope(t)), 3153 uint64(0), 3154 int64(0)))) 3155 select { 3156 case <-mockSupport.Blocks: 3157 t.Fatalf("Expected no block being cut given invalid config message") 3158 case <-time.After(shortTimeout): 3159 } 3160 3161 close(haltChan) // Identical to chain.Halt() 3162 <-done 3163 3164 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 3165 require.Equal(t, uint64(1), counts[indexRecvPass], "Expected 1 message received and unmarshaled") 3166 require.Equal(t, uint64(1), counts[indexProcessRegularError], "Expected 1 REGULAR message error") 3167 }) 3168 3169 // This tests resubmission path with following steps: 3170 // 1) Kafka emits a message with lagged config seq, consenter is expected to re-process and 3171 // re-submit the message, as well as block `WaitReady` API 3172 // 2) Kafka is expected to receive a producer message where config seq is advanced to catch 3173 // up with current config seq, and OriginalOffset is not nil to capture the offset that 3174 // consenter previously received from Kafka 3175 // 3) when consenter receives Kafka message submitted in 2), where config seq is in sync, 3176 // it cuts a block for it and lifts block on `WaitReady`. 3177 t.Run("ValidResubmit", func(t *testing.T) { 3178 if testing.Short() { 3179 t.Skip("Skipping test in short mode") 3180 } 3181 3182 startChan := make(chan struct{}) 3183 close(startChan) 3184 errorChan := make(chan struct{}) 3185 close(errorChan) 3186 haltChan := make(chan struct{}) 3187 doneReprocessing := make(chan struct{}) 3188 close(doneReprocessing) 3189 3190 lastCutBlockNumber := uint64(3) 3191 3192 mockSupport := &mockmultichannel.ConsenterSupport{ 3193 Blocks: make(chan *cb.Block), // WriteBlock will post here 3194 BlockCutterVal: mockblockcutter.NewReceiver(), 3195 ChannelIDVal: mockChannel.topic(), 3196 HeightVal: lastCutBlockNumber, // Incremented during the WriteBlock call 3197 SharedConfigVal: newMockOrderer(longTimeout, []string{mockBroker.Addr()}, true), 3198 ChannelConfigVal: newMockChannel(), 3199 SequenceVal: uint64(1), 3200 ConfigSeqVal: uint64(1), 3201 ProcessConfigMsgVal: newMockConfigEnvelope(), 3202 } 3203 defer close(mockSupport.BlockCutterVal.Block) 3204 3205 expectedKafkaMsgCh := make(chan *ab.KafkaMessage, 1) 3206 producer := mocks.NewSyncProducer(t, mockBrokerConfig) 3207 producer.ExpectSendMessageWithCheckerFunctionAndSucceed(func(val []byte) error { 3208 defer close(expectedKafkaMsgCh) 3209 3210 expectedKafkaMsg := &ab.KafkaMessage{} 3211 if err := proto.Unmarshal(val, expectedKafkaMsg); err != nil { 3212 return err 3213 } 3214 3215 regular := expectedKafkaMsg.GetRegular() 3216 if regular == nil { 3217 return fmt.Errorf("Expect message type to be regular") 3218 } 3219 3220 if regular.ConfigSeq != mockSupport.Sequence() { 3221 return fmt.Errorf("Expect new config seq to be %d, got %d", mockSupport.Sequence(), regular.ConfigSeq) 3222 } 3223 3224 if regular.OriginalOffset == 0 { 3225 return fmt.Errorf("Expect Original Offset to be non-zero if resubmission") 3226 } 3227 3228 expectedKafkaMsgCh <- expectedKafkaMsg 3229 return nil 3230 }) 3231 3232 bareMinimumChain := &chainImpl{ 3233 producer: producer, 3234 parentConsumer: mockParentConsumer, 3235 channelConsumer: mockChannelConsumer, 3236 3237 consenter: mockConsenter, 3238 channel: mockChannel, 3239 ConsenterSupport: mockSupport, 3240 lastCutBlockNumber: lastCutBlockNumber, 3241 3242 startChan: startChan, 3243 errorChan: errorChan, 3244 haltChan: haltChan, 3245 doneProcessingMessagesToBlocks: make(chan struct{}), 3246 doneReprocessingMsgInFlight: doneReprocessing, 3247 } 3248 3249 var counts []uint64 3250 done := make(chan struct{}) 3251 3252 go func() { 3253 counts, err = bareMinimumChain.processMessagesToBlocks() 3254 done <- struct{}{} 3255 }() 3256 3257 // check that WaitReady is not blocked at beginning 3258 blockIngressMsg(t, false, bareMinimumChain.WaitReady) 3259 3260 // Emits a config message with lagged config sequence 3261 mpc.YieldMessage(newMockConsumerMessage(newConfigMessage( 3262 protoutil.MarshalOrPanic(newMockConfigEnvelope()), 3263 uint64(0), 3264 int64(0)))) 3265 select { 3266 case <-mockSupport.Blocks: 3267 t.Fatalf("Expected no block being cut given lagged config message") 3268 case <-time.After(shortTimeout): 3269 } 3270 3271 // check that WaitReady is actually blocked because of in-flight reprocessed messages 3272 blockIngressMsg(t, true, bareMinimumChain.WaitReady) 3273 3274 select { 3275 case expectedKafkaMsg := <-expectedKafkaMsgCh: 3276 require.NotNil(t, expectedKafkaMsg) 3277 // Emits the kafka message produced by consenter 3278 mpc.YieldMessage(newMockConsumerMessage(expectedKafkaMsg)) 3279 case <-time.After(shortTimeout): 3280 t.Fatalf("Expected to receive kafka message") 3281 } 3282 3283 select { 3284 case <-mockSupport.Blocks: 3285 case <-time.After(shortTimeout): 3286 t.Fatalf("Expected one block being cut") 3287 } 3288 3289 // `WaitReady` should be unblocked now 3290 blockIngressMsg(t, false, bareMinimumChain.WaitReady) 3291 3292 close(haltChan) // Identical to chain.Halt() 3293 <-done 3294 3295 require.NoError(t, err, "Expected the processMessagesToBlocks call to return without errors") 3296 require.Equal(t, uint64(2), counts[indexRecvPass], "Expected 2 message received and unmarshaled") 3297 require.Equal(t, uint64(2), counts[indexProcessRegularPass], "Expected 2 REGULAR message error") 3298 }) 3299 }) 3300 } 3301 3302 // Test helper functions here. 3303 3304 func newRegularMessage(payload []byte) *ab.KafkaMessage { 3305 return &ab.KafkaMessage{ 3306 Type: &ab.KafkaMessage_Regular{ 3307 Regular: &ab.KafkaMessageRegular{ 3308 Payload: payload, 3309 }, 3310 }, 3311 } 3312 } 3313 3314 func newMockNormalEnvelope(t *testing.T) *cb.Envelope { 3315 return &cb.Envelope{Payload: protoutil.MarshalOrPanic(&cb.Payload{ 3316 Header: &cb.Header{ChannelHeader: protoutil.MarshalOrPanic( 3317 &cb.ChannelHeader{Type: int32(cb.HeaderType_MESSAGE), ChannelId: channelNameForTest(t)})}, 3318 Data: []byte("Foo"), 3319 })} 3320 } 3321 3322 func newMockConfigEnvelope() *cb.Envelope { 3323 return &cb.Envelope{Payload: protoutil.MarshalOrPanic(&cb.Payload{ 3324 Header: &cb.Header{ChannelHeader: protoutil.MarshalOrPanic( 3325 &cb.ChannelHeader{Type: int32(cb.HeaderType_CONFIG), ChannelId: "foo"})}, 3326 Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{}), 3327 })} 3328 } 3329 3330 func newMockOrdererTxEnvelope() *cb.Envelope { 3331 return &cb.Envelope{Payload: protoutil.MarshalOrPanic(&cb.Payload{ 3332 Header: &cb.Header{ChannelHeader: protoutil.MarshalOrPanic( 3333 &cb.ChannelHeader{Type: int32(cb.HeaderType_ORDERER_TRANSACTION), ChannelId: "foo"})}, 3334 Data: protoutil.MarshalOrPanic(newMockConfigEnvelope()), 3335 })} 3336 } 3337 3338 func TestDeliverSession(t *testing.T) { 3339 type testEnvironment struct { 3340 channelID string 3341 topic string 3342 partition int32 3343 height int64 3344 nextOffset int64 3345 support *mockConsenterSupport 3346 broker0 *sarama.MockBroker 3347 broker1 *sarama.MockBroker 3348 broker2 *sarama.MockBroker 3349 testMsg sarama.Encoder 3350 } 3351 3352 // initializes test environment 3353 newTestEnvironment := func(t *testing.T) *testEnvironment { 3354 channelID := channelNameForTest(t) 3355 topic := channelID 3356 partition := int32(defaultPartition) 3357 height := int64(100) 3358 nextOffset := height + 1 3359 broker0 := sarama.NewMockBroker(t, 0) 3360 broker1 := sarama.NewMockBroker(t, 1) 3361 broker2 := sarama.NewMockBroker(t, 2) 3362 3363 // broker0 will seed the info about the other brokers and the partition leader 3364 broker0.SetHandlerByMap(map[string]sarama.MockResponse{ 3365 "MetadataRequest": sarama.NewMockMetadataResponse(t). 3366 SetBroker(broker1.Addr(), broker1.BrokerID()). 3367 SetBroker(broker2.Addr(), broker2.BrokerID()). 3368 SetLeader(topic, partition, broker1.BrokerID()), 3369 }) 3370 3371 // configure broker1 with responses needed for startup 3372 broker1.SetHandlerByMap(map[string]sarama.MockResponse{ 3373 // CONNECT ProduceRequest 3374 "ProduceRequest": sarama.NewMockProduceResponse(t). 3375 SetError(topic, partition, sarama.ErrNoError), 3376 // respond to request for offset of topic 3377 "OffsetRequest": sarama.NewMockOffsetResponse(t). 3378 SetOffset(topic, partition, sarama.OffsetOldest, 0). 3379 SetOffset(topic, partition, sarama.OffsetNewest, nextOffset), 3380 // respond to fetch requests with empty response while starting up 3381 "FetchRequest": sarama.NewMockFetchResponse(t, 1), 3382 }) 3383 3384 // configure broker2 with a default fetch request response 3385 broker2.SetHandlerByMap(map[string]sarama.MockResponse{ 3386 // respond to fetch requests with empty response while starting up 3387 "FetchRequest": sarama.NewMockFetchResponse(t, 1), 3388 }) 3389 3390 // setup mock blockcutter 3391 blockcutter := &mockReceiver{} 3392 blockcutter.On("Ordered", mock.Anything).Return([][]*cb.Envelope{{&cb.Envelope{}}}, false) 3393 3394 // setup mock chain support and mock method calls 3395 support := &mockConsenterSupport{} 3396 support.On("Height").Return(uint64(height)) 3397 support.On("ChannelID").Return(topic) 3398 support.On("Sequence").Return(uint64(0)) 3399 support.On("SharedConfig").Return(newMockOrderer(0, []string{broker0.Addr()}, false)) 3400 support.On("ClassifyMsg", mock.Anything).Return(msgprocessor.NormalMsg, nil) 3401 support.On("ProcessNormalMsg", mock.Anything).Return(uint64(0), nil) 3402 support.On("BlockCutter").Return(blockcutter) 3403 support.On("CreateNextBlock", mock.Anything).Return(&cb.Block{}) 3404 support.On("Serialize", []byte("creator"), nil) 3405 3406 // test message that will be returned by mock brokers 3407 testMsg := sarama.ByteEncoder(protoutil.MarshalOrPanic( 3408 newRegularMessage(protoutil.MarshalOrPanic(&cb.Envelope{ 3409 Payload: protoutil.MarshalOrPanic(&cb.Payload{ 3410 Header: &cb.Header{ 3411 ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ 3412 ChannelId: topic, 3413 }), 3414 }, 3415 Data: []byte("TEST_DATA"), 3416 }), 3417 })), 3418 )) 3419 3420 return &testEnvironment{ 3421 channelID: channelID, 3422 topic: topic, 3423 partition: partition, 3424 height: height, 3425 nextOffset: nextOffset, 3426 support: support, 3427 broker0: broker0, 3428 broker1: broker1, 3429 broker2: broker2, 3430 testMsg: testMsg, 3431 } 3432 } 3433 3434 // BrokerDeath simulates the partition leader dying and a 3435 // second broker becoming the leader before the deliver session times out. 3436 t.Run("BrokerDeath", func(t *testing.T) { 3437 // initialize test environment 3438 env := newTestEnvironment(t) 3439 3440 // broker1 will be closed within the test 3441 defer env.broker0.Close() 3442 defer env.broker2.Close() 3443 3444 // initialize consenter 3445 consenter, _ := New(mockLocalConfig.Kafka, &disabled.Provider{}, &mockkafka.HealthChecker{}, nil, func(string) {}) 3446 3447 // initialize chain 3448 metadata := &cb.Metadata{Value: protoutil.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: env.height})} 3449 chain, err := consenter.HandleChain(env.support, metadata) 3450 if err != nil { 3451 t.Fatal(err) 3452 } 3453 3454 // start the chain, and wait for it to settle down 3455 chain.Start() 3456 select { 3457 case <-chain.(*chainImpl).startChan: 3458 logger.Debug("chain started") 3459 case <-time.After(shortTimeout): 3460 t.Fatal("chain should have started by now") 3461 } 3462 3463 // direct blocks to this channel 3464 blocks := make(chan *cb.Block, 1) 3465 env.support.On("WriteBlock", mock.Anything, mock.Anything).Return().Run(func(arg1 mock.Arguments) { 3466 blocks <- arg1.Get(0).(*cb.Block) 3467 }) 3468 3469 // send a few messages from broker1 3470 fetchResponse1 := sarama.NewMockFetchResponse(t, 1) 3471 for i := 0; i < 5; i++ { 3472 fetchResponse1.SetMessage(env.topic, env.partition, env.nextOffset, env.testMsg) 3473 env.nextOffset++ 3474 } 3475 env.broker1.SetHandlerByMap(map[string]sarama.MockResponse{ 3476 "FetchRequest": fetchResponse1, 3477 }) 3478 3479 logger.Debug("Waiting for messages from broker1") 3480 for i := 0; i < 5; i++ { 3481 select { 3482 case <-blocks: 3483 case <-time.After(shortTimeout): 3484 t.Fatalf("timed out waiting for messages (receieved %d messages)", i) 3485 } 3486 } 3487 3488 // prepare broker2 to send a few messages 3489 fetchResponse2 := sarama.NewMockFetchResponse(t, 1) 3490 for i := 0; i < 5; i++ { 3491 fetchResponse2.SetMessage(env.topic, env.partition, env.nextOffset, env.testMsg) 3492 env.nextOffset++ 3493 } 3494 3495 env.broker2.SetHandlerByMap(map[string]sarama.MockResponse{ 3496 "ProduceRequest": sarama.NewMockProduceResponse(t). 3497 SetError(env.topic, env.partition, sarama.ErrNoError), 3498 "FetchRequest": fetchResponse2, 3499 }) 3500 3501 // shutdown broker1 3502 env.broker1.Close() 3503 3504 // prepare broker0 to respond that broker2 is now the leader 3505 env.broker0.SetHandlerByMap(map[string]sarama.MockResponse{ 3506 "MetadataRequest": sarama.NewMockMetadataResponse(t). 3507 SetLeader(env.topic, env.partition, env.broker2.BrokerID()), 3508 }) 3509 3510 logger.Debug("Waiting for messages from broker2") 3511 for i := 0; i < 5; i++ { 3512 select { 3513 case <-blocks: 3514 case <-time.After(shortTimeout): 3515 t.Fatalf("timed out waiting for messages (receieved %d messages)", i) 3516 } 3517 } 3518 3519 chain.Halt() 3520 }) 3521 3522 // An ErrOffsetOutOfRange is non-recoverable 3523 t.Run("ErrOffsetOutOfRange", func(t *testing.T) { 3524 // initialize test environment 3525 env := newTestEnvironment(t) 3526 3527 // broker cleanup 3528 defer env.broker2.Close() 3529 defer env.broker1.Close() 3530 defer env.broker0.Close() 3531 3532 // initialize consenter 3533 consenter, _ := New(mockLocalConfig.Kafka, &disabled.Provider{}, &mockkafka.HealthChecker{}, nil, func(string) {}) 3534 3535 // initialize chain 3536 metadata := &cb.Metadata{Value: protoutil.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: env.height})} 3537 chain, err := consenter.HandleChain(env.support, metadata) 3538 if err != nil { 3539 t.Fatal(err) 3540 } 3541 3542 // start the chain, and wait for it to settle down 3543 chain.Start() 3544 select { 3545 case <-chain.(*chainImpl).startChan: 3546 logger.Debug("chain started") 3547 case <-time.After(shortTimeout): 3548 t.Fatal("chain should have started by now") 3549 } 3550 3551 // direct blocks to this channel 3552 blocks := make(chan *cb.Block, 1) 3553 env.support.On("WriteBlock", mock.Anything, mock.Anything).Return().Run(func(arg1 mock.Arguments) { 3554 blocks <- arg1.Get(0).(*cb.Block) 3555 }) 3556 3557 // set broker1 to respond to two fetch requests: 3558 // - The first fetch request will get an ErrOffsetOutOfRange error response. 3559 // - The second fetch request will get a valid (i.e. non-error) response. 3560 fetchResponse := &sarama.FetchResponse{} 3561 fetchResponse.AddError(env.topic, env.partition, sarama.ErrOffsetOutOfRange) 3562 fetchResponse.AddMessage(env.topic, env.partition, nil, env.testMsg, env.nextOffset) 3563 env.nextOffset++ 3564 env.broker1.SetHandlerByMap(map[string]sarama.MockResponse{ 3565 "FetchRequest": sarama.NewMockWrapper(fetchResponse), 3566 // answers for CONNECT message 3567 "ProduceRequest": sarama.NewMockProduceResponse(t). 3568 SetError(env.topic, env.partition, sarama.ErrNoError), 3569 }) 3570 3571 select { 3572 case <-blocks: 3573 // the valid fetch response should not of been fetched 3574 t.Fatal("Did not expect new blocks") 3575 case <-time.After(shortTimeout): 3576 t.Fatal("Errored() should have closed by now") 3577 case <-chain.Errored(): 3578 } 3579 3580 chain.Halt() 3581 }) 3582 3583 // test chain timeout 3584 t.Run("DeliverSessionTimedOut", func(t *testing.T) { 3585 // initialize test environment 3586 env := newTestEnvironment(t) 3587 3588 // broker cleanup 3589 defer env.broker2.Close() 3590 defer env.broker1.Close() 3591 defer env.broker0.Close() 3592 3593 // initialize consenter 3594 consenter, _ := New(mockLocalConfig.Kafka, &disabled.Provider{}, &mockkafka.HealthChecker{}, nil, func(string) {}) 3595 3596 // initialize chain 3597 metadata := &cb.Metadata{Value: protoutil.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: env.height})} 3598 chain, err := consenter.HandleChain(env.support, metadata) 3599 if err != nil { 3600 t.Fatal(err) 3601 } 3602 3603 // start the chain, and wait for it to settle down 3604 chain.Start() 3605 select { 3606 case <-chain.(*chainImpl).startChan: 3607 logger.Debug("chain started") 3608 case <-time.After(shortTimeout): 3609 t.Fatal("chain should have started by now") 3610 } 3611 3612 // direct blocks to this channel 3613 blocks := make(chan *cb.Block, 1) 3614 env.support.On("WriteBlock", mock.Anything, mock.Anything).Return().Run(func(arg1 mock.Arguments) { 3615 blocks <- arg1.Get(0).(*cb.Block) 3616 }) 3617 3618 metadataResponse := new(sarama.MetadataResponse) 3619 metadataResponse.AddTopicPartition(env.topic, env.partition, -1, []int32{}, []int32{}, sarama.ErrBrokerNotAvailable) 3620 3621 // configure seed broker to return error on metadata request, otherwise the 3622 // consumer client will keep 'subscribing' successfully to the topic/partition 3623 env.broker0.SetHandlerByMap(map[string]sarama.MockResponse{ 3624 "MetadataRequest": sarama.NewMockWrapper(metadataResponse), 3625 }) 3626 3627 // set broker1 to return an error. 3628 // Note that the following are not considered errors from the sarama client 3629 // consumer's point of view: 3630 // - ErrUnknownTopicOrPartition 3631 // - ErrNotLeaderForPartition 3632 // - ErrLeaderNotAvailable 3633 // - ErrReplicaNotAvailable: 3634 fetchResponse := &sarama.FetchResponse{} 3635 fetchResponse.AddError(env.topic, env.partition, sarama.ErrUnknown) 3636 env.broker1.SetHandlerByMap(map[string]sarama.MockResponse{ 3637 "FetchRequest": sarama.NewMockWrapper(fetchResponse), 3638 // answers for CONNECT message 3639 "ProduceRequest": sarama.NewMockProduceResponse(t). 3640 SetError(env.topic, env.partition, sarama.ErrNoError), 3641 }) 3642 3643 select { 3644 case <-blocks: 3645 t.Fatal("Did not expect new blocks") 3646 case <-time.After(mockRetryOptions.NetworkTimeouts.ReadTimeout + shortTimeout): 3647 t.Fatal("Errored() should have closed by now") 3648 case <-chain.Errored(): 3649 t.Log("Errored() closed") 3650 } 3651 3652 chain.Halt() 3653 }) 3654 } 3655 3656 func TestHealthCheck(t *testing.T) { 3657 gt := NewGomegaWithT(t) 3658 var err error 3659 3660 ch := newChannel("mockChannelFoo", defaultPartition) 3661 mockSyncProducer := &mockkafka.SyncProducer{} 3662 chain := &chainImpl{ 3663 channel: ch, 3664 producer: mockSyncProducer, 3665 } 3666 3667 err = chain.HealthCheck(context.Background()) 3668 gt.Expect(err).NotTo(HaveOccurred()) 3669 gt.Expect(mockSyncProducer.SendMessageCallCount()).To(Equal(1)) 3670 3671 payload := protoutil.MarshalOrPanic(newConnectMessage()) 3672 message := newProducerMessage(chain.channel, payload) 3673 gt.Expect(mockSyncProducer.SendMessageArgsForCall(0)).To(Equal(message)) 3674 3675 // Only return error if the error is not for enough replicas 3676 mockSyncProducer.SendMessageReturns(int32(1), int64(1), sarama.ErrNotEnoughReplicas) 3677 chain.replicaIDs = []int32{int32(1), int32(2)} 3678 err = chain.HealthCheck(context.Background()) 3679 gt.Expect(err).To(MatchError(fmt.Sprintf("[replica ids: [1 2]]: %s", sarama.ErrNotEnoughReplicas.Error()))) 3680 gt.Expect(mockSyncProducer.SendMessageCallCount()).To(Equal(2)) 3681 3682 // If another type of error is returned, it should be ignored by health check 3683 mockSyncProducer.SendMessageReturns(int32(1), int64(1), errors.New("error occurred")) 3684 err = chain.HealthCheck(context.Background()) 3685 gt.Expect(err).NotTo(HaveOccurred()) 3686 gt.Expect(mockSyncProducer.SendMessageCallCount()).To(Equal(3)) 3687 } 3688 3689 type mockReceiver struct { 3690 mock.Mock 3691 } 3692 3693 func (r *mockReceiver) Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, pending bool) { 3694 args := r.Called(msg) 3695 return args.Get(0).([][]*cb.Envelope), args.Bool(1) 3696 } 3697 3698 func (r *mockReceiver) Cut() []*cb.Envelope { 3699 args := r.Called() 3700 return args.Get(0).([]*cb.Envelope) 3701 } 3702 3703 type mockConsenterSupport struct { 3704 mock.Mock 3705 } 3706 3707 func (c *mockConsenterSupport) Block(seq uint64) *cb.Block { 3708 return nil 3709 } 3710 3711 func (c *mockConsenterSupport) VerifyBlockSignature([]*protoutil.SignedData, *cb.ConfigEnvelope) error { 3712 return nil 3713 } 3714 3715 func (c *mockConsenterSupport) NewSignatureHeader() (*cb.SignatureHeader, error) { 3716 args := c.Called() 3717 return args.Get(0).(*cb.SignatureHeader), args.Error(1) 3718 } 3719 3720 func (c *mockConsenterSupport) Sign(message []byte) ([]byte, error) { 3721 args := c.Called(message) 3722 return args.Get(0).([]byte), args.Error(1) 3723 } 3724 3725 func (c *mockConsenterSupport) Serialize() ([]byte, error) { 3726 args := c.Called() 3727 return args.Get(0).([]byte), args.Error(1) 3728 } 3729 3730 func (c *mockConsenterSupport) ClassifyMsg(chdr *cb.ChannelHeader) msgprocessor.Classification { 3731 args := c.Called(chdr) 3732 return args.Get(0).(msgprocessor.Classification) 3733 } 3734 3735 func (c *mockConsenterSupport) ProcessNormalMsg(env *cb.Envelope) (configSeq uint64, err error) { 3736 args := c.Called(env) 3737 return args.Get(0).(uint64), args.Error(1) 3738 } 3739 3740 func (c *mockConsenterSupport) ProcessConfigUpdateMsg(env *cb.Envelope) (config *cb.Envelope, configSeq uint64, err error) { 3741 args := c.Called(env) 3742 return args.Get(0).(*cb.Envelope), args.Get(1).(uint64), args.Error(2) 3743 } 3744 3745 func (c *mockConsenterSupport) ProcessConfigMsg(env *cb.Envelope) (config *cb.Envelope, configSeq uint64, err error) { 3746 args := c.Called(env) 3747 return args.Get(0).(*cb.Envelope), args.Get(1).(uint64), args.Error(2) 3748 } 3749 3750 func (c *mockConsenterSupport) BlockCutter() blockcutter.Receiver { 3751 args := c.Called() 3752 return args.Get(0).(blockcutter.Receiver) 3753 } 3754 3755 func (c *mockConsenterSupport) SharedConfig() channelconfig.Orderer { 3756 args := c.Called() 3757 return args.Get(0).(channelconfig.Orderer) 3758 } 3759 3760 func (c *mockConsenterSupport) ChannelConfig() channelconfig.Channel { 3761 args := c.Called() 3762 return args.Get(0).(channelconfig.Channel) 3763 } 3764 3765 func (c *mockConsenterSupport) CreateNextBlock(messages []*cb.Envelope) *cb.Block { 3766 args := c.Called(messages) 3767 return args.Get(0).(*cb.Block) 3768 } 3769 3770 func (c *mockConsenterSupport) WriteBlock(block *cb.Block, encodedMetadataValue []byte) { 3771 c.Called(block, encodedMetadataValue) 3772 } 3773 3774 func (c *mockConsenterSupport) WriteConfigBlock(block *cb.Block, encodedMetadataValue []byte) { 3775 c.Called(block, encodedMetadataValue) 3776 } 3777 3778 func (c *mockConsenterSupport) Sequence() uint64 { 3779 args := c.Called() 3780 return args.Get(0).(uint64) 3781 } 3782 3783 func (c *mockConsenterSupport) ChannelID() string { 3784 args := c.Called() 3785 return args.String(0) 3786 } 3787 3788 func (c *mockConsenterSupport) Height() uint64 { 3789 args := c.Called() 3790 return args.Get(0).(uint64) 3791 } 3792 3793 func (c *mockConsenterSupport) Append(block *cb.Block) error { 3794 c.Called(block) 3795 return nil 3796 }