github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/batch/batch_manager_test.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in comdiliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or imdilied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package batch 16 17 import ( 18 "context" 19 "crypto/sha256" 20 "encoding/hex" 21 "fmt" 22 "testing" 23 "time" 24 25 "github.com/kaleido-io/firefly/internal/config" 26 "github.com/kaleido-io/firefly/internal/log" 27 "github.com/kaleido-io/firefly/mocks/databasemocks" 28 "github.com/kaleido-io/firefly/mocks/datamocks" 29 "github.com/kaleido-io/firefly/pkg/database" 30 "github.com/kaleido-io/firefly/pkg/fftypes" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/mock" 33 ) 34 35 func TestE2EDispatchBroadcast(t *testing.T) { 36 log.SetLevel("debug") 37 38 mdi := &databasemocks.Plugin{} 39 mdm := &datamocks.Manager{} 40 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(nil, nil).Once() 41 mdi.On("UpsertOffset", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 42 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 43 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(&fftypes.Offset{ 44 ID: fftypes.NewUUID(), 45 }, nil) 46 readyForDispatch := make(chan bool) 47 waitForDispatch := make(chan *fftypes.Batch) 48 handler := func(ctx context.Context, b *fftypes.Batch, s []*fftypes.Bytes32) error { 49 _, ok := <-readyForDispatch 50 if !ok { 51 return nil 52 } 53 assert.Len(t, s, 2) 54 h := sha256.New() 55 nonceBytes, _ := hex.DecodeString( 56 "746f70696331", 57 /*| topic1 | */ 58 ) // little endian 12345 in 8 byte hex 59 h.Write(nonceBytes) 60 assert.Equal(t, hex.EncodeToString(h.Sum([]byte{})), s[0].String()) 61 62 h = sha256.New() 63 nonceBytes, _ = hex.DecodeString( 64 "746f70696332", 65 /*| topic2 | */ 66 ) // little endian 12345 in 8 byte hex 67 h.Write(nonceBytes) 68 assert.Equal(t, hex.EncodeToString(h.Sum([]byte{})), s[1].String()) 69 70 waitForDispatch <- b 71 return nil 72 } 73 ctx, cancel := context.WithCancel(context.Background()) 74 bmi, _ := NewBatchManager(ctx, mdi, mdm) 75 bm := bmi.(*batchManager) 76 77 bm.RegisterDispatcher([]fftypes.MessageType{fftypes.MessageTypeBroadcast}, handler, Options{ 78 BatchMaxSize: 2, 79 BatchTimeout: 0, 80 DisposeTimeout: 120 * time.Second, 81 }) 82 83 dataID1 := fftypes.NewUUID() 84 dataHash := fftypes.NewRandB32() 85 msg := &fftypes.Message{ 86 Header: fftypes.MessageHeader{ 87 Type: fftypes.MessageTypeBroadcast, 88 ID: fftypes.NewUUID(), 89 Topics: []string{"topic1", "topic2"}, 90 Namespace: "ns1", 91 Author: "0x12345", 92 }, 93 Data: fftypes.DataRefs{ 94 {ID: dataID1, Hash: dataHash}, 95 }, 96 } 97 data := &fftypes.Data{ 98 ID: dataID1, 99 Hash: dataHash, 100 } 101 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return([]*fftypes.Data{data}, true, nil) 102 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{msg}, nil).Once() 103 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{}, nil) 104 mdi.On("UpsertBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 105 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 106 rag := mdi.On("RunAsGroup", mock.Anything, mock.Anything, mock.Anything).Return(nil) 107 rag.RunFn = func(a mock.Arguments) { 108 ctx := a.Get(0).(context.Context) 109 fn := a.Get(1).(func(context.Context) error) 110 fn(ctx) 111 } 112 mdi.On("UpdateMessages", mock.Anything, mock.MatchedBy(func(f database.Filter) bool { 113 fi, err := f.Finalize() 114 assert.NoError(t, err) 115 assert.Equal(t, fmt.Sprintf("id IN ['%s']", msg.Header.ID.String()), fi.String()) 116 return true 117 }), mock.Anything).Return(nil) 118 119 err := bm.Start() 120 assert.NoError(t, err) 121 122 bm.NewMessages() <- msg.Sequence 123 124 readyForDispatch <- true 125 b := <-waitForDispatch 126 assert.Equal(t, *msg.Header.ID, *b.Payload.Messages[0].Header.ID) 127 assert.Equal(t, *data.ID, *b.Payload.Data[0].ID) 128 129 // Wait until everything closes 130 close(readyForDispatch) 131 cancel() 132 bm.WaitStop() 133 134 } 135 136 func TestE2EDispatchPrivate(t *testing.T) { 137 log.SetLevel("debug") 138 139 mdi := &databasemocks.Plugin{} 140 mdm := &datamocks.Manager{} 141 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(nil, nil).Once() 142 mdi.On("UpsertOffset", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 143 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 144 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(&fftypes.Offset{ 145 ID: fftypes.NewUUID(), 146 }, nil) 147 readyForDispatch := make(chan bool) 148 waitForDispatch := make(chan *fftypes.Batch) 149 var groupID fftypes.Bytes32 150 _ = groupID.UnmarshalText([]byte("44dc0861e69d9bab17dd5e90a8898c2ea156ad04e5fabf83119cc010486e6c1b")) 151 handler := func(ctx context.Context, b *fftypes.Batch, s []*fftypes.Bytes32) error { 152 _, ok := <-readyForDispatch 153 if !ok { 154 return nil 155 } 156 assert.Len(t, s, 2) 157 h := sha256.New() 158 nonceBytes, _ := hex.DecodeString( 159 "746f70696331" + "44dc0861e69d9bab17dd5e90a8898c2ea156ad04e5fabf83119cc010486e6c1b" + "30783132333435" + "0000000000003039", 160 /*| topic1 | | ---- group id -------------------------------------------------| |author'0x12345'| |i64 nonce (12345) */ 161 /*| context | | sender + nonce */ 162 ) // little endian 12345 in 8 byte hex 163 h.Write(nonceBytes) 164 assert.Equal(t, hex.EncodeToString(h.Sum([]byte{})), s[0].String()) 165 166 h = sha256.New() 167 nonceBytes, _ = hex.DecodeString( 168 "746f70696332" + "44dc0861e69d9bab17dd5e90a8898c2ea156ad04e5fabf83119cc010486e6c1b" + "30783132333435" + "000000000000303a", 169 /*| topic2 | | ---- group id -------------------------------------------------| |author'0x12345'| |i64 nonce (12346) */ 170 /*| context | | sender + nonce */ 171 ) // little endian 12345 in 8 byte hex 172 h.Write(nonceBytes) 173 assert.Equal(t, hex.EncodeToString(h.Sum([]byte{})), s[1].String()) 174 waitForDispatch <- b 175 return nil 176 } 177 ctx, cancel := context.WithCancel(context.Background()) 178 bmi, _ := NewBatchManager(ctx, mdi, mdm) 179 bm := bmi.(*batchManager) 180 181 bm.RegisterDispatcher([]fftypes.MessageType{fftypes.MessageTypePrivate}, handler, Options{ 182 BatchMaxSize: 2, 183 BatchTimeout: 0, 184 DisposeTimeout: 120 * time.Second, 185 }) 186 187 dataID1 := fftypes.NewUUID() 188 dataHash := fftypes.NewRandB32() 189 msg := &fftypes.Message{ 190 Header: fftypes.MessageHeader{ 191 Type: fftypes.MessageTypePrivate, 192 ID: fftypes.NewUUID(), 193 Topics: []string{"topic1", "topic2"}, 194 Namespace: "ns1", 195 Author: "0x12345", 196 Group: &groupID, 197 }, 198 Data: fftypes.DataRefs{ 199 {ID: dataID1, Hash: dataHash}, 200 }, 201 } 202 data := &fftypes.Data{ 203 ID: dataID1, 204 Hash: dataHash, 205 } 206 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return([]*fftypes.Data{data}, true, nil) 207 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{msg}, nil).Once() 208 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{}, nil) 209 mdi.On("UpsertBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 210 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 211 rag := mdi.On("RunAsGroup", mock.Anything, mock.Anything, mock.Anything).Return(nil) 212 rag.RunFn = func(a mock.Arguments) { 213 ctx := a.Get(0).(context.Context) 214 fn := a.Get(1).(func(context.Context) error) 215 fn(ctx) 216 } 217 mdi.On("UpdateMessages", mock.Anything, mock.MatchedBy(func(f database.Filter) bool { 218 fi, err := f.Finalize() 219 assert.NoError(t, err) 220 assert.Equal(t, fmt.Sprintf("id IN ['%s']", msg.Header.ID.String()), fi.String()) 221 return true 222 }), mock.Anything).Return(nil) 223 ugcn := mdi.On("UpsertNonceNext", mock.Anything, mock.Anything).Return(nil) 224 nextNonce := int64(12345) 225 ugcn.RunFn = func(a mock.Arguments) { 226 a[1].(*fftypes.Nonce).Nonce = nextNonce 227 nextNonce++ 228 } 229 230 err := bm.Start() 231 assert.NoError(t, err) 232 233 bm.NewMessages() <- msg.Sequence 234 235 readyForDispatch <- true 236 b := <-waitForDispatch 237 assert.Equal(t, *msg.Header.ID, *b.Payload.Messages[0].Header.ID) 238 assert.Equal(t, *data.ID, *b.Payload.Data[0].ID) 239 240 // Wait until everything closes 241 close(readyForDispatch) 242 cancel() 243 bm.WaitStop() 244 245 } 246 func TestInitFailNoPersistence(t *testing.T) { 247 _, err := NewBatchManager(context.Background(), nil, nil) 248 assert.Error(t, err) 249 } 250 251 func TestInitRestoreExistingOffset(t *testing.T) { 252 mdi := &databasemocks.Plugin{} 253 mdm := &datamocks.Manager{} 254 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(&fftypes.Offset{ 255 Type: fftypes.OffsetTypeBatch, 256 Namespace: fftypes.SystemNamespace, 257 Name: msgBatchOffsetName, 258 Current: 12345, 259 }, nil) 260 bm, err := NewBatchManager(context.Background(), mdi, mdm) 261 assert.NoError(t, err) 262 defer bm.Close() 263 err = bm.Start() 264 assert.NoError(t, err) 265 assert.Equal(t, int64(12345), bm.(*batchManager).offset) 266 } 267 268 func TestInitFailCannotRestoreOffset(t *testing.T) { 269 mdi := &databasemocks.Plugin{} 270 mdm := &datamocks.Manager{} 271 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(nil, fmt.Errorf("pop")) 272 bm, err := NewBatchManager(context.Background(), mdi, mdm) 273 assert.NoError(t, err) 274 defer bm.Close() 275 bm.(*batchManager).retry.MaximumDelay = 1 * time.Microsecond 276 err = bm.Start() 277 assert.Regexp(t, "pop", err) 278 } 279 280 func TestInitFailCannotCreateOffset(t *testing.T) { 281 mdi := &databasemocks.Plugin{} 282 mdm := &datamocks.Manager{} 283 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(nil, nil).Once() 284 mdi.On("UpsertOffset", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 285 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(nil, fmt.Errorf("pop")) 286 bm, err := NewBatchManager(context.Background(), mdi, mdm) 287 assert.NoError(t, err) 288 defer bm.Close() 289 bm.(*batchManager).retry.MaximumDelay = 1 * time.Microsecond 290 err = bm.Start() 291 assert.Regexp(t, "pop", err) 292 } 293 294 func TestGetInvalidBatchTypeMsg(t *testing.T) { 295 296 mdi := &databasemocks.Plugin{} 297 mdm := &datamocks.Manager{} 298 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeBatch, fftypes.SystemNamespace, msgBatchOffsetName).Return(&fftypes.Offset{ 299 Current: 12345, 300 }, nil) 301 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 302 defer bm.Close() 303 msg := &fftypes.Message{Header: fftypes.MessageHeader{}} 304 err := bm.(*batchManager).dispatchMessage(nil, msg) 305 assert.Regexp(t, "FF10126", err) 306 } 307 308 func TestMessageSequencerCancelledContext(t *testing.T) { 309 mdi := &databasemocks.Plugin{} 310 mdm := &datamocks.Manager{} 311 mdi.On("GetMessages", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 312 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 313 defer bm.Close() 314 ctx, cancel := context.WithCancel(context.Background()) 315 cancel() 316 bm.(*batchManager).ctx = ctx 317 bm.(*batchManager).messageSequencer() 318 assert.Equal(t, 1, len(mdi.Calls)) 319 } 320 321 func TestMessageSequencerMissingMessageData(t *testing.T) { 322 mdi := &databasemocks.Plugin{} 323 mdm := &datamocks.Manager{} 324 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 325 326 dataID := fftypes.NewUUID() 327 gmMock := mdi.On("GetMessages", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Message{ 328 { 329 Header: fftypes.MessageHeader{ 330 ID: fftypes.NewUUID(), 331 Namespace: "ns1", 332 }, 333 Data: []*fftypes.DataRef{ 334 {ID: dataID}, 335 }}, 336 }, nil) 337 gmMock.RunFn = func(a mock.Arguments) { 338 bm.Close() // so we only go round once 339 } 340 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return(nil, false, nil) 341 342 bm.(*batchManager).messageSequencer() 343 mdi.AssertExpectations(t) 344 mdm.AssertExpectations(t) 345 } 346 347 func TestMessageSequencerDispatchFail(t *testing.T) { 348 mdi := &databasemocks.Plugin{} 349 mdm := &datamocks.Manager{} 350 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 351 352 dataID := fftypes.NewUUID() 353 gmMock := mdi.On("GetMessages", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Message{ 354 { 355 Header: fftypes.MessageHeader{ 356 ID: fftypes.NewUUID(), 357 Type: fftypes.MessageTypePrivate, 358 Namespace: "ns1", 359 }, 360 Data: []*fftypes.DataRef{ 361 {ID: dataID}, 362 }}, 363 }, nil) 364 gmMock.RunFn = func(a mock.Arguments) { 365 bm.Close() // so we only go round once 366 } 367 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return([]*fftypes.Data{{ID: dataID}}, true, nil) 368 369 bm.(*batchManager).messageSequencer() 370 mdi.AssertExpectations(t) 371 mdm.AssertExpectations(t) 372 } 373 374 func TestMessageSequencerUpdateMessagesFail(t *testing.T) { 375 mdi := &databasemocks.Plugin{} 376 mdm := &datamocks.Manager{} 377 ctx, cancelCtx := context.WithCancel(context.Background()) 378 bm, _ := NewBatchManager(ctx, mdi, mdm) 379 bm.RegisterDispatcher([]fftypes.MessageType{fftypes.MessageTypeBroadcast}, func(c context.Context, b *fftypes.Batch, s []*fftypes.Bytes32) error { 380 return nil 381 }, Options{BatchMaxSize: 1, DisposeTimeout: 0}) 382 383 dataID := fftypes.NewUUID() 384 mdi.On("GetMessages", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Message{ 385 { 386 Header: fftypes.MessageHeader{ 387 ID: fftypes.NewUUID(), 388 Type: fftypes.MessageTypeBroadcast, 389 Namespace: "ns1", 390 }, 391 Data: []*fftypes.DataRef{ 392 {ID: dataID}, 393 }}, 394 }, nil) 395 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return([]*fftypes.Data{{ID: dataID}}, true, nil) 396 mdi.On("UpdateMessages", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("fizzle")) 397 rag := mdi.On("RunAsGroup", mock.Anything, mock.Anything, mock.Anything) 398 rag.RunFn = func(a mock.Arguments) { 399 ctx := a.Get(0).(context.Context) 400 fn := a.Get(1).(func(context.Context) error) 401 err := fn(ctx).(error) 402 if err != nil && err.Error() == "fizzle" { 403 cancelCtx() // so we only go round once 404 bm.Close() 405 } 406 rag.ReturnArguments = mock.Arguments{err} 407 } 408 409 bm.(*batchManager).messageSequencer() 410 mdi.AssertExpectations(t) 411 mdm.AssertExpectations(t) 412 } 413 414 func TestMessageSequencerUpdateBatchFail(t *testing.T) { 415 mdi := &databasemocks.Plugin{} 416 mdm := &datamocks.Manager{} 417 ctx, cancelCtx := context.WithCancel(context.Background()) 418 bm, _ := NewBatchManager(ctx, mdi, mdm) 419 bm.RegisterDispatcher([]fftypes.MessageType{fftypes.MessageTypeBroadcast}, func(c context.Context, b *fftypes.Batch, s []*fftypes.Bytes32) error { 420 return nil 421 }, Options{BatchMaxSize: 1, DisposeTimeout: 0}) 422 423 dataID := fftypes.NewUUID() 424 mdi.On("GetMessages", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Message{ 425 { 426 Header: fftypes.MessageHeader{ 427 ID: fftypes.NewUUID(), 428 Type: fftypes.MessageTypeBroadcast, 429 Namespace: "ns1", 430 }, 431 Data: []*fftypes.DataRef{ 432 {ID: dataID}, 433 }}, 434 }, nil) 435 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return([]*fftypes.Data{{ID: dataID}}, true, nil) 436 mdi.On("UpdateMessages", mock.Anything, mock.Anything, mock.Anything).Return(nil) 437 mdi.On("UpsertBatch", mock.Anything, mock.Anything, true, mock.Anything).Return(fmt.Errorf("fizzle")) 438 rag := mdi.On("RunAsGroup", mock.Anything, mock.Anything, mock.Anything) 439 rag.RunFn = func(a mock.Arguments) { 440 ctx := a.Get(0).(context.Context) 441 fn := a.Get(1).(func(context.Context) error) 442 err := fn(ctx).(error) 443 if err != nil && err.Error() == "fizzle" { 444 cancelCtx() // so we only go round once 445 bm.Close() 446 } 447 rag.ReturnArguments = mock.Arguments{err} 448 } 449 450 bm.(*batchManager).messageSequencer() 451 mdi.AssertExpectations(t) 452 mdm.AssertExpectations(t) 453 } 454 455 func TestWaitForPollTimeout(t *testing.T) { 456 mdi := &databasemocks.Plugin{} 457 mdm := &datamocks.Manager{} 458 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 459 bm.(*batchManager).messagePollTimeout = 1 * time.Microsecond 460 bm.(*batchManager).waitForShoulderTapOrPollTimeout() 461 } 462 463 func TestWaitConsumesMessagesAndDoesNotBlock(t *testing.T) { 464 config.Reset() 465 mdi := &databasemocks.Plugin{} 466 mdm := &datamocks.Manager{} 467 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 468 go bm.(*batchManager).newEventNotifications() 469 for i := 0; i < int(bm.(*batchManager).readPageSize); i++ { 470 bm.NewMessages() <- 12345 471 } 472 // And should generate a shoulder tap 473 <-bm.(*batchManager).shoulderTap 474 bm.Close() 475 } 476 477 func TestAssembleMessageDataNilData(t *testing.T) { 478 mdi := &databasemocks.Plugin{} 479 mdm := &datamocks.Manager{} 480 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 481 bm.Close() 482 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return(nil, false, nil) 483 _, err := bm.(*batchManager).assembleMessageData(&fftypes.Message{ 484 Header: fftypes.MessageHeader{ 485 ID: fftypes.NewUUID(), 486 }, 487 Data: fftypes.DataRefs{{ID: nil}}, 488 }) 489 assert.Regexp(t, "FF10133", err) 490 } 491 492 func TestAssembleMessageDataClosed(t *testing.T) { 493 mdi := &databasemocks.Plugin{} 494 mdm := &datamocks.Manager{} 495 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 496 bm.(*batchManager).retry.MaximumDelay = 1 * time.Microsecond 497 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 498 err := bm.(*batchManager).updateOffset(false, 10) 499 assert.EqualError(t, err, "pop") 500 } 501 502 func TestGetMessageDataFail(t *testing.T) { 503 mdi := &databasemocks.Plugin{} 504 mdm := &datamocks.Manager{} 505 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 506 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return(nil, false, fmt.Errorf("pop")) 507 bm.Close() 508 _, err := bm.(*batchManager).assembleMessageData(&fftypes.Message{ 509 Header: fftypes.MessageHeader{ 510 ID: fftypes.NewUUID(), 511 }, 512 Data: fftypes.DataRefs{ 513 {ID: fftypes.NewUUID(), Hash: fftypes.NewRandB32()}, 514 }, 515 }) 516 assert.EqualError(t, err, "pop") 517 } 518 519 func TestGetMessageNotFound(t *testing.T) { 520 mdi := &databasemocks.Plugin{} 521 mdm := &datamocks.Manager{} 522 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 523 mdm.On("GetMessageData", mock.Anything, mock.Anything, true).Return(nil, false, nil) 524 bm.Close() 525 _, err := bm.(*batchManager).assembleMessageData(&fftypes.Message{ 526 Header: fftypes.MessageHeader{ 527 ID: fftypes.NewUUID(), 528 }, 529 Data: fftypes.DataRefs{ 530 {ID: fftypes.NewUUID(), Hash: fftypes.NewRandB32()}, 531 }, 532 }) 533 assert.Regexp(t, "FF10133", err) 534 } 535 536 func TestWaitForShoulderTap(t *testing.T) { 537 mdi := &databasemocks.Plugin{} 538 mdm := &datamocks.Manager{} 539 bm, _ := NewBatchManager(context.Background(), mdi, mdm) 540 bm.(*batchManager).shoulderTap <- true 541 bm.(*batchManager).waitForShoulderTapOrPollTimeout() 542 }