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 }