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