github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/events/aggregator_test.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package events 18 19 import ( 20 "context" 21 "crypto/sha256" 22 "fmt" 23 "testing" 24 25 "github.com/kaleido-io/firefly/internal/config" 26 "github.com/kaleido-io/firefly/mocks/broadcastmocks" 27 "github.com/kaleido-io/firefly/mocks/databasemocks" 28 "github.com/kaleido-io/firefly/mocks/datamocks" 29 "github.com/kaleido-io/firefly/mocks/privatemessagingmocks" 30 "github.com/kaleido-io/firefly/pkg/database" 31 "github.com/kaleido-io/firefly/pkg/fftypes" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/mock" 34 ) 35 36 func uuidMatches(id1 *fftypes.UUID) interface{} { 37 return mock.MatchedBy(func(id2 *fftypes.UUID) bool { return id1.Equals(id2) }) 38 } 39 40 func newTestAggregator() (*aggregator, func()) { 41 mdi := &databasemocks.Plugin{} 42 mbm := &broadcastmocks.Manager{} 43 mdm := &datamocks.Manager{} 44 mpm := &privatemessagingmocks.Manager{} 45 ctx, cancel := context.WithCancel(context.Background()) 46 ag := newAggregator(ctx, mdi, mbm, mpm, mdm, newEventNotifier(ctx, "ut")) 47 return ag, cancel 48 } 49 50 func TestAggregationMaskedZeroNonceMatch(t *testing.T) { 51 52 ag, cancel := newTestAggregator() 53 defer cancel() 54 55 // Generate some pin data 56 member1 := "0x12345" 57 member2 := "0x23456" 58 topic := "some-topic" 59 batchID := fftypes.NewUUID() 60 groupID := fftypes.NewRandB32() 61 msgID := fftypes.NewUUID() 62 h := sha256.New() 63 h.Write([]byte(topic)) 64 h.Write((*groupID)[:]) 65 contextUnmasked := fftypes.HashResult(h) 66 member1NonceZero := ag.calcHash(topic, groupID, member1, 0) 67 member2NonceZero := ag.calcHash(topic, groupID, member2, 0) 68 member2NonceOne := ag.calcHash(topic, groupID, member2, 1) 69 70 mdi := ag.database.(*databasemocks.Plugin) 71 mdm := ag.data.(*datamocks.Manager) 72 mpm := ag.messaging.(*privatemessagingmocks.Manager) 73 74 // Get the batch 75 mdi.On("GetBatchByID", ag.ctx, uuidMatches(batchID)).Return(&fftypes.Batch{ 76 ID: batchID, 77 Payload: fftypes.BatchPayload{ 78 Messages: []*fftypes.Message{ 79 { 80 Header: fftypes.MessageHeader{ 81 ID: msgID, 82 Group: groupID, 83 Topics: []string{topic}, 84 Author: member2, 85 }, 86 Pins: []string{member2NonceZero.String()}, 87 Data: fftypes.DataRefs{ 88 {ID: fftypes.NewUUID()}, 89 }, 90 }, 91 }, 92 }, 93 }, nil) 94 // Look for existing nextpins - none found, first on context 95 mdi.On("GetNextPins", ag.ctx, mock.Anything).Return([]*fftypes.NextPin{}, nil).Once() 96 // Get the group members 97 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 98 GroupIdentity: fftypes.GroupIdentity{ 99 Members: fftypes.Members{ 100 {Identity: member1}, 101 {Identity: member2}, 102 }, 103 }, 104 }, nil) 105 // Look for any earlier pins - none found 106 mdi.On("GetPins", ag.ctx, mock.Anything).Return([]*fftypes.Pin{}, nil).Once() 107 // Insert all the zero pins 108 mdi.On("InsertNextPin", ag.ctx, mock.MatchedBy(func(np *fftypes.NextPin) bool { 109 assert.Equal(t, *np.Context, *contextUnmasked) 110 np.Sequence = 10011 111 return *np.Hash == *member1NonceZero && np.Nonce == 0 112 })).Return(nil).Once() 113 mdi.On("InsertNextPin", ag.ctx, mock.MatchedBy(func(np *fftypes.NextPin) bool { 114 assert.Equal(t, *np.Context, *contextUnmasked) 115 np.Sequence = 10012 116 return *np.Hash == *member2NonceZero && np.Nonce == 0 117 })).Return(nil).Once() 118 // Validate the message is ok 119 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 120 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(true, nil) 121 // Insert the confirmed event 122 mdi.On("UpsertEvent", ag.ctx, mock.MatchedBy(func(e *fftypes.Event) bool { 123 return *e.Reference == *msgID && e.Type == fftypes.EventTypeMessageConfirmed 124 }), false).Return(nil) 125 // Update member2 to nonce 1 126 mdi.On("UpdateNextPin", ag.ctx, mock.MatchedBy(func(seq int64) bool { 127 return seq == 10012 128 }), mock.MatchedBy(func(update database.Update) bool { 129 ui, _ := update.Finalize() 130 assert.Equal(t, "nonce", ui.SetOperations[0].Field) 131 v, _ := ui.SetOperations[0].Value.Value() 132 assert.Equal(t, int64(1), v.(int64)) 133 assert.Equal(t, "hash", ui.SetOperations[1].Field) 134 v, _ = ui.SetOperations[1].Value.Value() 135 assert.Equal(t, member2NonceOne.String(), v) 136 return true 137 })).Return(nil) 138 // Set the pin to dispatched 139 mdi.On("SetPinDispatched", ag.ctx, int64(10001)).Return(nil) 140 // Update the message 141 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(nil) 142 // Confirm the offset 143 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 144 145 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 146 { 147 Sequence: 10001, 148 Masked: true, 149 Hash: member2NonceZero, 150 Batch: batchID, 151 Index: 0, 152 Dispatched: false, 153 }, 154 }) 155 assert.NoError(t, err) 156 157 mdi.AssertExpectations(t) 158 mdm.AssertExpectations(t) 159 } 160 161 func TestAggregationMaskedNextSequenceMatch(t *testing.T) { 162 163 ag, cancel := newTestAggregator() 164 defer cancel() 165 166 // Generate some pin data 167 member1 := "0x12345" 168 member2 := "0x23456" 169 topic := "some-topic" 170 batchID := fftypes.NewUUID() 171 groupID := fftypes.NewRandB32() 172 msgID := fftypes.NewUUID() 173 h := sha256.New() 174 h.Write([]byte(topic)) 175 h.Write((*groupID)[:]) 176 contextUnmasked := fftypes.HashResult(h) 177 member1Nonce100 := ag.calcHash(topic, groupID, member1, 100) 178 member2Nonce500 := ag.calcHash(topic, groupID, member2, 500) 179 member2Nonce501 := ag.calcHash(topic, groupID, member2, 501) 180 181 mdi := ag.database.(*databasemocks.Plugin) 182 mdm := ag.data.(*datamocks.Manager) 183 184 // Get the batch 185 mdi.On("GetBatchByID", ag.ctx, uuidMatches(batchID)).Return(&fftypes.Batch{ 186 ID: batchID, 187 Payload: fftypes.BatchPayload{ 188 Messages: []*fftypes.Message{ 189 { 190 Header: fftypes.MessageHeader{ 191 ID: msgID, 192 Group: groupID, 193 Topics: []string{topic}, 194 Author: member2, 195 }, 196 Pins: []string{member2Nonce500.String()}, 197 Data: fftypes.DataRefs{ 198 {ID: fftypes.NewUUID()}, 199 }, 200 }, 201 }, 202 }, 203 }, nil) 204 // Look for existing nextpins - none found, first on context 205 mdi.On("GetNextPins", ag.ctx, mock.Anything).Return([]*fftypes.NextPin{ 206 {Context: contextUnmasked, Identity: member1, Hash: member1Nonce100, Nonce: 100, Sequence: 929}, 207 {Context: contextUnmasked, Identity: member2, Hash: member2Nonce500, Nonce: 500, Sequence: 424}, 208 }, nil).Once() 209 // Validate the message is ok 210 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 211 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(true, nil) 212 // Insert the confirmed event 213 mdi.On("UpsertEvent", ag.ctx, mock.MatchedBy(func(e *fftypes.Event) bool { 214 return *e.Reference == *msgID && e.Type == fftypes.EventTypeMessageConfirmed 215 }), false).Return(nil) 216 // Update member2 to nonce 1 217 mdi.On("UpdateNextPin", ag.ctx, mock.MatchedBy(func(seq int64) bool { 218 return seq == 424 219 }), mock.MatchedBy(func(update database.Update) bool { 220 ui, _ := update.Finalize() 221 assert.Equal(t, "nonce", ui.SetOperations[0].Field) 222 v, _ := ui.SetOperations[0].Value.Value() 223 assert.Equal(t, int64(501), v.(int64)) 224 assert.Equal(t, "hash", ui.SetOperations[1].Field) 225 v, _ = ui.SetOperations[1].Value.Value() 226 assert.Equal(t, member2Nonce501.String(), v) 227 return true 228 })).Return(nil) 229 // Set the pin to dispatched 230 mdi.On("SetPinDispatched", ag.ctx, int64(10001)).Return(nil) 231 // Update the message 232 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(nil) 233 // Confirm the offset 234 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 235 236 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 237 { 238 Sequence: 10001, 239 Masked: true, 240 Hash: member2Nonce500, 241 Batch: batchID, 242 Index: 0, 243 Dispatched: false, 244 }, 245 }) 246 assert.NoError(t, err) 247 248 mdi.AssertExpectations(t) 249 mdm.AssertExpectations(t) 250 } 251 252 func TestAggregationBroadcast(t *testing.T) { 253 254 ag, cancel := newTestAggregator() 255 defer cancel() 256 257 // Generate some pin data 258 topic := "some-topic" 259 batchID := fftypes.NewUUID() 260 msgID := fftypes.NewUUID() 261 h := sha256.New() 262 h.Write([]byte(topic)) 263 contextUnmasked := fftypes.HashResult(h) 264 265 mdi := ag.database.(*databasemocks.Plugin) 266 mdm := ag.data.(*datamocks.Manager) 267 268 // Get the batch 269 member1 := "0x12345" 270 mdi.On("GetBatchByID", ag.ctx, uuidMatches(batchID)).Return(&fftypes.Batch{ 271 ID: batchID, 272 Payload: fftypes.BatchPayload{ 273 Messages: []*fftypes.Message{ 274 { 275 Header: fftypes.MessageHeader{ 276 ID: msgID, 277 Topics: []string{topic}, 278 Author: member1, 279 }, 280 Data: fftypes.DataRefs{ 281 {ID: fftypes.NewUUID()}, 282 }, 283 }, 284 }, 285 }, 286 }, nil) 287 // Do not resolve any pins earlier 288 mdi.On("GetPins", mock.Anything, mock.Anything).Return([]*fftypes.Pin{}, nil) 289 // Validate the message is ok 290 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 291 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(true, nil) 292 // Insert the confirmed event 293 mdi.On("UpsertEvent", ag.ctx, mock.MatchedBy(func(e *fftypes.Event) bool { 294 return *e.Reference == *msgID && e.Type == fftypes.EventTypeMessageConfirmed 295 }), false).Return(nil) 296 // Set the pin to dispatched 297 mdi.On("SetPinDispatched", ag.ctx, int64(10001)).Return(nil) 298 // Update the message 299 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(nil) 300 // Confirm the offset 301 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 302 303 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 304 { 305 Sequence: 10001, 306 Hash: contextUnmasked, 307 Batch: batchID, 308 Index: 0, 309 Dispatched: false, 310 }, 311 }) 312 assert.NoError(t, err) 313 314 mdi.AssertExpectations(t) 315 mdm.AssertExpectations(t) 316 } 317 func TestShutdownOnCancel(t *testing.T) { 318 ag, cancel := newTestAggregator() 319 mdi := ag.database.(*databasemocks.Plugin) 320 mdi.On("GetOffset", mock.Anything, fftypes.OffsetTypeAggregator, fftypes.SystemNamespace, aggregatorOffsetName).Return(&fftypes.Offset{ 321 Type: fftypes.OffsetTypeAggregator, 322 Namespace: fftypes.SystemNamespace, 323 Name: aggregatorOffsetName, 324 Current: 12345, 325 }, nil) 326 mdi.On("GetPins", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Pin{}, nil) 327 err := ag.start() 328 assert.NoError(t, err) 329 assert.Equal(t, int64(12345), ag.eventPoller.pollingOffset) 330 ag.eventPoller.eventNotifier.newEvents <- 12345 331 cancel() 332 <-ag.eventPoller.closed 333 } 334 335 func TestProcessPinsDBGroupFail(t *testing.T) { 336 ag, cancel := newTestAggregator() 337 defer cancel() 338 339 mdi := ag.database.(*databasemocks.Plugin) 340 rag := mdi.On("RunAsGroup", ag.ctx, mock.Anything) 341 rag.RunFn = func(a mock.Arguments) { 342 rag.ReturnArguments = mock.Arguments{ 343 a[1].(func(context.Context) error)(a[0].(context.Context)), 344 } 345 } 346 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) 347 348 _, err := ag.processPinsDBGroup([]fftypes.LocallySequenced{ 349 &fftypes.Pin{ 350 Batch: fftypes.NewUUID(), 351 }, 352 }) 353 assert.Regexp(t, "pop", err) 354 } 355 356 func TestGetPins(t *testing.T) { 357 ag, cancel := newTestAggregator() 358 defer cancel() 359 360 mdi := ag.database.(*databasemocks.Plugin) 361 mdi.On("GetPins", ag.ctx, mock.Anything).Return([]*fftypes.Pin{ 362 {Sequence: 12345}, 363 }, nil) 364 365 lc, err := ag.getPins(ag.ctx, database.EventQueryFactory.NewFilter(ag.ctx).Gte("sequence", 12345)) 366 assert.NoError(t, err) 367 assert.Equal(t, int64(12345), lc[0].LocalSequence()) 368 } 369 370 func TestProcessPinsMissingBatch(t *testing.T) { 371 ag, cancel := newTestAggregator() 372 defer cancel() 373 374 mdi := ag.database.(*databasemocks.Plugin) 375 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(nil, nil) 376 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 377 378 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 379 {Sequence: 12345, Batch: fftypes.NewUUID()}, 380 }) 381 assert.NoError(t, err) 382 383 } 384 385 func TestProcessPinsMissingNoMsg(t *testing.T) { 386 ag, cancel := newTestAggregator() 387 defer cancel() 388 389 mdi := ag.database.(*databasemocks.Plugin) 390 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(&fftypes.Batch{ 391 ID: fftypes.NewUUID(), 392 Payload: fftypes.BatchPayload{ 393 Messages: []*fftypes.Message{ 394 {Header: fftypes.MessageHeader{ID: fftypes.NewUUID()}}, 395 }, 396 }, 397 }, nil) 398 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 399 400 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 401 {Sequence: 12345, Batch: fftypes.NewUUID(), Index: 25}, 402 }) 403 assert.NoError(t, err) 404 mdi.AssertExpectations(t) 405 406 } 407 408 func TestProcessPinsBadMsgHeader(t *testing.T) { 409 ag, cancel := newTestAggregator() 410 defer cancel() 411 412 mdi := ag.database.(*databasemocks.Plugin) 413 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(&fftypes.Batch{ 414 ID: fftypes.NewUUID(), 415 Payload: fftypes.BatchPayload{ 416 Messages: []*fftypes.Message{ 417 {Header: fftypes.MessageHeader{ 418 ID: nil, /* missing */ 419 Topics: fftypes.FFNameArray{"topic1"}, 420 }}, 421 }, 422 }, 423 }, nil) 424 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 425 426 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 427 {Sequence: 12345, Batch: fftypes.NewUUID(), Index: 0}, 428 }) 429 assert.NoError(t, err) 430 mdi.AssertExpectations(t) 431 432 } 433 434 func TestProcessSkipDupMsg(t *testing.T) { 435 ag, cancel := newTestAggregator() 436 defer cancel() 437 438 batchID := fftypes.NewUUID() 439 mdi := ag.database.(*databasemocks.Plugin) 440 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(&fftypes.Batch{ 441 ID: batchID, 442 Payload: fftypes.BatchPayload{ 443 Messages: []*fftypes.Message{ 444 {Header: fftypes.MessageHeader{ 445 ID: fftypes.NewUUID(), 446 Topics: fftypes.FFNameArray{"topic1", "topic2"}, 447 }}, 448 }, 449 }, 450 }, nil).Once() 451 mdi.On("GetPins", mock.Anything, mock.Anything).Return([]*fftypes.Pin{ 452 {Sequence: 1111}, // blocks the context 453 }, nil) 454 mdi.On("UpdateOffset", ag.ctx, mock.Anything, mock.Anything).Return(nil) 455 456 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 457 {Sequence: 12345, Batch: batchID, Index: 0, Hash: fftypes.NewRandB32()}, 458 {Sequence: 12345, Batch: batchID, Index: 1, Hash: fftypes.NewRandB32()}, 459 }) 460 assert.NoError(t, err) 461 mdi.AssertExpectations(t) 462 463 } 464 465 func TestProcessMsgFailGetPins(t *testing.T) { 466 ag, cancel := newTestAggregator() 467 defer cancel() 468 469 batchID := fftypes.NewUUID() 470 mdi := ag.database.(*databasemocks.Plugin) 471 mdi.On("GetBatchByID", ag.ctx, mock.Anything).Return(&fftypes.Batch{ 472 ID: batchID, 473 Payload: fftypes.BatchPayload{ 474 Messages: []*fftypes.Message{ 475 {Header: fftypes.MessageHeader{ 476 ID: fftypes.NewUUID(), 477 Topics: fftypes.FFNameArray{"topic1"}, 478 }}, 479 }, 480 }, 481 }, nil).Once() 482 mdi.On("GetPins", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 483 484 err := ag.processPins(ag.ctx, []*fftypes.Pin{ 485 {Sequence: 12345, Batch: batchID, Index: 0, Hash: fftypes.NewRandB32()}, 486 }) 487 assert.EqualError(t, err, "pop") 488 mdi.AssertExpectations(t) 489 } 490 491 func TestProcessMsgFailMissingGroup(t *testing.T) { 492 ag, cancel := newTestAggregator() 493 defer cancel() 494 495 err := ag.processMessage(ag.ctx, &fftypes.Batch{}, true, 12345, &fftypes.Message{}) 496 assert.NoError(t, err) 497 498 } 499 500 func TestProcessMsgFailBadPin(t *testing.T) { 501 ag, cancel := newTestAggregator() 502 defer cancel() 503 504 err := ag.processMessage(ag.ctx, &fftypes.Batch{}, true, 12345, &fftypes.Message{ 505 Header: fftypes.MessageHeader{ 506 ID: fftypes.NewUUID(), 507 Group: fftypes.NewRandB32(), 508 Topics: fftypes.FFNameArray{"topic1"}, 509 }, 510 Pins: fftypes.FFNameArray{"!Wrong"}, 511 }) 512 assert.NoError(t, err) 513 514 } 515 516 func TestProcessMsgFailGetNextPins(t *testing.T) { 517 ag, cancel := newTestAggregator() 518 defer cancel() 519 520 mdi := ag.database.(*databasemocks.Plugin) 521 mdi.On("GetNextPins", ag.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) 522 523 err := ag.processMessage(ag.ctx, &fftypes.Batch{}, true, 12345, &fftypes.Message{ 524 Header: fftypes.MessageHeader{ 525 ID: fftypes.NewUUID(), 526 Group: fftypes.NewRandB32(), 527 Topics: fftypes.FFNameArray{"topic1"}, 528 }, 529 Pins: fftypes.FFNameArray{fftypes.NewRandB32().String()}, 530 }) 531 assert.EqualError(t, err, "pop") 532 533 } 534 535 func TestProcessMsgFailDispatch(t *testing.T) { 536 ag, cancel := newTestAggregator() 537 defer cancel() 538 539 mdi := ag.database.(*databasemocks.Plugin) 540 mdi.On("GetPins", ag.ctx, mock.Anything).Return([]*fftypes.Pin{}, nil) 541 mdm := ag.data.(*datamocks.Manager) 542 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return(nil, false, fmt.Errorf("pop")) 543 544 err := ag.processMessage(ag.ctx, &fftypes.Batch{}, false, 12345, &fftypes.Message{ 545 Header: fftypes.MessageHeader{ 546 ID: fftypes.NewUUID(), 547 Topics: fftypes.FFNameArray{"topic1"}, 548 }, 549 Pins: fftypes.FFNameArray{fftypes.NewRandB32().String()}, 550 }) 551 assert.EqualError(t, err, "pop") 552 553 } 554 555 func TestProcessMsgFailPinUpdate(t *testing.T) { 556 ag, cancel := newTestAggregator() 557 defer cancel() 558 pin := fftypes.NewRandB32() 559 560 mdi := ag.database.(*databasemocks.Plugin) 561 mdm := ag.data.(*datamocks.Manager) 562 mdi.On("GetNextPins", ag.ctx, mock.Anything).Return([]*fftypes.NextPin{ 563 {Context: fftypes.NewRandB32(), Hash: pin}, 564 }, nil) 565 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 566 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(false, nil) 567 mdi.On("UpsertEvent", ag.ctx, mock.Anything, false).Return(nil) 568 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(nil) 569 mdi.On("UpdateNextPin", ag.ctx, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 570 571 err := ag.processMessage(ag.ctx, &fftypes.Batch{}, true, 12345, &fftypes.Message{ 572 Header: fftypes.MessageHeader{ 573 ID: fftypes.NewUUID(), 574 Group: fftypes.NewRandB32(), 575 Topics: fftypes.FFNameArray{"topic1"}, 576 }, 577 Pins: fftypes.FFNameArray{pin.String()}, 578 }) 579 assert.EqualError(t, err, "pop") 580 581 } 582 583 func TestCheckMaskedContextReadyMismatchedAuthor(t *testing.T) { 584 ag, cancel := newTestAggregator() 585 defer cancel() 586 pin := fftypes.NewRandB32() 587 588 mdi := ag.database.(*databasemocks.Plugin) 589 mdi.On("GetNextPins", ag.ctx, mock.Anything).Return([]*fftypes.NextPin{ 590 {Context: fftypes.NewRandB32(), Hash: pin}, 591 }, nil) 592 593 _, err := ag.checkMaskedContextReady(ag.ctx, &fftypes.Message{ 594 Header: fftypes.MessageHeader{ 595 ID: fftypes.NewUUID(), 596 Group: fftypes.NewRandB32(), 597 Author: "author1", 598 }, 599 }, "topic1", 12345, fftypes.NewRandB32()) 600 assert.NoError(t, err) 601 602 } 603 604 func TestAttemptContextInitGetGroupByIDFail(t *testing.T) { 605 ag, cancel := newTestAggregator() 606 defer cancel() 607 608 mpm := ag.messaging.(*privatemessagingmocks.Manager) 609 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) 610 611 _, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 612 Header: fftypes.MessageHeader{ 613 ID: fftypes.NewUUID(), 614 Group: fftypes.NewRandB32(), 615 Author: "author1", 616 }, 617 }, "topic1", 12345, fftypes.NewRandB32(), fftypes.NewRandB32()) 618 assert.EqualError(t, err, "pop") 619 620 } 621 622 func TestAttemptContextInitGroupNotFound(t *testing.T) { 623 ag, cancel := newTestAggregator() 624 defer cancel() 625 626 mpm := ag.messaging.(*privatemessagingmocks.Manager) 627 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(nil, nil) 628 629 _, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 630 Header: fftypes.MessageHeader{ 631 ID: fftypes.NewUUID(), 632 Group: fftypes.NewRandB32(), 633 Author: "author1", 634 }, 635 }, "topic1", 12345, fftypes.NewRandB32(), fftypes.NewRandB32()) 636 assert.NoError(t, err) 637 638 } 639 640 func TestAttemptContextInitAuthorMismatch(t *testing.T) { 641 ag, cancel := newTestAggregator() 642 defer cancel() 643 644 groupID := fftypes.NewRandB32() 645 zeroHash := ag.calcHash("topic1", groupID, "author2", 0) 646 mpm := ag.messaging.(*privatemessagingmocks.Manager) 647 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 648 GroupIdentity: fftypes.GroupIdentity{ 649 Members: fftypes.Members{ 650 {Identity: "author2"}, 651 }, 652 }, 653 }, nil) 654 655 _, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 656 Header: fftypes.MessageHeader{ 657 ID: fftypes.NewUUID(), 658 Group: groupID, 659 Author: "author1", 660 }, 661 }, "topic1", 12345, fftypes.NewRandB32(), zeroHash) 662 assert.NoError(t, err) 663 664 } 665 666 func TestAttemptContextInitNoMatch(t *testing.T) { 667 ag, cancel := newTestAggregator() 668 defer cancel() 669 670 groupID := fftypes.NewRandB32() 671 mpm := ag.messaging.(*privatemessagingmocks.Manager) 672 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 673 GroupIdentity: fftypes.GroupIdentity{ 674 Members: fftypes.Members{ 675 {Identity: "author2"}, 676 }, 677 }, 678 }, nil) 679 680 _, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 681 Header: fftypes.MessageHeader{ 682 ID: fftypes.NewUUID(), 683 Group: groupID, 684 Author: "author1", 685 }, 686 }, "topic1", 12345, fftypes.NewRandB32(), fftypes.NewRandB32()) 687 assert.NoError(t, err) 688 689 } 690 691 func TestAttemptContextInitGetPinsFail(t *testing.T) { 692 ag, cancel := newTestAggregator() 693 defer cancel() 694 695 groupID := fftypes.NewRandB32() 696 zeroHash := ag.calcHash("topic1", groupID, "author1", 0) 697 mpm := ag.messaging.(*privatemessagingmocks.Manager) 698 mdi := ag.database.(*databasemocks.Plugin) 699 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 700 GroupIdentity: fftypes.GroupIdentity{ 701 Members: fftypes.Members{ 702 {Identity: "author1"}, 703 }, 704 }, 705 }, nil) 706 mdi.On("GetPins", ag.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) 707 708 _, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 709 Header: fftypes.MessageHeader{ 710 ID: fftypes.NewUUID(), 711 Group: groupID, 712 Author: "author1", 713 }, 714 }, "topic1", 12345, fftypes.NewRandB32(), zeroHash) 715 assert.EqualError(t, err, "pop") 716 717 } 718 719 func TestAttemptContextInitGetPinsBlocked(t *testing.T) { 720 ag, cancel := newTestAggregator() 721 defer cancel() 722 723 groupID := fftypes.NewRandB32() 724 zeroHash := ag.calcHash("topic1", groupID, "author1", 0) 725 mdi := ag.database.(*databasemocks.Plugin) 726 mpm := ag.messaging.(*privatemessagingmocks.Manager) 727 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 728 GroupIdentity: fftypes.GroupIdentity{ 729 Members: fftypes.Members{ 730 {Identity: "author1"}, 731 }, 732 }, 733 }, nil) 734 mdi.On("GetPins", ag.ctx, mock.Anything).Return([]*fftypes.Pin{ 735 {Sequence: 12345}, 736 }, nil) 737 738 np, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 739 Header: fftypes.MessageHeader{ 740 ID: fftypes.NewUUID(), 741 Group: groupID, 742 Author: "author1", 743 }, 744 }, "topic1", 12345, fftypes.NewRandB32(), zeroHash) 745 assert.NoError(t, err) 746 assert.Nil(t, np) 747 748 } 749 750 func TestAttemptContextInitInsertPinsFail(t *testing.T) { 751 ag, cancel := newTestAggregator() 752 defer cancel() 753 754 groupID := fftypes.NewRandB32() 755 zeroHash := ag.calcHash("topic1", groupID, "author1", 0) 756 mdi := ag.database.(*databasemocks.Plugin) 757 mpm := ag.messaging.(*privatemessagingmocks.Manager) 758 mpm.On("ResolveInitGroup", ag.ctx, mock.Anything).Return(&fftypes.Group{ 759 GroupIdentity: fftypes.GroupIdentity{ 760 Members: fftypes.Members{ 761 {Identity: "author1"}, 762 }, 763 }, 764 }, nil) 765 mdi.On("GetPins", ag.ctx, mock.Anything).Return([]*fftypes.Pin{}, nil) 766 mdi.On("InsertNextPin", ag.ctx, mock.Anything).Return(fmt.Errorf("pop")) 767 768 np, err := ag.attemptContextInit(ag.ctx, &fftypes.Message{ 769 Header: fftypes.MessageHeader{ 770 ID: fftypes.NewUUID(), 771 Group: groupID, 772 Author: "author1", 773 }, 774 }, "topic1", 12345, fftypes.NewRandB32(), zeroHash) 775 assert.Nil(t, np) 776 assert.EqualError(t, err, "pop") 777 778 } 779 780 func TestAttemptMessageDispatchFailGetData(t *testing.T) { 781 ag, cancel := newTestAggregator() 782 defer cancel() 783 784 mdm := ag.data.(*datamocks.Manager) 785 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return(nil, false, fmt.Errorf("pop")) 786 787 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 788 Header: fftypes.MessageHeader{ID: fftypes.NewUUID()}, 789 }) 790 assert.EqualError(t, err, "pop") 791 792 } 793 794 func TestAttemptMessageDispatchFailValidateData(t *testing.T) { 795 ag, cancel := newTestAggregator() 796 defer cancel() 797 798 mdm := ag.data.(*datamocks.Manager) 799 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 800 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(false, fmt.Errorf("pop")) 801 802 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 803 Header: fftypes.MessageHeader{ID: fftypes.NewUUID()}, 804 Data: fftypes.DataRefs{ 805 {ID: fftypes.NewUUID()}, 806 }, 807 }) 808 assert.EqualError(t, err, "pop") 809 810 } 811 812 func TestAttemptMessageDispatchFailValidateBadSystem(t *testing.T) { 813 ag, cancel := newTestAggregator() 814 defer cancel() 815 816 mbm := ag.broadcast.(*broadcastmocks.Manager) 817 mbm.On("HandleSystemBroadcast", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) 818 819 mdm := ag.data.(*datamocks.Manager) 820 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 821 822 mdi := ag.database.(*databasemocks.Plugin) 823 mdi.On("UpsertEvent", ag.ctx, mock.Anything, false).Return(nil) 824 825 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 826 Header: fftypes.MessageHeader{ 827 ID: fftypes.NewUUID(), 828 Namespace: fftypes.SystemNamespace, 829 }, 830 Data: fftypes.DataRefs{ 831 {ID: fftypes.NewUUID()}, 832 }, 833 }) 834 assert.NoError(t, err) 835 836 } 837 838 func TestAttemptMessageDispatchFailValidateSystemFail(t *testing.T) { 839 ag, cancel := newTestAggregator() 840 defer cancel() 841 842 mbm := ag.broadcast.(*broadcastmocks.Manager) 843 mbm.On("HandleSystemBroadcast", mock.Anything, mock.Anything, mock.Anything).Return(false, fmt.Errorf("pop")) 844 845 mdm := ag.data.(*datamocks.Manager) 846 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 847 848 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 849 Header: fftypes.MessageHeader{ 850 ID: fftypes.NewUUID(), 851 Namespace: fftypes.SystemNamespace, 852 }, 853 Data: fftypes.DataRefs{ 854 {ID: fftypes.NewUUID()}, 855 }, 856 }) 857 assert.EqualError(t, err, "pop") 858 859 } 860 861 func TestAttemptMessageDispatchEventFail(t *testing.T) { 862 ag, cancel := newTestAggregator() 863 defer cancel() 864 865 mdi := ag.database.(*databasemocks.Plugin) 866 mdm := ag.data.(*datamocks.Manager) 867 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 868 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(true, nil) 869 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(nil) 870 mdi.On("UpsertEvent", ag.ctx, mock.Anything, false).Return(fmt.Errorf("pop")) 871 872 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 873 Header: fftypes.MessageHeader{ID: fftypes.NewUUID()}, 874 }) 875 assert.EqualError(t, err, "pop") 876 877 } 878 879 func TestAttemptMessageUpdateMessageFail(t *testing.T) { 880 ag, cancel := newTestAggregator() 881 defer cancel() 882 883 mdi := ag.database.(*databasemocks.Plugin) 884 mdm := ag.data.(*datamocks.Manager) 885 mdm.On("GetMessageData", ag.ctx, mock.Anything, true).Return([]*fftypes.Data{}, true, nil) 886 mdm.On("ValidateAll", ag.ctx, mock.Anything).Return(true, nil) 887 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 888 889 _, err := ag.attemptMessageDispatch(ag.ctx, &fftypes.Message{ 890 Header: fftypes.MessageHeader{ID: fftypes.NewUUID()}, 891 }) 892 assert.EqualError(t, err, "pop") 893 894 } 895 896 func TestRewindOffchainBatchesNoBatches(t *testing.T) { 897 ag, cancel := newTestAggregator() 898 defer cancel() 899 900 mdi := ag.database.(*databasemocks.Plugin) 901 mdi.On("UpdateMessage", ag.ctx, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 902 903 rewind, offset := ag.rewindOffchainBatches() 904 assert.False(t, rewind) 905 assert.Equal(t, int64(0), offset) 906 } 907 908 func TestRewindOffchainBatchesBatchesNoRewind(t *testing.T) { 909 config.Set(config.EventAggregatorBatchSize, 10) 910 911 ag, cancel := newTestAggregator() 912 defer cancel() 913 914 ag.offchainBatches <- fftypes.NewUUID() 915 ag.offchainBatches <- fftypes.NewUUID() 916 ag.offchainBatches <- fftypes.NewUUID() 917 ag.offchainBatches <- fftypes.NewUUID() 918 919 mdi := ag.database.(*databasemocks.Plugin) 920 mdi.On("GetPins", ag.ctx, mock.Anything, mock.Anything).Return([]*fftypes.Pin{}, nil) 921 922 rewind, offset := ag.rewindOffchainBatches() 923 assert.False(t, rewind) 924 assert.Equal(t, int64(0), offset) 925 } 926 927 func TestRewindOffchainBatchesBatchesRewind(t *testing.T) { 928 config.Set(config.EventAggregatorBatchSize, 10) 929 930 ag, cancel := newTestAggregator() 931 defer cancel() 932 933 ag.offchainBatches <- fftypes.NewUUID() 934 ag.offchainBatches <- fftypes.NewUUID() 935 ag.offchainBatches <- fftypes.NewUUID() 936 ag.offchainBatches <- fftypes.NewUUID() 937 938 mdi := ag.database.(*databasemocks.Plugin) 939 mdi.On("GetPins", ag.ctx, mock.Anything, mock.Anything).Return([]*fftypes.Pin{ 940 {Sequence: 12345}, 941 }, nil) 942 943 rewind, offset := ag.rewindOffchainBatches() 944 assert.True(t, rewind) 945 assert.Equal(t, int64(12345), offset) 946 } 947 948 func TestRewindOffchainBatchesBatchesError(t *testing.T) { 949 config.Set(config.EventAggregatorBatchSize, 10) 950 951 ag, cancel := newTestAggregator() 952 cancel() 953 954 ag.offchainBatches <- fftypes.NewUUID() 955 956 mdi := ag.database.(*databasemocks.Plugin) 957 mdi.On("GetPins", ag.ctx, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 958 959 rewind, _ := ag.rewindOffchainBatches() 960 assert.False(t, rewind) 961 }