github.com/koko1123/flow-go-1@v0.29.6/engine/access/rest/events_test.go (about)

     1  package rest
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/koko1123/flow-go-1/engine/access/rest/util"
    12  
    13  	"github.com/koko1123/flow-go-1/access/mock"
    14  	"github.com/koko1123/flow-go-1/model/flow"
    15  	"github.com/koko1123/flow-go-1/utils/unittest"
    16  
    17  	mocks "github.com/stretchr/testify/mock"
    18  	"github.com/stretchr/testify/require"
    19  	"google.golang.org/grpc/codes"
    20  	"google.golang.org/grpc/status"
    21  )
    22  
    23  func TestGetEvents(t *testing.T) {
    24  	backend := &mock.API{}
    25  	events := generateEventsMocks(backend, 5)
    26  
    27  	allBlockIDs := make([]string, len(events))
    28  	for i, e := range events {
    29  		allBlockIDs[i] = e.BlockID.String()
    30  	}
    31  	startHeight := fmt.Sprintf("%d", events[0].BlockHeight)
    32  	endHeight := fmt.Sprintf("%d", events[len(events)-1].BlockHeight)
    33  
    34  	testVectors := []testVector{
    35  		// valid
    36  		{
    37  			description:      "Get events for a single block by ID",
    38  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "", "", []string{events[0].BlockID.String()}),
    39  			expectedStatus:   http.StatusOK,
    40  			expectedResponse: testBlockEventResponse([]flow.BlockEvents{events[0]}),
    41  		},
    42  		{
    43  			description:      "Get events by all block IDs",
    44  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "", "", allBlockIDs),
    45  			expectedStatus:   http.StatusOK,
    46  			expectedResponse: testBlockEventResponse(events),
    47  		},
    48  		{
    49  			description:      "Get events for height range",
    50  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", startHeight, endHeight, nil),
    51  			expectedStatus:   http.StatusOK,
    52  			expectedResponse: testBlockEventResponse(events),
    53  		},
    54  		{
    55  			description:      "Get invalid - invalid height format",
    56  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "0", "sealed", nil),
    57  			expectedStatus:   http.StatusOK,
    58  			expectedResponse: testBlockEventResponse(events),
    59  		},
    60  		// invalid
    61  		{
    62  			description:      "Get invalid - missing all fields",
    63  			request:          getEventReq(t, "", "", "", nil),
    64  			expectedStatus:   http.StatusBadRequest,
    65  			expectedResponse: `{"code":400,"message":"must provide either block IDs or start and end height range"}`,
    66  		},
    67  		{
    68  			description:      "Get invalid - missing query event type",
    69  			request:          getEventReq(t, "", "", "", []string{events[0].BlockID.String()}),
    70  			expectedStatus:   http.StatusBadRequest,
    71  			expectedResponse: `{"code":400,"message":"event type must be provided"}`,
    72  		},
    73  		{
    74  			description:      "Get invalid - missing end height",
    75  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "100", "", nil),
    76  			expectedStatus:   http.StatusBadRequest,
    77  			expectedResponse: `{"code":400,"message":"must provide either block IDs or start and end height range"}`,
    78  		},
    79  		{
    80  			description:      "Get invalid - start height bigger than end height",
    81  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "100", "50", nil),
    82  			expectedStatus:   http.StatusBadRequest,
    83  			expectedResponse: `{"code":400,"message":"start height must be less than or equal to end height"}`,
    84  		},
    85  		{
    86  			description:      "Get invalid - too big interval",
    87  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "0", "5000", nil),
    88  			expectedStatus:   http.StatusBadRequest,
    89  			expectedResponse: `{"code":400,"message":"height range 5000 exceeds maximum allowed of 250"}`,
    90  		},
    91  		{
    92  			description:      "Get invalid - can not provide all params",
    93  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "100", "120", []string{"10e782612a014b5c9c7d17994d7e67157064f3dd42fa92cd080bfb0fe22c3f71"}),
    94  			expectedStatus:   http.StatusBadRequest,
    95  			expectedResponse: `{"code":400,"message":"can only provide either block IDs or start and end height range"}`,
    96  		},
    97  		{
    98  			description:      "Get invalid - invalid height format",
    99  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "foo", "120", nil),
   100  			expectedStatus:   http.StatusBadRequest,
   101  			expectedResponse: `{"code":400,"message":"invalid start height: invalid height format"}`,
   102  		},
   103  		{
   104  			description:      "Get invalid - latest block smaller than start",
   105  			request:          getEventReq(t, "A.179b6b1cb6755e31.Foo.Bar", "100000", "sealed", nil),
   106  			expectedStatus:   http.StatusBadRequest,
   107  			expectedResponse: `{"code":400,"message":"current retrieved end height value is lower than start height"}`,
   108  		},
   109  	}
   110  
   111  	for _, test := range testVectors {
   112  		t.Run(test.description, func(t *testing.T) {
   113  			assertResponse(t, test.request, test.expectedStatus, test.expectedResponse, backend)
   114  		})
   115  	}
   116  
   117  }
   118  
   119  func getEventReq(t *testing.T, eventType string, start string, end string, blockIDs []string) *http.Request {
   120  	u, _ := url.Parse("/v1/events")
   121  	q := u.Query()
   122  
   123  	if len(blockIDs) > 0 {
   124  		q.Add(blockQueryParam, strings.Join(blockIDs, ","))
   125  	}
   126  
   127  	if start != "" && end != "" {
   128  		q.Add(startHeightQueryParam, start)
   129  		q.Add(endHeightQueryParam, end)
   130  	}
   131  
   132  	q.Add(eventTypeQuery, eventType)
   133  
   134  	u.RawQuery = q.Encode()
   135  
   136  	req, err := http.NewRequest("GET", u.String(), nil)
   137  	require.NoError(t, err)
   138  
   139  	return req
   140  }
   141  
   142  func generateEventsMocks(backend *mock.API, n int) []flow.BlockEvents {
   143  	events := make([]flow.BlockEvents, n)
   144  	ids := make([]flow.Identifier, n)
   145  
   146  	for i := 0; i < n; i++ {
   147  		header := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(uint64(i)))
   148  		ids[i] = header.ID()
   149  
   150  		events[i] = unittest.BlockEventsFixture(header, 2)
   151  
   152  		backend.Mock.
   153  			On("GetEventsForBlockIDs", mocks.Anything, mocks.Anything, []flow.Identifier{header.ID()}).
   154  			Return([]flow.BlockEvents{events[i]}, nil)
   155  	}
   156  
   157  	backend.Mock.
   158  		On("GetEventsForBlockIDs", mocks.Anything, mocks.Anything, ids).
   159  		Return(events, nil)
   160  
   161  	backend.Mock.On(
   162  		"GetEventsForHeightRange",
   163  		mocks.Anything,
   164  		mocks.Anything,
   165  		events[0].BlockHeight,
   166  		events[len(events)-1].BlockHeight,
   167  	).Return(events, nil)
   168  
   169  	latestBlock := unittest.BlockHeaderFixture()
   170  	latestBlock.Height = uint64(n - 1)
   171  
   172  	// default not found
   173  	backend.Mock.
   174  		On("GetEventsForBlockIDs", mocks.Anything, mocks.Anything, mocks.Anything).
   175  		Return(nil, status.Error(codes.NotFound, "not found"))
   176  
   177  	backend.Mock.
   178  		On("GetEventsForHeightRange", mocks.Anything, mocks.Anything).
   179  		Return(nil, status.Error(codes.NotFound, "not found"))
   180  
   181  	backend.Mock.
   182  		On("GetLatestBlockHeader", mocks.Anything, true).
   183  		Return(latestBlock, flow.BlockStatusSealed, nil)
   184  
   185  	return events
   186  }
   187  
   188  func testBlockEventResponse(events []flow.BlockEvents) string {
   189  	res := make([]string, len(events))
   190  
   191  	for i, e := range events {
   192  		events := make([]string, len(e.Events))
   193  
   194  		for i, ev := range e.Events {
   195  			events[i] = fmt.Sprintf(`{
   196  				"type": "%s",
   197  				"transaction_id": "%s",
   198  				"transaction_index": "%d",
   199  				"event_index": "%d",
   200  				"payload": "%s"
   201  			}`, ev.Type, ev.TransactionID, ev.TransactionIndex, ev.EventIndex, util.ToBase64(ev.Payload))
   202  		}
   203  
   204  		res[i] = fmt.Sprintf(`{
   205  			"block_id": "%s",
   206  			"block_height": "%d",
   207  			"block_timestamp": "%s",
   208  			"events": [%s]
   209  		}`,
   210  			e.BlockID.String(),
   211  			e.BlockHeight,
   212  			e.BlockTimestamp.Format(time.RFC3339Nano),
   213  			strings.Join(events, ","),
   214  		)
   215  	}
   216  
   217  	return fmt.Sprintf(`[%s]`, strings.Join(res, ","))
   218  }