github.com/onflow/flow-go@v0.33.17/engine/execution/rpc/engine_test.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/rand" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/rs/zerolog" 12 "github.com/stretchr/testify/mock" 13 "github.com/stretchr/testify/suite" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/grpc/status" 16 "google.golang.org/protobuf/testing/protocmp" 17 18 "github.com/onflow/flow/protobuf/go/flow/entities" 19 "github.com/onflow/flow/protobuf/go/flow/execution" 20 21 "github.com/onflow/flow-go/engine/common/rpc/convert" 22 mockEng "github.com/onflow/flow-go/engine/execution/mock" 23 "github.com/onflow/flow-go/engine/execution/state" 24 "github.com/onflow/flow-go/model/flow" 25 realstorage "github.com/onflow/flow-go/storage" 26 storage "github.com/onflow/flow-go/storage/mock" 27 "github.com/onflow/flow-go/utils/unittest" 28 ) 29 30 type Suite struct { 31 suite.Suite 32 log zerolog.Logger 33 events *storage.Events 34 exeResults *storage.ExecutionResults 35 txResults *storage.TransactionResults 36 commits *storage.Commits 37 headers *storage.Headers 38 } 39 40 func TestHandler(t *testing.T) { 41 suite.Run(t, new(Suite)) 42 } 43 44 func (suite *Suite) SetupTest() { 45 suite.log = zerolog.Logger{} 46 suite.events = storage.NewEvents(suite.T()) 47 suite.exeResults = storage.NewExecutionResults(suite.T()) 48 suite.txResults = storage.NewTransactionResults(suite.T()) 49 suite.commits = storage.NewCommits(suite.T()) 50 suite.headers = storage.NewHeaders(suite.T()) 51 } 52 53 // TestExecuteScriptAtBlockID tests the ExecuteScriptAtBlockID API call 54 func (suite *Suite) TestExecuteScriptAtBlockID() { 55 // setup handler 56 mockEngine := mockEng.NewScriptExecutor(suite.T()) 57 handler := &handler{ 58 engine: mockEngine, 59 chain: flow.Mainnet, 60 commits: suite.commits, 61 } 62 63 // setup dummy request/response 64 ctx := context.Background() 65 mockIdentifier := unittest.IdentifierFixture() 66 script := []byte("dummy script") 67 arguments := [][]byte(nil) 68 executionReq := execution.ExecuteScriptAtBlockIDRequest{ 69 BlockId: mockIdentifier[:], 70 Script: script, 71 } 72 scriptExecValue := []byte{9, 10, 11} 73 executionResp := execution.ExecuteScriptAtBlockIDResponse{ 74 Value: scriptExecValue, 75 } 76 77 suite.Run("happy path with successful script execution", func() { 78 suite.commits.On("ByBlockID", mockIdentifier).Return(nil, nil).Once() 79 mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). 80 Return(scriptExecValue, nil).Once() 81 response, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) 82 suite.Require().NoError(err) 83 suite.Require().Equal(&executionResp, response) 84 mockEngine.AssertExpectations(suite.T()) 85 }) 86 87 suite.Run("valid request for unknown block", func() { 88 suite.commits.On("ByBlockID", mockIdentifier).Return(nil, realstorage.ErrNotFound).Once() 89 _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) 90 suite.Require().Error(err) 91 errors.Is(err, status.Error(codes.NotFound, "")) 92 }) 93 94 suite.Run("valid request with script execution failure", func() { 95 suite.commits.On("ByBlockID", mockIdentifier).Return(nil, nil).Once() 96 mockEngine.On("ExecuteScriptAtBlockID", ctx, script, arguments, mockIdentifier). 97 Return(nil, status.Error(codes.InvalidArgument, "")).Once() 98 _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReq) 99 suite.Require().Error(err) 100 errors.Is(err, status.Error(codes.InvalidArgument, "")) 101 }) 102 103 suite.Run("invalid request with nil blockID", func() { 104 executionReqWithNilBlock := execution.ExecuteScriptAtBlockIDRequest{ 105 BlockId: nil, 106 Script: script, 107 } 108 _, err := handler.ExecuteScriptAtBlockID(ctx, &executionReqWithNilBlock) 109 110 // check that an error was received 111 suite.Require().Error(err) 112 errors.Is(err, status.Error(codes.InvalidArgument, "")) 113 }) 114 115 } 116 117 // TestGetEventsForBlockIDs tests the GetEventsForBlockIDs API call 118 func (suite *Suite) TestGetEventsForBlockIDs() { 119 120 totalBlocks := 10 121 eventsPerBlock := 10 122 123 blockIDs := make([][]byte, totalBlocks) 124 expectedResult := make([]*execution.GetEventsForBlockIDsResponse_Result, totalBlocks) 125 126 // setup the events storage mock 127 for i := range blockIDs { 128 block := unittest.BlockFixture() 129 block.Header.Height = uint64(i) 130 id := block.ID() 131 blockIDs[i] = id[:] 132 eventsForBlock := make([]flow.Event, eventsPerBlock) 133 eventMessages := make([]*entities.Event, eventsPerBlock) 134 for j := range eventsForBlock { 135 e := unittest.EventFixture(flow.EventAccountCreated, uint32(j), uint32(j), unittest.IdentifierFixture(), 0) 136 eventsForBlock[j] = e 137 eventMessages[j] = convert.EventToMessage(e) 138 } 139 // expect one call to lookup result for each block ID 140 suite.commits.On("ByBlockID", id).Return(nil, nil).Once() 141 142 // expect one call to lookup events for each block ID 143 suite.events.On("ByBlockIDEventType", id, flow.EventAccountCreated).Return(eventsForBlock, nil).Once() 144 145 // expect one call to lookup each block 146 suite.headers.On("ByBlockID", id).Return(block.Header, nil).Once() 147 148 // create the expected result for this block 149 expectedResult[i] = &execution.GetEventsForBlockIDsResponse_Result{ 150 BlockId: id[:], 151 BlockHeight: block.Header.Height, 152 Events: eventMessages, 153 } 154 } 155 156 // create the handler 157 handler := &handler{ 158 headers: suite.headers, 159 events: suite.events, 160 exeResults: suite.exeResults, 161 transactionResults: suite.txResults, 162 commits: suite.commits, 163 chain: flow.Mainnet, 164 maxBlockRange: DefaultMaxBlockRange, 165 } 166 167 concoctReq := func(errType string, blockIDs [][]byte) *execution.GetEventsForBlockIDsRequest { 168 return &execution.GetEventsForBlockIDsRequest{ 169 Type: errType, 170 BlockIds: blockIDs, 171 } 172 } 173 174 // happy path - valid requests receives a valid response 175 suite.Run("happy path", func() { 176 177 // create a valid API request 178 req := concoctReq(string(flow.EventAccountCreated), blockIDs) 179 180 // execute the GetEventsForBlockIDs call 181 resp, err := handler.GetEventsForBlockIDs(context.Background(), req) 182 183 // check that a successful response is received 184 suite.Require().NoError(err) 185 186 actualResult := resp.GetResults() 187 suite.Require().ElementsMatch(expectedResult, actualResult) 188 }) 189 190 // failure path - empty even type in the request results in an error 191 suite.Run("request with empty event type", func() { 192 193 // create an API request with empty even type 194 req := concoctReq("", blockIDs) 195 196 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 197 198 // check that an error was received 199 suite.Require().Error(err) 200 errors.Is(err, status.Error(codes.InvalidArgument, "")) 201 }) 202 203 // failure path - empty block ids in request results in an error 204 suite.Run("request with empty block IDs", func() { 205 206 // create an API request with empty block ids 207 req := concoctReq(string(flow.EventAccountCreated), nil) 208 209 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 210 211 // check that an error was received 212 suite.Require().Error(err) 213 errors.Is(err, status.Error(codes.InvalidArgument, "")) 214 }) 215 216 // failure path - non-existent block id in request results in an error 217 suite.Run("request with non-existent block ID", func() { 218 219 id := unittest.IdentifierFixture() 220 221 // expect a storage call for the invalid id but return an error 222 suite.commits.On("ByBlockID", id).Return(nil, realstorage.ErrNotFound).Once() 223 224 // create an API request with the invalid block id 225 req := concoctReq(string(flow.EventAccountCreated), [][]byte{id[:]}) 226 227 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 228 229 // check that an error was received 230 suite.Require().Error(err) 231 errors.Is(err, status.Error(codes.NotFound, "")) 232 }) 233 234 // request for too many blocks - receives a InvalidArgument error 235 suite.Run("request for too many blocks", func() { 236 237 // update range so it's smaller than list of blockIDs 238 handler.maxBlockRange = totalBlocks / 2 239 240 // create a valid API request 241 req := concoctReq(string(flow.EventAccountCreated), blockIDs) 242 243 // execute the GetEventsForBlockIDs call 244 _, err := handler.GetEventsForBlockIDs(context.Background(), req) 245 246 // check that an error was received 247 suite.Require().Error(err) 248 errors.Is(err, status.Error(codes.InvalidArgument, "")) 249 }) 250 } 251 252 // Test GetAccountAtBlockID tests the GetAccountAtBlockID API call 253 func (suite *Suite) TestGetAccountAtBlockID() { 254 255 id := unittest.IdentifierFixture() 256 serviceAddress := flow.Mainnet.Chain().ServiceAddress() 257 258 serviceAccount := flow.Account{ 259 Address: serviceAddress, 260 } 261 262 mockEngine := mockEng.NewScriptExecutor(suite.T()) 263 264 // create the handler 265 handler := &handler{ 266 engine: mockEngine, 267 chain: flow.Mainnet, 268 commits: suite.commits, 269 } 270 271 createReq := func(id []byte, address []byte) *execution.GetAccountAtBlockIDRequest { 272 return &execution.GetAccountAtBlockIDRequest{ 273 Address: address, 274 BlockId: id, 275 } 276 } 277 278 suite.Run("happy path with valid request", func() { 279 280 // setup mock expectations 281 suite.commits.On("ByBlockID", id).Return(nil, nil).Once() 282 283 mockEngine.On("GetAccount", mock.Anything, serviceAddress, id).Return(&serviceAccount, nil).Once() 284 285 req := createReq(id[:], serviceAddress.Bytes()) 286 287 resp, err := handler.GetAccountAtBlockID(context.Background(), req) 288 289 suite.Require().NoError(err) 290 actualAccount := resp.GetAccount() 291 expectedAccount, err := convert.AccountToMessage(&serviceAccount) 292 suite.Require().NoError(err) 293 suite.Require().Empty( 294 cmp.Diff(expectedAccount, actualAccount, protocmp.Transform())) 295 }) 296 297 suite.Run("invalid request with unknown block id", func() { 298 299 // setup mock expectations 300 suite.commits.On("ByBlockID", id).Return(nil, realstorage.ErrNotFound).Once() 301 302 req := createReq(id[:], serviceAddress.Bytes()) 303 304 _, err := handler.GetAccountAtBlockID(context.Background(), req) 305 306 suite.Require().Error(err) 307 errors.Is(err, status.Error(codes.NotFound, "")) 308 }) 309 310 suite.Run("invalid request with nil block id", func() { 311 312 req := createReq(nil, serviceAddress.Bytes()) 313 314 _, err := handler.GetAccountAtBlockID(context.Background(), req) 315 316 suite.Require().Error(err) 317 errors.Is(err, status.Error(codes.InvalidArgument, "")) 318 }) 319 320 suite.Run("invalid request with nil root address", func() { 321 322 req := createReq(id[:], nil) 323 324 _, err := handler.GetAccountAtBlockID(context.Background(), req) 325 326 suite.Require().Error(err) 327 errors.Is(err, status.Error(codes.InvalidArgument, "")) 328 }) 329 330 suite.Run("valid request for unavailable data", func() { 331 suite.commits.On("ByBlockID", id).Return(nil, nil).Once() 332 333 expectedErr := fmt.Errorf( 334 "failed to execute script at block (%s): %w (%s). "+ 335 "this error usually happens if the reference "+ 336 "block for this script is not set to a recent block.", 337 id, 338 state.ErrExecutionStatePruned, 339 unittest.IdentifierFixture(), 340 ) 341 342 mockEngine.On("GetAccount", mock.Anything, serviceAddress, id).Return(nil, expectedErr).Once() 343 344 req := createReq(id[:], serviceAddress.Bytes()) 345 346 resp, err := handler.GetAccountAtBlockID(context.Background(), req) 347 suite.Assert().Nil(resp) 348 suite.Assert().Equal(codes.OutOfRange, status.Code(err)) 349 }) 350 } 351 352 // Test GetRegisterAtBlockID tests the GetRegisterAtBlockID API call 353 func (suite *Suite) TestGetRegisterAtBlockID() { 354 355 id := unittest.IdentifierFixture() 356 serviceAddress := flow.Mainnet.Chain().ServiceAddress() 357 validKey := []byte("exists") 358 359 mockEngine := mockEng.NewScriptExecutor(suite.T()) 360 361 // create the handler 362 handler := &handler{ 363 engine: mockEngine, 364 chain: flow.Mainnet, 365 } 366 367 createReq := func(id, owner, key []byte) *execution.GetRegisterAtBlockIDRequest { 368 return &execution.GetRegisterAtBlockIDRequest{ 369 RegisterOwner: owner, 370 RegisterKey: key, 371 BlockId: id, 372 } 373 } 374 375 suite.Run("happy path with valid request", func() { 376 377 // setup mock expectations 378 mockEngine.On("GetRegisterAtBlockID", mock.Anything, serviceAddress.Bytes(), validKey, id).Return([]uint8{1}, nil).Once() 379 380 req := createReq(id[:], serviceAddress.Bytes(), validKey) 381 resp, err := handler.GetRegisterAtBlockID(context.Background(), req) 382 383 suite.Require().NoError(err) 384 value := resp.GetValue() 385 suite.Require().NoError(err) 386 suite.Require().True(len(value) > 0) 387 }) 388 389 suite.Run("invalid request with bad address", func() { 390 badOwner := []byte("\uFFFD") 391 // return error 392 mockEngine.On("GetRegisterAtBlockID", mock.Anything, badOwner, validKey, id).Return(nil, errors.New("error")).Once() 393 394 req := createReq(id[:], badOwner, validKey) 395 _, err := handler.GetRegisterAtBlockID(context.Background(), req) 396 suite.Require().Error(err) 397 }) 398 } 399 400 // TestGetTransactionResult tests the GetTransactionResult and GetTransactionResultByIndex API calls 401 func (suite *Suite) TestGetTransactionResult() { 402 403 totalEvents := 10 404 block := unittest.BlockFixture() 405 tx := unittest.TransactionFixture() 406 bID := block.ID() 407 txID := tx.ID() 408 txIndex := rand.Uint32() 409 410 // setup the events storage mock 411 eventsForTx := make([]flow.Event, totalEvents) 412 eventMessages := make([]*entities.Event, totalEvents) 413 for j := range eventsForTx { 414 e := unittest.EventFixture(flow.EventAccountCreated, uint32(j), uint32(j), unittest.IdentifierFixture(), 0) 415 eventsForTx[j] = e 416 eventMessages[j] = convert.EventToMessage(e) 417 } 418 419 // expect a call to lookup events by block ID and transaction ID 420 suite.events.On("ByBlockIDTransactionID", bID, txID).Return(eventsForTx, nil) 421 422 // create the handler 423 createHandler := func(txResults *storage.TransactionResults) *handler { 424 handler := &handler{ 425 headers: suite.headers, 426 events: suite.events, 427 transactionResults: txResults, 428 commits: suite.commits, 429 chain: flow.Mainnet, 430 } 431 return handler 432 } 433 434 // concoctReq creates a GetEventsForBlockIDTransactionIDRequest 435 concoctReq := func(bID []byte, tID []byte) *execution.GetTransactionResultRequest { 436 return &execution.GetTransactionResultRequest{ 437 BlockId: bID, 438 TransactionId: tID, 439 } 440 } 441 442 // concoctIndexReq creates a GetTransactionByIndexRequest 443 concoctIndexReq := func(bID []byte, tIndex uint32) *execution.GetTransactionByIndexRequest { 444 return &execution.GetTransactionByIndexRequest{ 445 BlockId: bID, 446 Index: uint32(tIndex), 447 } 448 } 449 450 assertEqual := func(expected, actual *execution.GetTransactionResultResponse) { 451 suite.Require().Equal(expected.GetStatusCode(), actual.GetStatusCode()) 452 suite.Require().Equal(expected.GetErrorMessage(), actual.GetErrorMessage()) 453 suite.Require().ElementsMatch(expected.GetEvents(), actual.GetEvents()) 454 } 455 456 // happy path - valid requests receives all events for the given transaction 457 suite.Run("happy path with valid events and no transaction error", func() { 458 459 // create the expected result 460 expectedResult := &execution.GetTransactionResultResponse{ 461 StatusCode: 0, 462 ErrorMessage: "", 463 Events: eventMessages, 464 } 465 466 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 467 txResults := storage.NewTransactionResults(suite.T()) 468 txResult := flow.TransactionResult{ 469 TransactionID: flow.Identifier{}, 470 ErrorMessage: "", 471 } 472 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 473 474 handler := createHandler(txResults) 475 476 // create a valid API request 477 req := concoctReq(bID[:], txID[:]) 478 479 // execute the GetTransactionResult call 480 actualResult, err := handler.GetTransactionResult(context.Background(), req) 481 482 // check that a successful response is received 483 suite.Require().NoError(err) 484 485 // check that all fields in response are as expected 486 assertEqual(expectedResult, actualResult) 487 }) 488 489 // happy path - valid requests receives all events for the given transaction by index 490 suite.Run("index happy path with valid events and no transaction error", func() { 491 492 // create the expected result 493 expectedResult := &execution.GetTransactionResultResponse{ 494 StatusCode: 0, 495 ErrorMessage: "", 496 Events: eventMessages, 497 } 498 499 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 500 txResults := storage.NewTransactionResults(suite.T()) 501 txResult := flow.TransactionResult{ 502 TransactionID: flow.Identifier{}, 503 ErrorMessage: "", 504 } 505 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 506 507 // expect a call to lookup events by block ID and tx index 508 suite.events.On("ByBlockIDTransactionIndex", bID, txIndex).Return(eventsForTx, nil).Once() 509 510 handler := createHandler(txResults) 511 512 // create a valid API request 513 req := concoctIndexReq(bID[:], txIndex) 514 515 // execute the GetTransactionResult call 516 actualResult, err := handler.GetTransactionResultByIndex(context.Background(), req) 517 518 // check that a successful response is received 519 suite.Require().NoError(err) 520 521 // check that all fields in response are as expected 522 assertEqual(expectedResult, actualResult) 523 }) 524 525 // happy path - valid requests receives all events and an error for the given transaction 526 suite.Run("happy path with valid events and a transaction error", func() { 527 528 // create the expected result 529 expectedResult := &execution.GetTransactionResultResponse{ 530 StatusCode: 1, 531 ErrorMessage: "runtime error", 532 Events: eventMessages, 533 } 534 535 // setup the storage to return a transaction error 536 txResults := storage.NewTransactionResults(suite.T()) 537 txResult := flow.TransactionResult{ 538 TransactionID: txID, 539 ErrorMessage: "runtime error", 540 } 541 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 542 543 handler := createHandler(txResults) 544 545 // create a valid API request 546 req := concoctReq(bID[:], txID[:]) 547 548 // execute the GetEventsForBlockIDTransactionID call 549 actualResult, err := handler.GetTransactionResult(context.Background(), req) 550 551 // check that a successful response is received 552 suite.Require().NoError(err) 553 554 // check that all fields in response are as expected 555 assertEqual(expectedResult, actualResult) 556 }) 557 558 // happy path - valid requests receives all events and an error for the given transaction 559 suite.Run("index happy path with valid events and a transaction error", func() { 560 561 // create the expected result 562 expectedResult := &execution.GetTransactionResultResponse{ 563 StatusCode: 1, 564 ErrorMessage: "runtime error", 565 Events: eventMessages, 566 } 567 568 // setup the storage to return a transaction error 569 txResults := storage.NewTransactionResults(suite.T()) 570 txResult := flow.TransactionResult{ 571 TransactionID: txID, 572 ErrorMessage: "runtime error", 573 } 574 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 575 576 // expect a call to lookup events by block ID and tx index 577 suite.events.On("ByBlockIDTransactionIndex", bID, txIndex).Return(eventsForTx, nil).Once() 578 579 handler := createHandler(txResults) 580 581 // create a valid API request 582 req := concoctIndexReq(bID[:], txIndex) 583 584 // execute the GetEventsForBlockIDTransactionID call 585 actualResult, err := handler.GetTransactionResultByIndex(context.Background(), req) 586 587 // check that a successful response is received 588 suite.Require().NoError(err) 589 590 // check that all fields in response are as expected 591 assertEqual(expectedResult, actualResult) 592 593 // check that appropriate storage calls were made 594 }) 595 596 // failure path - nil transaction ID in the request results in an error 597 suite.Run("request with nil tx ID", func() { 598 599 // create an API request with transaction ID as nil 600 req := concoctReq(bID[:], nil) 601 602 txResults := storage.NewTransactionResults(suite.T()) 603 handler := createHandler(txResults) 604 605 _, err := handler.GetTransactionResult(context.Background(), req) 606 607 // check that an error was received 608 suite.Require().Error(err) 609 errors.Is(err, status.Error(codes.InvalidArgument, "")) 610 }) 611 612 // failure path - nil block id in the request results in an error 613 suite.Run("request with nil block ID", func() { 614 615 // create an API request with a nil block id 616 req := concoctReq(nil, txID[:]) 617 618 txResults := storage.NewTransactionResults(suite.T()) 619 handler := createHandler(txResults) 620 621 _, err := handler.GetTransactionResult(context.Background(), req) 622 623 // check that an error was received 624 suite.Require().Error(err) 625 errors.Is(err, status.Error(codes.InvalidArgument, "")) 626 }) 627 628 // failure path - nil block id in the index request results in an error 629 suite.Run("index request with nil block ID", func() { 630 631 // create an API request with a nil block id 632 req := concoctIndexReq(nil, txIndex) 633 634 txResults := storage.NewTransactionResults(suite.T()) 635 handler := createHandler(txResults) 636 637 _, err := handler.GetTransactionResultByIndex(context.Background(), req) 638 639 // check that an error was received 640 suite.Require().Error(err) 641 errors.Is(err, status.Error(codes.InvalidArgument, "")) 642 }) 643 644 // failure path - non-existent transaction ID in request results in an error 645 suite.Run("request with non-existent transaction ID", func() { 646 647 wrongTxID := unittest.IdentifierFixture() 648 649 // create an API request with the invalid transaction ID 650 req := concoctReq(bID[:], wrongTxID[:]) 651 652 // expect a storage call for the invalid tx ID but return an error 653 txResults := storage.NewTransactionResults(suite.T()) 654 txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, realstorage.ErrNotFound).Once() 655 656 handler := createHandler(txResults) 657 658 _, err := handler.GetTransactionResult(context.Background(), req) 659 660 // check that an error was received 661 suite.Require().Error(err) 662 errors.Is(err, status.Error(codes.NotFound, "")) 663 }) 664 665 // failure path - non-existent transaction ID in request results in an exception 666 suite.Run("request with non-existent transaction ID, exception", func() { 667 668 wrongTxID := unittest.IdentifierFixture() 669 670 // create an API request with the invalid transaction ID 671 req := concoctReq(bID[:], wrongTxID[:]) 672 673 // expect a storage call for the invalid tx ID but return an exception 674 txResults := storage.NewTransactionResults(suite.T()) 675 txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, errors.New("internal-error")).Once() 676 677 handler := createHandler(txResults) 678 679 _, err := handler.GetTransactionResult(context.Background(), req) 680 681 // check that an error was received 682 suite.Require().Error(err) 683 errors.Is(err, status.Error(codes.Internal, "")) 684 }) 685 686 // failure path - non-existent transaction index in request results in an error 687 suite.Run("request with non-existent transaction index", func() { 688 689 wrongTxIndex := txIndex + 1 690 691 // create an API request with the invalid transaction ID 692 req := concoctIndexReq(bID[:], wrongTxIndex) 693 694 // expect a storage call for the invalid tx ID but return an error 695 txResults := storage.NewTransactionResults(suite.T()) 696 txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, realstorage.ErrNotFound).Once() 697 698 handler := createHandler(txResults) 699 700 _, err := handler.GetTransactionResultByIndex(context.Background(), req) 701 702 // check that an error was received 703 suite.Require().Error(err) 704 errors.Is(err, status.Error(codes.NotFound, "")) 705 }) 706 707 // failure path - non-existent transaction index in request results in an exception 708 suite.Run("request with non-existent transaction index, exception", func() { 709 710 wrongTxIndex := txIndex + 1 711 712 // create an API request with the invalid transaction ID 713 req := concoctIndexReq(bID[:], wrongTxIndex) 714 715 // expect a storage call for the invalid tx ID but return an exception 716 txResults := storage.NewTransactionResults(suite.T()) 717 txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, errors.New("internal-error")).Once() 718 719 handler := createHandler(txResults) 720 721 _, err := handler.GetTransactionResultByIndex(context.Background(), req) 722 723 // check that an error was received 724 suite.Require().Error(err) 725 errors.Is(err, status.Error(codes.Internal, "")) 726 }) 727 } 728 729 // TestGetTransactionResultsByBlock tests TestGetTransactionResultsByBlockID API calls 730 func (suite *Suite) TestGetTransactionResultsByBlockID() { 731 732 totalEvents := 10 733 block := unittest.BlockFixture() 734 tx := unittest.TransactionFixture() 735 bID := block.ID() 736 nonexistingBlockID := unittest.IdentifierFixture() 737 tx1ID := tx.ID() 738 tx2ID := tx.ID() 739 //txIndex := rand.Uint32() 740 741 // setup the events storage mock 742 eventsForTx1 := make([]flow.Event, totalEvents-3) 743 eventsForTx2 := make([]flow.Event, totalEvents-len(eventsForTx1)) 744 eventsForBlock := make([]flow.Event, totalEvents) 745 746 convertedEventsForTx1 := make([]*entities.Event, len(eventsForTx1)) 747 convertedEventsForTx2 := make([]*entities.Event, len(eventsForTx2)) 748 749 for j := 0; j < len(eventsForTx1); j++ { 750 e := unittest.EventFixture(flow.EventAccountCreated, uint32(0), uint32(j), tx1ID, 0) 751 eventsForTx1[j] = e 752 eventsForBlock[j] = e 753 convertedEventsForTx1[j] = convert.EventToMessage(e) 754 } 755 for j := 0; j < len(eventsForTx2); j++ { 756 e := unittest.EventFixture(flow.EventAccountCreated, uint32(1), uint32(j), tx2ID, 0) 757 eventsForTx2[j] = e 758 eventsForBlock[len(eventsForTx1)+j] = e 759 convertedEventsForTx2[j] = convert.EventToMessage(e) 760 } 761 762 // create the handler 763 createHandler := func(txResults *storage.TransactionResults) *handler { 764 handler := &handler{ 765 headers: suite.headers, 766 events: suite.events, 767 transactionResults: txResults, 768 commits: suite.commits, 769 chain: flow.Mainnet, 770 } 771 return handler 772 } 773 774 // concoctReq creates a GetTransactionResultsByBlockIDRequest 775 concoctReq := func(bID []byte) *execution.GetTransactionsByBlockIDRequest { 776 return &execution.GetTransactionsByBlockIDRequest{ 777 BlockId: bID, 778 } 779 } 780 781 assertEqual := func(expected, actual *execution.GetTransactionResultsResponse) { 782 783 suite.Require().Len(expected.TransactionResults, len(actual.TransactionResults)) 784 785 for i, txResult := range actual.TransactionResults { 786 suite.Require().Equal(txResult.GetStatusCode(), actual.TransactionResults[i].GetStatusCode()) 787 suite.Require().Equal(txResult.GetErrorMessage(), actual.TransactionResults[i].GetErrorMessage()) 788 suite.Require().ElementsMatch(txResult.GetEvents(), actual.TransactionResults[i].GetEvents()) 789 } 790 } 791 792 // happy path - valid requests receives all events for the given transaction 793 suite.Run("happy path with valid events and no transaction error", func() { 794 795 suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() 796 797 // expect a call to lookup events by block ID and transaction ID 798 suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() 799 800 // create the expected result 801 expectedResult := &execution.GetTransactionResultsResponse{ 802 TransactionResults: []*execution.GetTransactionResultResponse{ 803 { 804 StatusCode: 0, 805 ErrorMessage: "", 806 Events: convertedEventsForTx1, 807 }, 808 { 809 StatusCode: 0, 810 ErrorMessage: "", 811 Events: convertedEventsForTx1, 812 }, 813 }, 814 } 815 816 // expect a call to lookup transaction result by block ID return a result with no error 817 txResultsMock := storage.NewTransactionResults(suite.T()) 818 txResults := []flow.TransactionResult{ 819 { 820 TransactionID: tx1ID, 821 ErrorMessage: "", 822 }, 823 { 824 TransactionID: tx2ID, 825 ErrorMessage: "", 826 }, 827 } 828 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 829 830 handler := createHandler(txResultsMock) 831 832 // create a valid API request 833 req := concoctReq(bID[:]) 834 835 // execute the GetTransactionResult call 836 actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 837 838 // check that a successful response is received 839 suite.Require().NoError(err) 840 841 // check that all fields in response are as expected 842 assertEqual(expectedResult, actualResult) 843 }) 844 845 // happy path - valid requests receives all events and an error for the given transaction 846 suite.Run("happy path with valid events and a transaction error", func() { 847 848 suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() 849 850 // expect a call to lookup events by block ID and transaction ID 851 suite.events.On("ByBlockID", bID).Return(eventsForBlock, nil).Once() 852 853 // create the expected result 854 expectedResult := &execution.GetTransactionResultsResponse{ 855 TransactionResults: []*execution.GetTransactionResultResponse{ 856 { 857 StatusCode: 0, 858 ErrorMessage: "", 859 Events: convertedEventsForTx1, 860 }, 861 { 862 StatusCode: 1, 863 ErrorMessage: "runtime error", 864 Events: convertedEventsForTx2, 865 }, 866 }, 867 } 868 869 // expect a call to lookup transaction result by block ID return a result with no error 870 txResultsMock := storage.NewTransactionResults(suite.T()) 871 txResults := []flow.TransactionResult{ 872 { 873 TransactionID: tx1ID, 874 ErrorMessage: "", 875 }, 876 { 877 TransactionID: tx2ID, 878 ErrorMessage: "runtime error", 879 }, 880 } 881 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 882 883 handler := createHandler(txResultsMock) 884 885 // create a valid API request 886 req := concoctReq(bID[:]) 887 888 // execute the GetTransactionResult call 889 actualResult, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 890 891 // check that a successful response is received 892 suite.Require().NoError(err) 893 894 // check that all fields in response are as expected 895 assertEqual(expectedResult, actualResult) 896 }) 897 898 // failure path - nil block id in the request results in an error 899 suite.Run("request with nil block ID", func() { 900 901 // create an API request with a nil block id 902 req := concoctReq(nil) 903 904 txResults := storage.NewTransactionResults(suite.T()) 905 handler := createHandler(txResults) 906 907 _, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 908 909 // check that an error was received 910 suite.Require().Error(err) 911 errors.Is(err, status.Error(codes.InvalidArgument, "")) 912 }) 913 914 // failure path - nonexisting block id in the request results in not found error 915 suite.Run("request with nonexisting block ID", func() { 916 917 suite.commits.On("ByBlockID", nonexistingBlockID).Return(nil, realstorage.ErrNotFound).Once() 918 919 txResultsMock := storage.NewTransactionResults(suite.T()) 920 handler := createHandler(txResultsMock) 921 922 // create a valid API request 923 req := concoctReq(nonexistingBlockID[:]) 924 925 // execute the GetTransactionResult call 926 _, err := handler.GetTransactionResultsByBlockID(context.Background(), req) 927 928 // check that an error was received 929 suite.Require().Error(err) 930 errors.Is(err, status.Error(codes.NotFound, "")) 931 }) 932 } 933 934 // TestGetTransactionErrorMessage tests the GetTransactionErrorMessage and GetTransactionErrorMessageByIndex API calls 935 func (suite *Suite) TestGetTransactionErrorMessage() { 936 block := unittest.BlockFixture() 937 tx := unittest.TransactionFixture() 938 bID := block.ID() 939 txID := tx.ID() 940 txIndex := rand.Uint32() 941 942 // create the handler 943 createHandler := func(txResults *storage.TransactionResults) *handler { 944 handler := &handler{ 945 headers: suite.headers, 946 events: suite.events, 947 transactionResults: txResults, 948 commits: suite.commits, 949 chain: flow.Mainnet, 950 } 951 return handler 952 } 953 954 // concoctReq creates a GetTransactionErrorMessageRequest 955 concoctReq := func(bID []byte, tID []byte) *execution.GetTransactionErrorMessageRequest { 956 return &execution.GetTransactionErrorMessageRequest{ 957 BlockId: bID, 958 TransactionId: tID, 959 } 960 } 961 962 // concoctIndexReq creates a GetTransactionErrorMessageByIndexRequest 963 concoctIndexReq := func(bID []byte, tIndex uint32) *execution.GetTransactionErrorMessageByIndexRequest { 964 return &execution.GetTransactionErrorMessageByIndexRequest{ 965 BlockId: bID, 966 Index: tIndex, 967 } 968 } 969 970 suite.Run("happy path - by tx id - no transaction error", func() { 971 972 // create the expected result 973 expectedResult := &execution.GetTransactionErrorMessageResponse{ 974 TransactionId: convert.IdentifierToMessage(txID), 975 ErrorMessage: "", 976 } 977 978 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 979 txResults := storage.NewTransactionResults(suite.T()) 980 txResult := flow.TransactionResult{ 981 TransactionID: txID, 982 ErrorMessage: "", 983 } 984 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 985 986 handler := createHandler(txResults) 987 988 // create a valid API request 989 req := concoctReq(bID[:], txID[:]) 990 991 // execute the GetTransactionErrorMessage call 992 actualResult, err := handler.GetTransactionErrorMessage(context.Background(), req) 993 994 // check that a successful response is received 995 suite.Require().NoError(err) 996 997 // check that all fields in response are as expected 998 suite.Equal(expectedResult, actualResult) 999 }) 1000 1001 suite.Run("happy path - at index - no transaction error", func() { 1002 1003 // create the expected result 1004 expectedResult := &execution.GetTransactionErrorMessageResponse{ 1005 TransactionId: convert.IdentifierToMessage(txID), 1006 ErrorMessage: "", 1007 } 1008 1009 // expect a call to lookup transaction result by block ID and transaction ID, return a result with no error 1010 txResults := storage.NewTransactionResults(suite.T()) 1011 txResult := flow.TransactionResult{ 1012 TransactionID: txID, 1013 ErrorMessage: "", 1014 } 1015 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 1016 1017 handler := createHandler(txResults) 1018 1019 // create a valid API request 1020 req := concoctIndexReq(bID[:], txIndex) 1021 1022 // execute the GetTransactionResult call 1023 actualResult, err := handler.GetTransactionErrorMessageByIndex(context.Background(), req) 1024 1025 // check that a successful response is received 1026 suite.Require().NoError(err) 1027 1028 // check that all fields in response are as expected 1029 suite.Equal(expectedResult, actualResult) 1030 }) 1031 1032 suite.Run("happy path - by tx id - transaction error", func() { 1033 1034 // create the expected result 1035 expectedResult := &execution.GetTransactionErrorMessageResponse{ 1036 TransactionId: convert.IdentifierToMessage(txID), 1037 ErrorMessage: "runtime error", 1038 } 1039 1040 // setup the storage to return a transaction error 1041 txResults := storage.NewTransactionResults(suite.T()) 1042 txResult := flow.TransactionResult{ 1043 TransactionID: txID, 1044 ErrorMessage: "runtime error", 1045 } 1046 txResults.On("ByBlockIDTransactionID", bID, txID).Return(&txResult, nil).Once() 1047 1048 handler := createHandler(txResults) 1049 1050 // create a valid API request 1051 req := concoctReq(bID[:], txID[:]) 1052 1053 // execute the GetTransactionErrorMessage call 1054 actualResult, err := handler.GetTransactionErrorMessage(context.Background(), req) 1055 1056 // check that a successful response is received 1057 suite.Require().NoError(err) 1058 1059 // check that all fields in response are as expected 1060 suite.Equal(expectedResult, actualResult) 1061 }) 1062 1063 suite.Run("happy path - at index - transaction error", func() { 1064 1065 // create the expected result 1066 expectedResult := &execution.GetTransactionErrorMessageResponse{ 1067 TransactionId: convert.IdentifierToMessage(txID), 1068 ErrorMessage: "runtime error", 1069 } 1070 1071 // setup the storage to return a transaction error 1072 txResults := storage.NewTransactionResults(suite.T()) 1073 txResult := flow.TransactionResult{ 1074 TransactionID: txID, 1075 ErrorMessage: "runtime error", 1076 } 1077 txResults.On("ByBlockIDTransactionIndex", bID, txIndex).Return(&txResult, nil).Once() 1078 1079 handler := createHandler(txResults) 1080 1081 // create a valid API request 1082 req := concoctIndexReq(bID[:], txIndex) 1083 1084 // execute the GetTransactionErrorMessageByIndex call 1085 actualResult, err := handler.GetTransactionErrorMessageByIndex(context.Background(), req) 1086 1087 // check that a successful response is received 1088 suite.Require().NoError(err) 1089 1090 // check that all fields in response are as expected 1091 suite.Equal(expectedResult, actualResult) 1092 }) 1093 1094 // failure path - nil transaction ID in the request results in an error 1095 suite.Run("request with nil tx ID", func() { 1096 1097 // create an API request with transaction ID as nil 1098 req := concoctReq(bID[:], nil) 1099 1100 txResults := storage.NewTransactionResults(suite.T()) 1101 handler := createHandler(txResults) 1102 1103 _, err := handler.GetTransactionErrorMessage(context.Background(), req) 1104 1105 // check that an error was received 1106 suite.Require().Error(err) 1107 errors.Is(err, status.Error(codes.InvalidArgument, "")) 1108 }) 1109 1110 // failure path - nil block id in the request results in an error 1111 suite.Run("request with nil block ID", func() { 1112 1113 // create an API request with a nil block id 1114 req := concoctReq(nil, txID[:]) 1115 1116 txResults := storage.NewTransactionResults(suite.T()) 1117 handler := createHandler(txResults) 1118 1119 _, err := handler.GetTransactionErrorMessage(context.Background(), req) 1120 1121 // check that an error was received 1122 suite.Require().Error(err) 1123 errors.Is(err, status.Error(codes.InvalidArgument, "")) 1124 }) 1125 1126 // failure path - nil block id in the index request results in an error 1127 suite.Run("index request with nil block ID", func() { 1128 1129 // create an API request with a nil block id 1130 req := concoctIndexReq(nil, txIndex) 1131 1132 txResults := storage.NewTransactionResults(suite.T()) 1133 handler := createHandler(txResults) 1134 1135 _, err := handler.GetTransactionErrorMessageByIndex(context.Background(), req) 1136 1137 // check that an error was received 1138 suite.Require().Error(err) 1139 errors.Is(err, status.Error(codes.InvalidArgument, "")) 1140 }) 1141 1142 // failure path - non-existent transaction ID in request results in an error 1143 suite.Run("request with non-existent transaction ID", func() { 1144 1145 wrongTxID := unittest.IdentifierFixture() 1146 1147 // create an API request with the invalid transaction ID 1148 req := concoctReq(bID[:], wrongTxID[:]) 1149 1150 // expect a storage call for the invalid tx ID but return an error 1151 txResults := storage.NewTransactionResults(suite.T()) 1152 txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, realstorage.ErrNotFound).Once() 1153 1154 handler := createHandler(txResults) 1155 1156 _, err := handler.GetTransactionErrorMessage(context.Background(), req) 1157 1158 // check that an error was received 1159 suite.Require().Error(err) 1160 errors.Is(err, status.Error(codes.NotFound, "")) 1161 }) 1162 1163 // failure path - non-existent transaction ID in request results in an exception 1164 suite.Run("request with non-existent transaction ID, exception", func() { 1165 1166 wrongTxID := unittest.IdentifierFixture() 1167 1168 // create an API request with the invalid transaction ID 1169 req := concoctReq(bID[:], wrongTxID[:]) 1170 1171 // expect a storage call for the invalid tx ID but return an exception 1172 txResults := storage.NewTransactionResults(suite.T()) 1173 txResults.On("ByBlockIDTransactionID", bID, wrongTxID).Return(nil, errors.New("internal-error")).Once() 1174 1175 handler := createHandler(txResults) 1176 1177 _, err := handler.GetTransactionErrorMessage(context.Background(), req) 1178 1179 // check that an error was received 1180 suite.Require().Error(err) 1181 errors.Is(err, status.Error(codes.Internal, "")) 1182 }) 1183 1184 // failure path - non-existent transaction index in request results in an error 1185 suite.Run("request with non-existent transaction index", func() { 1186 1187 wrongTxIndex := txIndex + 1 1188 1189 // create an API request with the invalid transaction ID 1190 req := concoctIndexReq(bID[:], wrongTxIndex) 1191 1192 // expect a storage call for the invalid tx ID but return an error 1193 txResults := storage.NewTransactionResults(suite.T()) 1194 txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, realstorage.ErrNotFound).Once() 1195 1196 handler := createHandler(txResults) 1197 1198 _, err := handler.GetTransactionErrorMessageByIndex(context.Background(), req) 1199 1200 // check that an error was received 1201 suite.Require().Error(err) 1202 errors.Is(err, status.Error(codes.NotFound, "")) 1203 }) 1204 1205 // failure path - non-existent transaction index in request results in an exception 1206 suite.Run("request with non-existent transaction index, exception", func() { 1207 1208 wrongTxIndex := txIndex + 1 1209 1210 // create an API request with the invalid transaction ID 1211 req := concoctIndexReq(bID[:], wrongTxIndex) 1212 1213 // expect a storage call for the invalid tx ID but return an exception 1214 txResults := storage.NewTransactionResults(suite.T()) 1215 txResults.On("ByBlockIDTransactionIndex", bID, wrongTxIndex).Return(nil, errors.New("internal-error")).Once() 1216 1217 handler := createHandler(txResults) 1218 1219 _, err := handler.GetTransactionErrorMessageByIndex(context.Background(), req) 1220 1221 // check that an error was received 1222 suite.Require().Error(err) 1223 errors.Is(err, status.Error(codes.Internal, "")) 1224 }) 1225 } 1226 1227 // TestGetTransactionErrorMessagesByBlockID tests GetTransactionErrorMessagesByBlockID API calls 1228 func (suite *Suite) TestGetTransactionErrorMessagesByBlockID() { 1229 block := unittest.BlockFixture() 1230 tx := unittest.TransactionFixture() 1231 bID := block.ID() 1232 nonexistingBlockID := unittest.IdentifierFixture() 1233 tx1ID := tx.ID() 1234 tx2ID := tx.ID() 1235 tx3ID := tx.ID() 1236 1237 // create the handler 1238 createHandler := func(txResults *storage.TransactionResults) *handler { 1239 handler := &handler{ 1240 headers: suite.headers, 1241 events: suite.events, 1242 transactionResults: txResults, 1243 commits: suite.commits, 1244 chain: flow.Mainnet, 1245 } 1246 return handler 1247 } 1248 1249 // concoctReq creates a GetTransactionErrorMessagesByBlockIDRequest 1250 concoctReq := func(bID []byte) *execution.GetTransactionErrorMessagesByBlockIDRequest { 1251 return &execution.GetTransactionErrorMessagesByBlockIDRequest{ 1252 BlockId: bID, 1253 } 1254 } 1255 1256 // happy path - if no transaction errors are found, an empty list is returned 1257 suite.Run("happy path with no transaction error", func() { 1258 suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() 1259 1260 // create the expected result 1261 expectedResult := &execution.GetTransactionErrorMessagesResponse{ 1262 Results: []*execution.GetTransactionErrorMessagesResponse_Result{}, 1263 } 1264 1265 // expect a call to lookup transaction result by block ID return a result with no error 1266 txResultsMock := storage.NewTransactionResults(suite.T()) 1267 txResults := []flow.TransactionResult{ 1268 { 1269 TransactionID: tx1ID, 1270 ErrorMessage: "", 1271 }, 1272 { 1273 TransactionID: tx2ID, 1274 ErrorMessage: "", 1275 }, 1276 } 1277 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 1278 1279 handler := createHandler(txResultsMock) 1280 1281 // create a valid API request 1282 req := concoctReq(bID[:]) 1283 1284 // execute the GetTransactionErrorMessagesByBlockID call 1285 actualResult, err := handler.GetTransactionErrorMessagesByBlockID(context.Background(), req) 1286 1287 // check that a successful response is received 1288 suite.Require().NoError(err) 1289 1290 // check that all fields in response are as expected 1291 suite.Assert().ElementsMatch(expectedResult.Results, actualResult.Results) 1292 }) 1293 1294 // happy path - valid requests receives error messages for all failed transactions. 1295 suite.Run("happy path with transaction errors", func() { 1296 1297 suite.commits.On("ByBlockID", bID).Return(nil, nil).Once() 1298 1299 // create the expected result 1300 expectedResult := &execution.GetTransactionErrorMessagesResponse{ 1301 Results: []*execution.GetTransactionErrorMessagesResponse_Result{ 1302 { 1303 TransactionId: convert.IdentifierToMessage(tx2ID), 1304 Index: 1, 1305 ErrorMessage: "runtime error", 1306 }, 1307 { 1308 TransactionId: convert.IdentifierToMessage(tx3ID), 1309 Index: 2, 1310 ErrorMessage: "runtime error", 1311 }, 1312 }, 1313 } 1314 1315 // expect a call to lookup transaction result by block ID return a result with no error 1316 txResultsMock := storage.NewTransactionResults(suite.T()) 1317 txResults := []flow.TransactionResult{ 1318 { 1319 TransactionID: tx1ID, 1320 ErrorMessage: "", 1321 }, 1322 { 1323 TransactionID: tx2ID, 1324 ErrorMessage: "runtime error", 1325 }, 1326 { 1327 TransactionID: tx3ID, 1328 ErrorMessage: "runtime error", 1329 }, 1330 } 1331 txResultsMock.On("ByBlockID", bID).Return(txResults, nil).Once() 1332 1333 handler := createHandler(txResultsMock) 1334 1335 // create a valid API request 1336 req := concoctReq(bID[:]) 1337 1338 // execute the GetTransactionErrorMessagesByBlockID call 1339 actualResult, err := handler.GetTransactionErrorMessagesByBlockID(context.Background(), req) 1340 1341 // check that a successful response is received 1342 suite.Require().NoError(err) 1343 1344 // check that all fields in response are as expected 1345 suite.Assert().ElementsMatch(expectedResult.Results, actualResult.Results) 1346 }) 1347 1348 // failure path - nil block id in the request results in an error 1349 suite.Run("request with nil block ID", func() { 1350 1351 // create an API request with a nil block id 1352 req := concoctReq(nil) 1353 1354 txResults := storage.NewTransactionResults(suite.T()) 1355 handler := createHandler(txResults) 1356 1357 _, err := handler.GetTransactionErrorMessagesByBlockID(context.Background(), req) 1358 1359 // check that an error was received 1360 suite.Require().Error(err) 1361 errors.Is(err, status.Error(codes.InvalidArgument, "")) 1362 }) 1363 1364 // failure path - nonexisting block id in the request results in not found error 1365 suite.Run("request with nonexisting block ID", func() { 1366 1367 suite.commits.On("ByBlockID", nonexistingBlockID).Return(nil, realstorage.ErrNotFound).Once() 1368 1369 txResultsMock := storage.NewTransactionResults(suite.T()) 1370 handler := createHandler(txResultsMock) 1371 1372 // create a valid API request 1373 req := concoctReq(nonexistingBlockID[:]) 1374 1375 // execute the GetTransactionResult call 1376 _, err := handler.GetTransactionErrorMessagesByBlockID(context.Background(), req) 1377 1378 // check that an error was received 1379 suite.Require().Error(err) 1380 errors.Is(err, status.Error(codes.NotFound, "")) 1381 }) 1382 }