github.com/onflow/flow-go@v0.33.17/engine/access/state_stream/backend/handler_test.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/stretchr/testify/suite"
    15  	pb "google.golang.org/genproto/googleapis/bytestream"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/codes"
    18  	"google.golang.org/grpc/status"
    19  
    20  	"github.com/onflow/cadence/encoding/ccf"
    21  	jsoncdc "github.com/onflow/cadence/encoding/json"
    22  	"github.com/onflow/flow/protobuf/go/flow/entities"
    23  	"github.com/onflow/flow/protobuf/go/flow/executiondata"
    24  
    25  	"github.com/onflow/flow-go/engine/access/state_stream"
    26  	ssmock "github.com/onflow/flow-go/engine/access/state_stream/mock"
    27  	"github.com/onflow/flow-go/engine/common/rpc/convert"
    28  	"github.com/onflow/flow-go/model/flow"
    29  	"github.com/onflow/flow-go/storage"
    30  	"github.com/onflow/flow-go/utils/unittest"
    31  	"github.com/onflow/flow-go/utils/unittest/generator"
    32  )
    33  
    34  func TestHeartbeatResponseSuite(t *testing.T) {
    35  	suite.Run(t, new(HandlerTestSuite))
    36  }
    37  
    38  type HandlerTestSuite struct {
    39  	BackendExecutionDataSuite
    40  	handler *Handler
    41  }
    42  
    43  // fakeReadServerImpl is an utility structure for receiving response from grpc handler without building a complete pipeline with client and server.
    44  // It allows to receive streamed events pushed by server in buffered channel that can be later used to assert correctness of responses
    45  type fakeReadServerImpl struct {
    46  	pb.ByteStream_ReadServer
    47  	ctx      context.Context
    48  	received chan *executiondata.SubscribeEventsResponse
    49  }
    50  
    51  var _ executiondata.ExecutionDataAPI_SubscribeEventsServer = (*fakeReadServerImpl)(nil)
    52  
    53  func (fake *fakeReadServerImpl) Context() context.Context {
    54  	return fake.ctx
    55  }
    56  
    57  func (fake *fakeReadServerImpl) Send(response *executiondata.SubscribeEventsResponse) error {
    58  	fake.received <- response
    59  	return nil
    60  }
    61  
    62  func (s *HandlerTestSuite) SetupTest() {
    63  	s.BackendExecutionDataSuite.SetupTest()
    64  	chain := flow.MonotonicEmulator.Chain()
    65  	s.handler = NewHandler(s.backend, chain, makeConfig(5))
    66  }
    67  
    68  // TestHeartbeatResponse tests the periodic heartbeat response.
    69  //
    70  // Test Steps:
    71  // - Generate different events in blocks.
    72  // - Create different filters for generated events.
    73  // - Wait for either responses with filtered events or heartbeat responses.
    74  // - Verify that the responses are being sent with proper heartbeat interval.
    75  func (s *HandlerTestSuite) TestHeartbeatResponse() {
    76  	reader := &fakeReadServerImpl{
    77  		ctx:      context.Background(),
    78  		received: make(chan *executiondata.SubscribeEventsResponse, 100),
    79  	}
    80  
    81  	// notify backend block is available
    82  	s.backend.setHighestHeight(s.blocks[len(s.blocks)-1].Header.Height)
    83  
    84  	s.Run("All events filter", func() {
    85  		// create empty event filter
    86  		filter := &executiondata.EventFilter{}
    87  		// create subscribe events request, set the created filter and heartbeatInterval
    88  		req := &executiondata.SubscribeEventsRequest{
    89  			StartBlockHeight:  0,
    90  			Filter:            filter,
    91  			HeartbeatInterval: 1,
    92  		}
    93  
    94  		// subscribe for events
    95  		go func() {
    96  			err := s.handler.SubscribeEvents(req, reader)
    97  			require.NoError(s.T(), err)
    98  		}()
    99  
   100  		for _, b := range s.blocks {
   101  			// consume execution data from subscription
   102  			unittest.RequireReturnsBefore(s.T(), func() {
   103  				resp, ok := <-reader.received
   104  				require.True(s.T(), ok, "channel closed while waiting for exec data for block %d %v", b.Header.Height, b.ID())
   105  
   106  				blockID, err := convert.BlockID(resp.BlockId)
   107  				require.NoError(s.T(), err)
   108  				require.Equal(s.T(), b.Header.ID(), blockID)
   109  				require.Equal(s.T(), b.Header.Height, resp.BlockHeight)
   110  			}, time.Second, fmt.Sprintf("timed out waiting for exec data for block %d %v", b.Header.Height, b.ID()))
   111  		}
   112  	})
   113  
   114  	s.Run("Event A.0x1.Foo.Bar filter with heartbeat interval 1", func() {
   115  		// create A.0x1.Foo.Bar event filter
   116  		pbFilter := &executiondata.EventFilter{
   117  			EventType: []string{string(testEventTypes[0])},
   118  			Contract:  nil,
   119  			Address:   nil,
   120  		}
   121  		// create subscribe events request, set the created filter and heartbeatInterval
   122  		req := &executiondata.SubscribeEventsRequest{
   123  			StartBlockHeight:  0,
   124  			Filter:            pbFilter,
   125  			HeartbeatInterval: 1,
   126  		}
   127  
   128  		// subscribe for events
   129  		go func() {
   130  			err := s.handler.SubscribeEvents(req, reader)
   131  			require.NoError(s.T(), err)
   132  		}()
   133  
   134  		for _, b := range s.blocks {
   135  
   136  			// consume execution data from subscription
   137  			unittest.RequireReturnsBefore(s.T(), func() {
   138  				resp, ok := <-reader.received
   139  				require.True(s.T(), ok, "channel closed while waiting for exec data for block %d %v", b.Header.Height, b.ID())
   140  
   141  				blockID, err := convert.BlockID(resp.BlockId)
   142  				require.NoError(s.T(), err)
   143  				require.Equal(s.T(), b.Header.ID(), blockID)
   144  				require.Equal(s.T(), b.Header.Height, resp.BlockHeight)
   145  			}, time.Second, fmt.Sprintf("timed out waiting for exec data for block %d %v", b.Header.Height, b.ID()))
   146  		}
   147  	})
   148  
   149  	s.Run("Non existent filter with heartbeat interval 2", func() {
   150  		// create non existent filter
   151  		pbFilter := &executiondata.EventFilter{
   152  			EventType: []string{"A.0x1.NonExistent.Event"},
   153  			Contract:  nil,
   154  			Address:   nil,
   155  		}
   156  
   157  		// create subscribe events request, set the created filter and heartbeatInterval
   158  		req := &executiondata.SubscribeEventsRequest{
   159  			StartBlockHeight:  0,
   160  			Filter:            pbFilter,
   161  			HeartbeatInterval: 2,
   162  		}
   163  
   164  		// subscribe for events
   165  		go func() {
   166  			err := s.handler.SubscribeEvents(req, reader)
   167  			require.NoError(s.T(), err)
   168  		}()
   169  
   170  		// expect a response for every other block
   171  		expectedBlocks := make([]*flow.Block, 0)
   172  		for i, block := range s.blocks {
   173  			if (i+1)%int(req.HeartbeatInterval) == 0 {
   174  				expectedBlocks = append(expectedBlocks, block)
   175  			}
   176  		}
   177  
   178  		require.Len(s.T(), expectedBlocks, len(s.blocks)/int(req.HeartbeatInterval))
   179  
   180  		for _, b := range expectedBlocks {
   181  			// consume execution data from subscription
   182  			unittest.RequireReturnsBefore(s.T(), func() {
   183  				resp, ok := <-reader.received
   184  				require.True(s.T(), ok, "channel closed while waiting for exec data for block %d %v", b.Header.Height, b.ID())
   185  
   186  				blockID, err := convert.BlockID(resp.BlockId)
   187  				require.NoError(s.T(), err)
   188  				require.Equal(s.T(), b.Header.Height, resp.BlockHeight)
   189  				require.Equal(s.T(), b.Header.ID(), blockID)
   190  				require.Empty(s.T(), resp.Events)
   191  			}, time.Second, fmt.Sprintf("timed out waiting for exec data for block %d %v", b.Header.Height, b.ID()))
   192  		}
   193  	})
   194  }
   195  
   196  // TestGetExecutionDataByBlockID tests the execution data by block id with different event encoding versions.
   197  func TestGetExecutionDataByBlockID(t *testing.T) {
   198  	ctx, cancel := context.WithCancel(context.Background())
   199  	defer cancel()
   200  
   201  	ccfEvents, jsonEvents := generateEvents(t, 3)
   202  
   203  	tests := []struct {
   204  		eventVersion entities.EventEncodingVersion
   205  		expected     []flow.Event
   206  	}{
   207  		{
   208  			entities.EventEncodingVersion_JSON_CDC_V0,
   209  			jsonEvents,
   210  		},
   211  		{
   212  			entities.EventEncodingVersion_CCF_V0,
   213  			ccfEvents,
   214  		},
   215  	}
   216  
   217  	for _, test := range tests {
   218  		t.Run(fmt.Sprintf("test %s event encoding version", test.eventVersion.String()), func(t *testing.T) {
   219  			result := unittest.BlockExecutionDataFixture(
   220  				unittest.WithChunkExecutionDatas(
   221  					unittest.ChunkExecutionDataFixture(t, 1024, unittest.WithChunkEvents(ccfEvents)),
   222  					unittest.ChunkExecutionDataFixture(t, 1024, unittest.WithChunkEvents(ccfEvents)),
   223  				),
   224  			)
   225  			blockID := result.BlockID
   226  
   227  			api := ssmock.NewAPI(t)
   228  			api.On("GetExecutionDataByBlockID", mock.Anything, blockID).Return(result, nil)
   229  
   230  			h := NewHandler(api, flow.Localnet.Chain(), makeConfig(1))
   231  
   232  			response, err := h.GetExecutionDataByBlockID(ctx, &executiondata.GetExecutionDataByBlockIDRequest{
   233  				BlockId:              blockID[:],
   234  				EventEncodingVersion: test.eventVersion,
   235  			})
   236  			require.NoError(t, err)
   237  			require.NotNil(t, response)
   238  
   239  			blockExecutionData := response.GetBlockExecutionData()
   240  			require.Equal(t, blockID[:], blockExecutionData.GetBlockId())
   241  
   242  			convertedExecData, err := convert.MessageToBlockExecutionData(blockExecutionData, flow.Testnet.Chain())
   243  			require.NoError(t, err)
   244  
   245  			// Verify that the payload is valid
   246  			for _, chunk := range convertedExecData.ChunkExecutionDatas {
   247  				for i, e := range chunk.Events {
   248  					assert.Equal(t, test.expected[i], e)
   249  
   250  					var err error
   251  					if test.eventVersion == entities.EventEncodingVersion_JSON_CDC_V0 {
   252  						_, err = jsoncdc.Decode(nil, e.Payload)
   253  					} else {
   254  						_, err = ccf.Decode(nil, e.Payload)
   255  					}
   256  					require.NoError(t, err)
   257  				}
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  // TestExecutionDataStream tests the execution data stream with different event encoding versions.
   264  func TestExecutionDataStream(t *testing.T) {
   265  	t.Parallel()
   266  
   267  	ctx, cancel := context.WithCancel(context.Background())
   268  	defer cancel()
   269  
   270  	// Send a single response.
   271  	blockHeight := uint64(1)
   272  
   273  	// Helper function to perform a stream request and handle responses.
   274  	makeStreamRequest := func(
   275  		stream *StreamMock[executiondata.SubscribeExecutionDataRequest, executiondata.SubscribeExecutionDataResponse],
   276  		api *ssmock.API,
   277  		request *executiondata.SubscribeExecutionDataRequest,
   278  		response *ExecutionDataResponse,
   279  	) {
   280  		sub := NewSubscription(1)
   281  
   282  		api.On("SubscribeExecutionData", mock.Anything, flow.ZeroID, uint64(0), mock.Anything).Return(sub)
   283  
   284  		h := NewHandler(api, flow.Localnet.Chain(), makeConfig(1))
   285  
   286  		wg := sync.WaitGroup{}
   287  		wg.Add(1)
   288  		go func() {
   289  			wg.Done()
   290  			err := h.SubscribeExecutionData(request, stream)
   291  			require.NoError(t, err)
   292  			t.Log("subscription closed")
   293  		}()
   294  		wg.Wait()
   295  
   296  		err := sub.Send(ctx, response, 100*time.Millisecond)
   297  		require.NoError(t, err)
   298  
   299  		// Notify end of data.
   300  		sub.Close()
   301  	}
   302  
   303  	// handleExecutionDataStreamResponses handles responses from the execution data stream.
   304  	handleExecutionDataStreamResponses := func(
   305  		stream *StreamMock[executiondata.SubscribeExecutionDataRequest, executiondata.SubscribeExecutionDataResponse],
   306  		version entities.EventEncodingVersion,
   307  		expectedEvents []flow.Event,
   308  	) {
   309  		var responses []*executiondata.SubscribeExecutionDataResponse
   310  		for {
   311  			t.Log(len(responses))
   312  			resp, err := stream.RecvToClient()
   313  			if err == io.EOF {
   314  				break
   315  			}
   316  			require.NoError(t, err)
   317  			responses = append(responses, resp)
   318  			close(stream.sentFromServer)
   319  		}
   320  
   321  		for _, resp := range responses {
   322  			convertedExecData, err := convert.MessageToBlockExecutionData(resp.GetBlockExecutionData(), flow.Testnet.Chain())
   323  			require.NoError(t, err)
   324  
   325  			assert.Equal(t, blockHeight, resp.GetBlockHeight())
   326  
   327  			// only expect a single response
   328  			assert.Equal(t, 1, len(responses))
   329  
   330  			// Verify that the payload is valid
   331  			for _, chunk := range convertedExecData.ChunkExecutionDatas {
   332  				for i, e := range chunk.Events {
   333  					assert.Equal(t, expectedEvents[i], e)
   334  
   335  					var err error
   336  					if version == entities.EventEncodingVersion_JSON_CDC_V0 {
   337  						_, err = jsoncdc.Decode(nil, e.Payload)
   338  					} else {
   339  						_, err = ccf.Decode(nil, e.Payload)
   340  					}
   341  					require.NoError(t, err)
   342  				}
   343  			}
   344  		}
   345  	}
   346  
   347  	ccfEvents, jsonEvents := generateEvents(t, 3)
   348  
   349  	tests := []struct {
   350  		eventVersion entities.EventEncodingVersion
   351  		expected     []flow.Event
   352  	}{
   353  		{
   354  			entities.EventEncodingVersion_JSON_CDC_V0,
   355  			jsonEvents,
   356  		},
   357  		{
   358  			entities.EventEncodingVersion_CCF_V0,
   359  			ccfEvents,
   360  		},
   361  	}
   362  
   363  	for _, test := range tests {
   364  		t.Run(fmt.Sprintf("test %s event encoding version", test.eventVersion.String()), func(t *testing.T) {
   365  			api := ssmock.NewAPI(t)
   366  			stream := makeStreamMock[executiondata.SubscribeExecutionDataRequest, executiondata.SubscribeExecutionDataResponse](ctx)
   367  
   368  			makeStreamRequest(
   369  				stream,
   370  				api,
   371  				&executiondata.SubscribeExecutionDataRequest{
   372  					EventEncodingVersion: test.eventVersion,
   373  				},
   374  				&ExecutionDataResponse{
   375  					Height: blockHeight,
   376  					ExecutionData: unittest.BlockExecutionDataFixture(
   377  						unittest.WithChunkExecutionDatas(
   378  							unittest.ChunkExecutionDataFixture(t, 1024, unittest.WithChunkEvents(ccfEvents)),
   379  							unittest.ChunkExecutionDataFixture(t, 1024, unittest.WithChunkEvents(ccfEvents)),
   380  						),
   381  					),
   382  				},
   383  			)
   384  			handleExecutionDataStreamResponses(stream, test.eventVersion, test.expected)
   385  		})
   386  	}
   387  }
   388  
   389  // TestEventStream tests the event stream with different event encoding versions.
   390  func TestEventStream(t *testing.T) {
   391  	t.Parallel()
   392  
   393  	ctx, cancel := context.WithCancel(context.Background())
   394  	defer cancel()
   395  
   396  	blockHeight := uint64(1)
   397  	blockID := unittest.IdentifierFixture()
   398  
   399  	// Helper function to perform a stream request and handle responses.
   400  	makeStreamRequest := func(
   401  		stream *StreamMock[executiondata.SubscribeEventsRequest, executiondata.SubscribeEventsResponse],
   402  		api *ssmock.API,
   403  		request *executiondata.SubscribeEventsRequest,
   404  		response *EventsResponse,
   405  	) {
   406  		sub := NewSubscription(1)
   407  
   408  		api.On("SubscribeEvents", mock.Anything, flow.ZeroID, uint64(0), mock.Anything).Return(sub)
   409  
   410  		h := NewHandler(api, flow.Localnet.Chain(), makeConfig(1))
   411  
   412  		wg := sync.WaitGroup{}
   413  		wg.Add(1)
   414  		go func() {
   415  			wg.Done()
   416  			err := h.SubscribeEvents(request, stream)
   417  			require.NoError(t, err)
   418  			t.Log("subscription closed")
   419  		}()
   420  		wg.Wait()
   421  
   422  		// send a single response
   423  		err := sub.Send(ctx, response, 100*time.Millisecond)
   424  		require.NoError(t, err)
   425  
   426  		// notify end of data
   427  		sub.Close()
   428  	}
   429  
   430  	// handleExecutionDataStreamResponses handles responses from the execution data stream.
   431  	handleExecutionDataStreamResponses := func(
   432  		stream *StreamMock[executiondata.SubscribeEventsRequest, executiondata.SubscribeEventsResponse],
   433  		version entities.EventEncodingVersion,
   434  		expectedEvents []flow.Event,
   435  	) {
   436  		var responses []*executiondata.SubscribeEventsResponse
   437  		for {
   438  			t.Log(len(responses))
   439  			resp, err := stream.RecvToClient()
   440  			if err == io.EOF {
   441  				break
   442  			}
   443  			// make sure the payload is valid
   444  			require.NoError(t, err)
   445  			responses = append(responses, resp)
   446  
   447  			// shutdown the stream after one response
   448  			close(stream.sentFromServer)
   449  		}
   450  
   451  		for _, resp := range responses {
   452  			convertedEvents := convert.MessagesToEvents(resp.GetEvents())
   453  
   454  			assert.Equal(t, blockHeight, resp.GetBlockHeight())
   455  			assert.Equal(t, blockID, convert.MessageToIdentifier(resp.GetBlockId()))
   456  			assert.Equal(t, expectedEvents, convertedEvents)
   457  			// only expect a single response
   458  			assert.Equal(t, 1, len(responses))
   459  
   460  			for _, e := range convertedEvents {
   461  				var err error
   462  				if version == entities.EventEncodingVersion_JSON_CDC_V0 {
   463  					_, err = jsoncdc.Decode(nil, e.Payload)
   464  				} else {
   465  					_, err = ccf.Decode(nil, e.Payload)
   466  				}
   467  				require.NoError(t, err)
   468  			}
   469  		}
   470  	}
   471  
   472  	// generate events with a payload to include
   473  	ccfEvents, jsonEvents := generateEvents(t, 3)
   474  
   475  	tests := []struct {
   476  		eventVersion entities.EventEncodingVersion
   477  		expected     []flow.Event
   478  	}{
   479  		{
   480  			entities.EventEncodingVersion_JSON_CDC_V0,
   481  			jsonEvents,
   482  		},
   483  		{
   484  			entities.EventEncodingVersion_CCF_V0,
   485  			ccfEvents,
   486  		},
   487  	}
   488  
   489  	for _, test := range tests {
   490  		t.Run(fmt.Sprintf("test %s event encoding version", test.eventVersion.String()), func(t *testing.T) {
   491  			stream := makeStreamMock[executiondata.SubscribeEventsRequest, executiondata.SubscribeEventsResponse](ctx)
   492  
   493  			makeStreamRequest(
   494  				stream,
   495  				ssmock.NewAPI(t),
   496  				&executiondata.SubscribeEventsRequest{
   497  					EventEncodingVersion: test.eventVersion,
   498  				},
   499  				&EventsResponse{
   500  					BlockID: blockID,
   501  					Height:  blockHeight,
   502  					Events:  ccfEvents,
   503  				},
   504  			)
   505  			handleExecutionDataStreamResponses(stream, test.eventVersion, test.expected)
   506  		})
   507  	}
   508  }
   509  
   510  // TestGetRegisterValues tests the register values.
   511  func TestGetRegisterValues(t *testing.T) {
   512  	t.Parallel()
   513  
   514  	ctx, cancel := context.WithCancel(context.Background())
   515  	defer cancel()
   516  
   517  	testHeight := uint64(1)
   518  
   519  	// test register IDs + values
   520  	testIds := flow.RegisterIDs{
   521  		flow.UUIDRegisterID(0),
   522  		flow.AccountStatusRegisterID(unittest.AddressFixture()),
   523  		unittest.RegisterIDFixture(),
   524  	}
   525  	testValues := []flow.RegisterValue{
   526  		[]byte("uno"),
   527  		[]byte("dos"),
   528  		[]byte("tres"),
   529  	}
   530  	invalidIDs := append(testIds, flow.RegisterID{}) // valid + invalid IDs
   531  
   532  	t.Run("invalid message", func(t *testing.T) {
   533  		api := ssmock.NewAPI(t)
   534  		h := NewHandler(api, flow.Testnet.Chain(), makeConfig(1))
   535  
   536  		invalidMessage := &executiondata.GetRegisterValuesRequest{
   537  			RegisterIds: nil,
   538  		}
   539  		_, err := h.GetRegisterValues(ctx, invalidMessage)
   540  		require.Equal(t, codes.InvalidArgument, status.Code(err))
   541  	})
   542  
   543  	t.Run("valid registers", func(t *testing.T) {
   544  		api := ssmock.NewAPI(t)
   545  		api.On("GetRegisterValues", testIds, testHeight).Return(testValues, nil)
   546  		h := NewHandler(api, flow.Testnet.Chain(), makeConfig(1))
   547  
   548  		validRegisters := make([]*entities.RegisterID, len(testIds))
   549  		for i, id := range testIds {
   550  			validRegisters[i] = convert.RegisterIDToMessage(id)
   551  		}
   552  
   553  		req := &executiondata.GetRegisterValuesRequest{
   554  			RegisterIds: validRegisters,
   555  			BlockHeight: testHeight,
   556  		}
   557  
   558  		resp, err := h.GetRegisterValues(ctx, req)
   559  		require.NoError(t, err)
   560  		require.Equal(t, testValues, resp.GetValues())
   561  	})
   562  
   563  	t.Run("unavailable registers", func(t *testing.T) {
   564  		api := ssmock.NewAPI(t)
   565  		expectedErr := status.Errorf(codes.NotFound, "could not get register values: %v", storage.ErrNotFound)
   566  		api.On("GetRegisterValues", invalidIDs, testHeight).Return(nil, expectedErr)
   567  		h := NewHandler(api, flow.Testnet.Chain(), makeConfig(1))
   568  
   569  		unavailableRegisters := make([]*entities.RegisterID, len(invalidIDs))
   570  		for i, id := range invalidIDs {
   571  			unavailableRegisters[i] = convert.RegisterIDToMessage(id)
   572  		}
   573  
   574  		req := &executiondata.GetRegisterValuesRequest{
   575  			RegisterIds: unavailableRegisters,
   576  			BlockHeight: testHeight,
   577  		}
   578  
   579  		_, err := h.GetRegisterValues(ctx, req)
   580  		require.Equal(t, codes.NotFound, status.Code(err))
   581  	})
   582  
   583  	t.Run("wrong height", func(t *testing.T) {
   584  		api := ssmock.NewAPI(t)
   585  		expectedErr := status.Errorf(codes.OutOfRange, "could not get register values: %v", storage.ErrHeightNotIndexed)
   586  		api.On("GetRegisterValues", testIds, testHeight+1).Return(nil, expectedErr)
   587  		h := NewHandler(api, flow.Testnet.Chain(), makeConfig(1))
   588  
   589  		validRegisters := make([]*entities.RegisterID, len(testIds))
   590  		for i, id := range testIds {
   591  			validRegisters[i] = convert.RegisterIDToMessage(id)
   592  		}
   593  
   594  		req := &executiondata.GetRegisterValuesRequest{
   595  			RegisterIds: validRegisters,
   596  			BlockHeight: testHeight + 1,
   597  		}
   598  
   599  		_, err := h.GetRegisterValues(ctx, req)
   600  		require.Equal(t, codes.OutOfRange, status.Code(err))
   601  	})
   602  }
   603  
   604  func generateEvents(t *testing.T, n int) ([]flow.Event, []flow.Event) {
   605  	ccfEvents := generator.GetEventsWithEncoding(n, entities.EventEncodingVersion_CCF_V0)
   606  	jsonEvents := make([]flow.Event, len(ccfEvents))
   607  	for i, e := range ccfEvents {
   608  		jsonEvent, err := convert.CcfEventToJsonEvent(e)
   609  		require.NoError(t, err)
   610  		jsonEvents[i] = *jsonEvent
   611  	}
   612  	return ccfEvents, jsonEvents
   613  }
   614  
   615  func makeConfig(maxGlobalStreams uint32) Config {
   616  	return Config{
   617  		EventFilterConfig:    state_stream.DefaultEventFilterConfig,
   618  		ClientSendTimeout:    state_stream.DefaultSendTimeout,
   619  		ClientSendBufferSize: state_stream.DefaultSendBufferSize,
   620  		MaxGlobalStreams:     maxGlobalStreams,
   621  		HeartbeatInterval:    state_stream.DefaultHeartbeatInterval,
   622  	}
   623  }
   624  
   625  func makeStreamMock[R, T any](ctx context.Context) *StreamMock[R, T] {
   626  	return &StreamMock[R, T]{
   627  		ctx:            ctx,
   628  		recvToServer:   make(chan *R, 10),
   629  		sentFromServer: make(chan *T, 10),
   630  	}
   631  }
   632  
   633  type StreamMock[R, T any] struct {
   634  	grpc.ServerStream
   635  	ctx            context.Context
   636  	recvToServer   chan *R
   637  	sentFromServer chan *T
   638  }
   639  
   640  func (m *StreamMock[R, T]) Context() context.Context {
   641  	return m.ctx
   642  }
   643  func (m *StreamMock[R, T]) Send(resp *T) error {
   644  	m.sentFromServer <- resp
   645  	return nil
   646  }
   647  
   648  func (m *StreamMock[R, T]) Recv() (*R, error) {
   649  	req, more := <-m.recvToServer
   650  	if !more {
   651  		return nil, io.EOF
   652  	}
   653  	return req, nil
   654  }
   655  
   656  func (m *StreamMock[R, T]) SendFromClient(req *R) error {
   657  	m.recvToServer <- req
   658  	return nil
   659  }
   660  
   661  func (m *StreamMock[R, T]) RecvToClient() (*T, error) {
   662  	response, more := <-m.sentFromServer
   663  	if !more {
   664  		return nil, io.EOF
   665  	}
   666  	return response, nil
   667  }