github.com/koko1123/flow-go-1@v0.29.6/engine/execution/rpc/engine_test.go (about)

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