github.com/koko1123/flow-go-1@v0.29.6/engine/execution/rpc/engine_test.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "errors" 6 "math/rand" 7 "testing" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/rs/zerolog" 11 "github.com/stretchr/testify/mock" 12 "github.com/stretchr/testify/suite" 13 "google.golang.org/grpc/codes" 14 "google.golang.org/grpc/status" 15 "google.golang.org/protobuf/testing/protocmp" 16 17 "github.com/onflow/flow/protobuf/go/flow/entities" 18 "github.com/onflow/flow/protobuf/go/flow/execution" 19 20 "github.com/koko1123/flow-go-1/engine/common/rpc/convert" 21 ingestion "github.com/koko1123/flow-go-1/engine/execution/ingestion/mock" 22 "github.com/koko1123/flow-go-1/model/flow" 23 realstorage "github.com/koko1123/flow-go-1/storage" 24 storage "github.com/koko1123/flow-go-1/storage/mock" 25 "github.com/koko1123/flow-go-1/utils/unittest" 26 ) 27 28 type Suite struct { 29 suite.Suite 30 log zerolog.Logger 31 events *storage.Events 32 exeResults *storage.ExecutionResults 33 txResults *storage.TransactionResults 34 commits *storage.Commits 35 headers *storage.Headers 36 } 37 38 func TestHandler(t *testing.T) { 39 suite.Run(t, new(Suite)) 40 } 41 42 func (suite *Suite) SetupTest() { 43 suite.log = zerolog.Logger{} 44 suite.events = new(storage.Events) 45 suite.exeResults = new(storage.ExecutionResults) 46 suite.txResults = new(storage.TransactionResults) 47 suite.commits = new(storage.Commits) 48 suite.headers = new(storage.Headers) 49 } 50 51 // TestExecuteScriptAtBlockID tests the ExecuteScriptAtBlockID API call 52 func (suite *Suite) TestExecuteScriptAtBlockID() { 53 // setup handler 54 mockEngine := new(ingestion.IngestRPC) 55 handler := &handler{ 56 engine: mockEngine, 57 chain: flow.Mainnet, 58 } 59 60 // setup dummy request/response 61 ctx := context.Background() 62 mockIdentifier := unittest.IdentifierFixture() 63 script := []byte("dummy script") 64 arguments := [][]byte(nil) 65 executionReq := execution.ExecuteScriptAtBlockIDRequest{ 66 BlockId: mockIdentifier[:], 67 Script: script, 68 } 69 scriptExecValue := []byte{9, 10, 11} 70 executionResp := execution.ExecuteScriptAtBlockIDResponse{ 71 Value: scriptExecValue, 72 } 73 74 suite.Run("happy path with successful script execution", func() { 75 mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). 76 Return(scriptExecValue, nil).Once() 77 response, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) 78 suite.Require().NoError(err) 79 suite.Require().Equal(&executionResp, response) 80 mockEngine.AssertExpectations(suite.T()) 81 }) 82 83 suite.Run("valid request with script execution failure", func() { 84 mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). 85 Return(nil, status.Error(codes.InvalidArgument, "")).Once() 86 _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) 87 suite.Require().Error(err) 88 errors.Is(err, status.Error(codes.InvalidArgument, "")) 89 }) 90 91 suite.Run("invalid request with nil blockID", func() { 92 executionReqWithNilBlock := execution.ExecuteScriptAtBlockIDRequest{ 93 BlockId: nil, 94 Script: script, 95 } 96 _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReqWithNilBlock) 97 98 // check that an error was received 99 suite.Require().Error(err) 100 errors.Is(err, status.Error(codes.InvalidArgument, "")) 101 }) 102 103 } 104 105 // TestGetEventsForBlockIDs tests the GetEventsForBlockIDs API call 106 func (suite *Suite) TestGetEventsForBlockIDs() { 107 108 totalBlocks := 10 109 eventsPerBlock := 10 110 111 blockIDs := make([][]byte, totalBlocks) 112 expectedResult := make([]*execution.GetEventsForBlockIDsResponse_Result, totalBlocks) 113 114 // setup the events storage mock 115 for i := range blockIDs { 116 block := unittest.BlockFixture() 117 block.Header.Height = uint64(i) 118 id := block.ID() 119 blockIDs[i] = id[:] 120 eventsForBlock := make([]flow.Event, eventsPerBlock) 121 eventMessages := make([]*entities.Event, eventsPerBlock) 122 for j := range eventsForBlock { 123 e := unittest.EventFixture(flow.EventAccountCreated, uint32(j), uint32(j), unittest.IdentifierFixture(), 0) 124 eventsForBlock[j] = e 125 eventMessages[j] = convert.EventToMessage(e) 126 } 127 // expect one call to lookup result for each block ID 128 //suite.exeResults.On("ByBlockID", id).Return(nil, nil).Once() 129 suite.commits.On("ByBlockID", id).Return(nil, nil).Once() 130 131 // expect one call to lookup events for each block ID 132 suite.events.On("ByBlockIDEventType", id, flow.EventAccountCreated).Return(eventsForBlock, nil).Once() 133 134 // expect one call to lookup each block 135 suite.headers.On("ByBlockID", id).Return(block.Header, nil).Once() 136 137 // create the expected result for this block 138 expectedResult[i] = &execution.GetEventsForBlockIDsResponse_Result{ 139 BlockId: id[:], 140 BlockHeight: block.Header.Height, 141 Events: eventMessages, 142 } 143 } 144 145 // create the handler 146 handler := &handler{ 147 headers: suite.headers, 148 events: suite.events, 149 exeResults: suite.exeResults, 150 transactionResults: suite.txResults, 151 commits: suite.commits, 152 chain: flow.Mainnet, 153 } 154 155 concoctReq := func(errType string, blockIDs [][]byte) *execution.GetEventsForBlockIDsRequest { 156 return &execution.GetEventsForBlockIDsRequest{ 157 Type: errType, 158 BlockIds: blockIDs, 159 } 160 } 161 162 // happy path - valid requests receives a valid response 163 suite.Run("happy path", func() { 164 165 // create a valid API request 166 req := concoctReq(string(flow.EventAccountCreated), blockIDs) 167 168 // execute the GetEventsForBlockIDs call 169 resp, err := handler.GetEventsForBlockIDs(context.Background(), req) 170 171 // check that a successful response is received 172 suite.Require().NoError(err) 173 174 actualResult := resp.GetResults() 175 suite.Require().ElementsMatch(expectedResult, actualResult) 176 177 // check that appropriate storage calls were made 178 suite.events.AssertExpectations(suite.T()) 179 }) 180 181 // failure path - empty even type in the request results in an error 182 suite.Run("request with empty event type", func() { 183 184 // create an API request with empty even type 185 req := concoctReq("", blockIDs) 186 187 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 188 189 // check that an error was received 190 suite.Require().Error(err) 191 errors.Is(err, status.Error(codes.InvalidArgument, "")) 192 193 // check that no storage calls was made 194 suite.events.AssertExpectations(suite.T()) 195 }) 196 197 // failure path - empty block ids in request results in an error 198 suite.Run("request with empty block IDs", func() { 199 200 // create an API request with empty block ids 201 req := concoctReq(string(flow.EventAccountCreated), nil) 202 203 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 204 205 // check that an error was received 206 suite.Require().Error(err) 207 errors.Is(err, status.Error(codes.InvalidArgument, "")) 208 209 // check that no storage calls was made 210 suite.events.AssertExpectations(suite.T()) 211 }) 212 213 // failure path - non-existent block id in request results in an error 214 suite.Run("request with non-existent block ID", func() { 215 216 id := unittest.IdentifierFixture() 217 218 // expect a storage call for the invalid id but return an error 219 suite.commits.On("ByBlockID", id).Return(nil, realstorage.ErrNotFound).Once() 220 221 // create an API request with the invalid block id 222 req := concoctReq(string(flow.EventAccountCreated), [][]byte{id[:]}) 223 224 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 225 226 // check that an error was received 227 suite.Require().Error(err) 228 errors.Is(err, status.Error(codes.NotFound, "")) 229 230 // check that no storage calls was made 231 suite.events.AssertExpectations(suite.T()) 232 }) 233 } 234 235 // Test GetAccountAtBlockID tests the GetAccountAtBlockID API call 236 func (suite *Suite) TestGetAccountAtBlockID() { 237 238 id := unittest.IdentifierFixture() 239 serviceAddress := flow.Mainnet.Chain().ServiceAddress() 240 241 serviceAccount := flow.Account{ 242 Address: serviceAddress, 243 } 244 245 mockEngine := new(ingestion.IngestRPC) 246 247 // create the handler 248 handler := &handler{ 249 engine: mockEngine, 250 chain: flow.Mainnet, 251 } 252 253 createReq := func(id []byte, address []byte) *execution.GetAccountAtBlockIDRequest { 254 return &execution.GetAccountAtBlockIDRequest{ 255 Address: address, 256 BlockId: id, 257 } 258 } 259 260 suite.Run("happy path with valid request", func() { 261 262 // setup mock expectations 263 mockEngine.On("GetAccount", mock.Anything, serviceAddress, id).Return(&serviceAccount, nil).Once() 264 265 req := createReq(id[:], serviceAddress.Bytes()) 266 267 resp, err := handler.GetAccountAtBlockID(context.Background(), req) 268 269 suite.Require().NoError(err) 270 actualAccount := resp.GetAccount() 271 expectedAccount, err := convert.AccountToMessage(&serviceAccount) 272 suite.Require().NoError(err) 273 suite.Require().Empty( 274 cmp.Diff(expectedAccount, actualAccount, protocmp.Transform())) 275 mockEngine.AssertExpectations(suite.T()) 276 }) 277 278 suite.Run("invalid request with nil block id", func() { 279 280 req := createReq(nil, serviceAddress.Bytes()) 281 282 _, err := handler.GetAccountAtBlockID(context.Background(), req) 283 284 suite.Require().Error(err) 285 }) 286 287 suite.Run("invalid request with nil root address", func() { 288 289 req := createReq(id[:], nil) 290 291 _, err := handler.GetAccountAtBlockID(context.Background(), req) 292 293 suite.Require().Error(err) 294 }) 295 } 296 297 // Test GetRegisterAtBlockID tests the GetRegisterAtBlockID API call 298 func (suite *Suite) TestGetRegisterAtBlockID() { 299 300 id := unittest.IdentifierFixture() 301 serviceAddress := flow.Mainnet.Chain().ServiceAddress() 302 validKey := []byte("exists") 303 304 mockEngine := new(ingestion.IngestRPC) 305 306 // create the handler 307 handler := &handler{ 308 engine: mockEngine, 309 chain: flow.Mainnet, 310 } 311 312 createReq := func(id, owner, key []byte) *execution.GetRegisterAtBlockIDRequest { 313 return &execution.GetRegisterAtBlockIDRequest{ 314 RegisterOwner: owner, 315 RegisterKey: key, 316 BlockId: id, 317 } 318 } 319 320 suite.Run("happy path with valid request", func() { 321 322 // setup mock expectations 323 mockEngine.On("GetRegisterAtBlockID", mock.Anything, serviceAddress.Bytes(), validKey, id).Return([]uint8{1}, nil).Once() 324 325 req := createReq(id[:], serviceAddress.Bytes(), validKey) 326 resp, err := handler.GetRegisterAtBlockID(context.Background(), req) 327 328 suite.Require().NoError(err) 329 value := resp.GetValue() 330 suite.Require().NoError(err) 331 suite.Require().True(len(value) > 0) 332 mockEngine.AssertExpectations(suite.T()) 333 }) 334 335 suite.Run("invalid request with bad address", func() { 336 badOwner := []byte("\uFFFD") 337 // return error 338 mockEngine.On("GetRegisterAtBlockID", mock.Anything, badOwner, validKey, id).Return(nil, errors.New("error")).Once() 339 340 req := createReq(id[:], badOwner, validKey) 341 _, err := handler.GetRegisterAtBlockID(context.Background(), req) 342 suite.Require().Error(err) 343 }) 344 } 345 346 // TestGetTransactionResult tests the GetTransactionResult and GetTransactionResultByIndex API calls 347 func (suite *Suite) TestGetTransactionResult() { 348 349 totalEvents := 10 350 block := unittest.BlockFixture() 351 tx := unittest.TransactionFixture() 352 bID := block.ID() 353 txID := tx.ID() 354 txIndex := rand.Uint32() 355 356 // setup the events storage mock 357 eventsForTx := make([]flow.Event, totalEvents) 358 eventMessages := make([]*entities.Event, totalEvents) 359 for j := range eventsForTx { 360 e := unittest.EventFixture(flow.EventAccountCreated, uint32(j), uint32(j), unittest.IdentifierFixture(), 0) 361 eventsForTx[j] = e 362 eventMessages[j] = convert.EventToMessage(e) 363 } 364 365 // expect a call to lookup events by block ID and transaction ID 366 suite.events.On("ByBlockIDTransactionID", bID, txID).Return(eventsForTx, nil) 367 368 // expect a call to lookup each block 369 suite.headers.On("ByID", block.ID()).Return(&block, true) 370 371 // create the handler 372 createHandler := func(txResults *storage.TransactionResults) *handler { 373 handler := &handler{ 374 headers: suite.headers, 375 events: suite.events, 376 transactionResults: txResults, 377 chain: flow.Mainnet, 378 } 379 return handler 380 } 381 382 // concoctReq creates a GetEventsForBlockIDTransactionIDRequest 383 concoctReq := func(bID []byte, tID []byte) *execution.GetTransactionResultRequest { 384 return &execution.GetTransactionResultRequest{ 385 BlockId: bID, 386 TransactionId: tID, 387 } 388 } 389 390 // concoctIndexReq creates a GetTransactionByIndexRequest 391 concoctIndexReq := func(bID []byte, tIndex uint32) *execution.GetTransactionByIndexRequest { 392 return &execution.GetTransactionByIndexRequest{ 393 BlockId: bID, 394 Index: uint32(tIndex), 395 } 396 } 397 398 assertEqual := func(expected, actual *execution.GetTransactionResultResponse) { 399 suite.Require().Equal(expected.GetStatusCode(), actual.GetStatusCode()) 400 suite.Require().Equal(expected.GetErrorMessage(), actual.GetErrorMessage()) 401 suite.Require().ElementsMatch(expected.GetEvents(), actual.GetEvents()) 402 } 403 404 // happy path - valid requests receives all events for the given transaction 405 suite.Run("happy path with valid events and no transaction error", func() { 406 407 // create the expected result 408 expectedResult := &execution.GetTransactionResultResponse{ 409 StatusCode: 0, 410 ErrorMessage: "", 411 Events: eventMessages, 412 } 413 414 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 415 txResults := new(storage.TransactionResults) 416 txResult := flow.TransactionResult{ 417 TransactionID: flow.Identifier{}, 418 ErrorMessage: "", 419 } 420 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 421 422 handler := createHandler(txResults) 423 424 // create a valid API request 425 req := concoctReq(bID[:], txID[:]) 426 427 // execute the GetTransactionResult call 428 actualResult, err := handler.GetTransactionResult(context.Background(), req) 429 430 // check that a successful response is received 431 suite.Require().NoError(err) 432 433 // check that all fields in response are as expected 434 assertEqual(expectedResult, actualResult) 435 436 // check that appropriate storage calls were made 437 suite.events.AssertExpectations(suite.T()) 438 txResults.AssertExpectations(suite.T()) 439 }) 440 441 // happy path - valid requests receives all events for the given transaction by index 442 suite.Run("index happy path with valid events and no transaction error", func() { 443 444 // create the expected result 445 expectedResult := &execution.GetTransactionResultResponse{ 446 StatusCode: 0, 447 ErrorMessage: "", 448 Events: eventMessages, 449 } 450 451 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 452 txResults := new(storage.TransactionResults) 453 txResult := flow.TransactionResult{ 454 TransactionID: flow.Identifier{}, 455 ErrorMessage: "", 456 } 457 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 458 459 // expect a call to lookup events by block ID and tx index 460 suite.events.On("ByBlockIDTransactionIndex", bID, txIndex).Return(eventsForTx, nil).Once() 461 462 handler := createHandler(txResults) 463 464 // create a valid API request 465 req := concoctIndexReq(bID[:], txIndex) 466 467 // execute the GetTransactionResult call 468 actualResult, err := handler.GetTransactionResultByIndex(context.Background(), req) 469 470 // check that a successful response is received 471 suite.Require().NoError(err) 472 473 // check that all fields in response are as expected 474 assertEqual(expectedResult, actualResult) 475 476 // check that appropriate storage calls were made 477 suite.events.AssertExpectations(suite.T()) 478 txResults.AssertExpectations(suite.T()) 479 }) 480 481 // happy path - valid requests receives all events and an error for the given transaction 482 suite.Run("happy path with valid events and a transaction error", func() { 483 484 // create the expected result 485 expectedResult := &execution.GetTransactionResultResponse{ 486 StatusCode: 1, 487 ErrorMessage: "runtime error", 488 Events: eventMessages, 489 } 490 491 // setup the storage to return a transaction error 492 txResults := new(storage.TransactionResults) 493 txResult := flow.TransactionResult{ 494 TransactionID: txID, 495 ErrorMessage: "runtime error", 496 } 497 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 498 499 handler := createHandler(txResults) 500 501 // create a valid API request 502 req := concoctReq(bID[:], txID[:]) 503 504 // execute the GetEventsForBlockIDTransactionID call 505 actualResult, err := handler.GetTransactionResult(context.Background(), req) 506 507 // check that a successful response is received 508 suite.Require().NoError(err) 509 510 // check that all fields in response are as expected 511 assertEqual(expectedResult, actualResult) 512 513 // check that appropriate storage calls were made 514 suite.events.AssertExpectations(suite.T()) 515 txResults.AssertExpectations(suite.T()) 516 }) 517 518 // happy path - valid requests receives all events and an error for the given transaction 519 suite.Run("index happy path with valid events and a transaction error", func() { 520 521 // create the expected result 522 expectedResult := &execution.GetTransactionResultResponse{ 523 StatusCode: 1, 524 ErrorMessage: "runtime error", 525 Events: eventMessages, 526 } 527 528 // setup the storage to return a transaction error 529 txResults := new(storage.TransactionResults) 530 txResult := flow.TransactionResult{ 531 TransactionID: txID, 532 ErrorMessage: "runtime error", 533 } 534 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 535 536 // expect a call to lookup events by block ID and tx index 537 suite.events.On("ByBlockIDTransactionIndex", bID, txIndex).Return(eventsForTx, nil).Once() 538 539 handler := createHandler(txResults) 540 541 // create a valid API request 542 req := concoctIndexReq(bID[:], txIndex) 543 544 // execute the GetEventsForBlockIDTransactionID call 545 actualResult, err := handler.GetTransactionResultByIndex(context.Background(), req) 546 547 // check that a successful response is received 548 suite.Require().NoError(err) 549 550 // check that all fields in response are as expected 551 assertEqual(expectedResult, actualResult) 552 553 // check that appropriate storage calls were made 554 suite.events.AssertExpectations(suite.T()) 555 txResults.AssertExpectations(suite.T()) 556 }) 557 558 // failure path - nil transaction ID in the request results in an error 559 suite.Run("request with nil tx ID", func() { 560 561 // create an API request with transaction ID as nil 562 req := concoctReq(bID[:], nil) 563 564 // expect a call to lookup transaction result by block ID and transaction ID, return an error 565 txResults := new(storage.TransactionResults) 566 567 txResults.On("ByBlockIDTransactionID", bID, nil).Return(nil, status.Error(codes.InvalidArgument, "")).Once() 568 569 handler := createHandler(txResults) 570 571 _, err := handler.GetTransactionResult(context.Background(), req) 572 573 // check that an error was received 574 suite.Require().Error(err) 575 errors.Is(err, status.Error(codes.InvalidArgument, "")) 576 577 // check that no storage calls was made 578 suite.events.AssertExpectations(suite.T()) 579 }) 580 581 // failure path - nil block id in the request results in an error 582 suite.Run("request with nil block ID", func() { 583 584 // create an API request with a nil block id 585 req := concoctReq(nil, txID[:]) 586 587 txResults := new(storage.TransactionResults) 588 589 txResults.On("ByBlockIDTransactionID", nil, txID).Return(nil, status.Error(codes.InvalidArgument, "")).Once() 590 591 handler := createHandler(txResults) 592 593 _, err := handler.GetTransactionResult(context.Background(), req) 594 595 // check that an error was received 596 suite.Require().Error(err) 597 errors.Is(err, status.Error(codes.InvalidArgument, "")) 598 599 // check that no storage calls was made 600 suite.events.AssertExpectations(suite.T()) 601 }) 602 603 // failure path - nil block id in the index request results in an error 604 suite.Run("index request with nil block ID", func() { 605 606 // create an API request with a nil block id 607 req := concoctIndexReq(nil, txIndex) 608 609 txResults := new(storage.TransactionResults) 610 611 txResults.On("ByBlockIDTransactionIndex", nil, txID).Return(nil, status.Error(codes.InvalidArgument, "")).Once() 612 613 handler := createHandler(txResults) 614 615 _, err := handler.GetTransactionResultByIndex(context.Background(), req) 616 617 // check that an error was received 618 suite.Require().Error(err) 619 errors.Is(err, status.Error(codes.InvalidArgument, "")) 620 621 // check that no storage calls was made 622 suite.events.AssertExpectations(suite.T()) 623 }) 624 625 // failure path - non-existent transaction ID in request results in an error 626 suite.Run("request with non-existent transaction ID", func() { 627 628 wrongTxID := unittest.IdentifierFixture() 629 630 // create an API request with the invalid transaction ID 631 req := concoctReq(bID[:], wrongTxID[:]) 632 633 // expect a storage call for the invalid tx ID but return an error 634 txResults := new(storage.TransactionResults) 635 txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, status.Error(codes.Internal, "")).Once() 636 637 handler := createHandler(txResults) 638 639 _, err := handler.GetTransactionResult(context.Background(), req) 640 641 // check that an error was received 642 suite.Require().Error(err) 643 errors.Is(err, status.Error(codes.Internal, "")) 644 645 // check that one storage call was made 646 suite.events.AssertExpectations(suite.T()) 647 }) 648 649 // failure path - non-existent transaction index in request results in an error 650 suite.Run("request with non-existent transaction index", func() { 651 652 wrongTxIndex := txIndex + 1 653 654 // create an API request with the invalid transaction ID 655 req := concoctIndexReq(bID[:], wrongTxIndex) 656 657 // expect a storage call for the invalid tx ID but return an error 658 txResults := new(storage.TransactionResults) 659 txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, status.Error(codes.Internal, "")).Once() 660 661 handler := createHandler(txResults) 662 663 _, err := handler.GetTransactionResultByIndex(context.Background(), req) 664 665 // check that an error was received 666 suite.Require().Error(err) 667 errors.Is(err, status.Error(codes.Internal, "")) 668 669 // check that one storage call was made 670 suite.events.AssertExpectations(suite.T()) 671 }) 672 } 673 674 // TestGetTransactionResultsByBlock tests TestGetTransactionResultsByBlockID API calls 675 func (suite *Suite) TestGetTransactionResultsByBlockID() { 676 677 totalEvents := 10 678 block := unittest.BlockFixture() 679 tx := unittest.TransactionFixture() 680 bID := block.ID() 681 nonexistingBlockID := unittest.IdentifierFixture() 682 tx1ID := tx.ID() 683 tx2ID := tx.ID() 684 //txIndex := rand.Uint32() 685 686 // setup the events storage mock 687 eventsForTx1 := make([]flow.Event, totalEvents-3) 688 eventsForTx2 := make([]flow.Event, totalEvents-len(eventsForTx1)) 689 eventsForBlock := make([]flow.Event, totalEvents) 690 691 convertedEventsForTx1 := make([]*entities.Event, len(eventsForTx1)) 692 convertedEventsForTx2 := make([]*entities.Event, len(eventsForTx2)) 693 694 for j := 0; j < len(eventsForTx1); j++ { 695 e := unittest.EventFixture(flow.EventAccountCreated, uint32(0), uint32(j), tx1ID, 0) 696 eventsForTx1[j] = e 697 eventsForBlock[j] = e 698 convertedEventsForTx1[j] = convert.EventToMessage(e) 699 } 700 for j := 0; j < len(eventsForTx2); j++ { 701 e := unittest.EventFixture(flow.EventAccountCreated, uint32(1), uint32(j), tx2ID, 0) 702 eventsForTx2[j] = e 703 eventsForBlock[len(eventsForTx1)+j] = e 704 convertedEventsForTx2[j] = convert.EventToMessage(e) 705 } 706 707 // create the handler 708 createHandler := func(txResults *storage.TransactionResults) *handler { 709 handler := &handler{ 710 headers: suite.headers, 711 events: suite.events, 712 transactionResults: txResults, 713 chain: flow.Mainnet, 714 } 715 return handler 716 } 717 718 // concoctReq creates a GetTransactionResultsByBlockIDRequest 719 concoctReq := func(bID []byte) *execution.GetTransactionsByBlockIDRequest { 720 return &execution.GetTransactionsByBlockIDRequest{ 721 BlockId: bID, 722 } 723 } 724 725 assertEqual := func(expected, actual *execution.GetTransactionResultsResponse) { 726 727 suite.Require().Len(expected.TransactionResults, len(actual.TransactionResults)) 728 729 for i, txResult := range actual.TransactionResults { 730 suite.Require().Equal(txResult.GetStatusCode(), actual.TransactionResults[i].GetStatusCode()) 731 suite.Require().Equal(txResult.GetErrorMessage(), actual.TransactionResults[i].GetErrorMessage()) 732 suite.Require().ElementsMatch(txResult.GetEvents(), actual.TransactionResults[i].GetEvents()) 733 } 734 } 735 736 // happy path - valid requests receives all events for the given transaction 737 suite.Run("happy path with valid events and no transaction error", func() { 738 739 // expect a call to lookup events by block ID and transaction ID 740 suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() 741 742 // create the expected result 743 expectedResult := &execution.GetTransactionResultsResponse{ 744 TransactionResults: []*execution.GetTransactionResultResponse{ 745 { 746 StatusCode: 0, 747 ErrorMessage: "", 748 Events: convertedEventsForTx1, 749 }, 750 { 751 StatusCode: 0, 752 ErrorMessage: "", 753 Events: convertedEventsForTx1, 754 }, 755 }, 756 } 757 758 // expect a call to lookup transaction result by block ID return a result with no error 759 txResultsMock := new(storage.TransactionResults) 760 txResults := []flow.TransactionResult{ 761 { 762 TransactionID: tx1ID, 763 ErrorMessage: "", 764 }, 765 { 766 TransactionID: tx2ID, 767 ErrorMessage: "", 768 }, 769 } 770 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 771 772 handler := createHandler(txResultsMock) 773 774 // create a valid API request 775 req := concoctReq(bID[:]) 776 777 // execute the GetTransactionResult call 778 actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 779 780 // check that a successful response is received 781 suite.Require().NoError(err) 782 783 // check that all fields in response are as expected 784 assertEqual(expectedResult, actualResult) 785 786 // check that appropriate storage calls were made 787 suite.events.AssertExpectations(suite.T()) 788 txResultsMock.AssertExpectations(suite.T()) 789 }) 790 791 // happy path - valid requests receives all events and an error for the given transaction 792 suite.Run("happy path with valid events and a transaction error", func() { 793 794 // expect a call to lookup events by block ID and transaction ID 795 suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() 796 797 // create the expected result 798 expectedResult := &execution.GetTransactionResultsResponse{ 799 TransactionResults: []*execution.GetTransactionResultResponse{ 800 { 801 StatusCode: 0, 802 ErrorMessage: "", 803 Events: convertedEventsForTx1, 804 }, 805 { 806 StatusCode: 1, 807 ErrorMessage: "runtime error", 808 Events: convertedEventsForTx2, 809 }, 810 }, 811 } 812 813 // expect a call to lookup transaction result by block ID return a result with no error 814 txResultsMock := new(storage.TransactionResults) 815 txResults := []flow.TransactionResult{ 816 { 817 TransactionID: tx1ID, 818 ErrorMessage: "", 819 }, 820 { 821 TransactionID: tx2ID, 822 ErrorMessage: "runtime error", 823 }, 824 } 825 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 826 827 handler := createHandler(txResultsMock) 828 829 // create a valid API request 830 req := concoctReq(bID[:]) 831 832 // execute the GetTransactionResult call 833 actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 834 835 // check that a successful response is received 836 suite.Require().NoError(err) 837 838 // check that all fields in response are as expected 839 assertEqual(expectedResult, actualResult) 840 841 // check that appropriate storage calls were made 842 suite.events.AssertExpectations(suite.T()) 843 txResultsMock.AssertExpectations(suite.T()) 844 }) 845 846 // failure path - nil block id in the request results in an error 847 suite.Run("request with nil block ID", func() { 848 849 // create an API request with a nil block id 850 req := concoctReq(nil) 851 852 txResults := new(storage.TransactionResults) 853 854 txResults.On("ByBlockID", nil).Return(nil, status.Error(codes.InvalidArgument, "")).Once() 855 856 handler := createHandler(txResults) 857 858 _, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 859 860 // check that an error was received 861 suite.Require().Error(err) 862 errors.Is(err, status.Error(codes.InvalidArgument, "")) 863 864 // check that no storage calls was made 865 suite.events.AssertExpectations(suite.T()) 866 }) 867 868 // failure path - nonexisting block id in the request results in valid, but empty 869 suite.Run("request with nonexisting block ID", func() { 870 871 // expect a call to lookup events by block ID and transaction ID 872 suite.events.On("ByBlockID", nonexistingBlockID).Return(eventsForBlock, nil).Once() 873 874 // create the expected result 875 expectedResult := &execution.GetTransactionResultsResponse{ 876 TransactionResults: []*execution.GetTransactionResultResponse{}, 877 } 878 879 // expect a call to lookup transaction result by block ID return a result with no error 880 txResultsMock := new(storage.TransactionResults) 881 var txResults []flow.TransactionResult 882 txResultsMock.On("ByBlockID", nonexistingBlockID).Return(txResults, nil).Once() 883 884 handler := createHandler(txResultsMock) 885 886 // create a valid API request 887 req := concoctReq(nonexistingBlockID[:]) 888 889 // execute the GetTransactionResult call 890 actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 891 892 // check that a successful response is received 893 suite.Require().NoError(err) 894 895 // check that all fields in response are as expected 896 assertEqual(expectedResult, actualResult) 897 898 // check that appropriate storage calls were made 899 suite.events.AssertExpectations(suite.T()) 900 txResultsMock.AssertExpectations(suite.T()) 901 }) 902 }