github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/ctl/service/r2/routes_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package r2 22 23 import ( 24 "bytes" 25 "context" 26 "encoding/json" 27 "fmt" 28 "net/http" 29 "testing" 30 "time" 31 32 "github.com/m3db/m3/src/ctl/auth" 33 "github.com/m3db/m3/src/ctl/service/r2/store" 34 "github.com/m3db/m3/src/metrics/rules" 35 "github.com/m3db/m3/src/metrics/rules/view" 36 "github.com/m3db/m3/src/metrics/rules/view/changes" 37 "github.com/m3db/m3/src/x/clock" 38 "github.com/m3db/m3/src/x/instrument" 39 40 "github.com/golang/mock/gomock" 41 "github.com/gorilla/mux" 42 "github.com/stretchr/testify/require" 43 "github.com/uber-go/tally" 44 ) 45 46 func TestHandleRoute(t *testing.T) { 47 s := newTestService(nil) 48 r := newTestGetRequest() 49 expected := view.Namespaces{} 50 actual, err := s.handleRoute(fetchNamespaces, r, newTestInstrumentMethodMetrics()) 51 require.NoError(t, err) 52 require.Equal(t, expected, actual) 53 } 54 55 func TestHandleRouteNilRequest(t *testing.T) { 56 s := newTestService(nil) 57 _, err := s.handleRoute(fetchNamespaces, nil, newTestInstrumentMethodMetrics()) 58 require.EqualError(t, err, errNilRequest.Error()) 59 } 60 func TestFetchNamespacesSuccess(t *testing.T) { 61 expected := view.Namespaces{} 62 actual, err := fetchNamespaces(newTestService(nil), newTestGetRequest()) 63 require.NoError(t, err) 64 require.Equal(t, expected, actual) 65 } 66 67 func TestFetchNamespaceSuccess(t *testing.T) { 68 expected := view.RuleSet{} 69 actual, err := fetchNamespace(newTestService(nil), newTestGetRequest()) 70 require.NoError(t, err) 71 require.Equal(t, expected, actual) 72 } 73 74 func TestValidateRuleSetSuccess(t *testing.T) { 75 expected := "Ruleset is valid" 76 actual, err := validateRuleSet(newTestService(nil), newTestPostRequest([]byte(`{}`))) 77 require.NoError(t, err) 78 require.Equal(t, expected, actual) 79 } 80 81 func TestCreateNamespaceSuccess(t *testing.T) { 82 expected := view.Namespace{} 83 actual, err := createNamespace(newTestService(nil), newTestPostRequest([]byte(`{"id": "id"}`))) 84 require.NoError(t, err) 85 require.Equal(t, expected, actual) 86 } 87 88 func TestDeleteNamespaceSuccess(t *testing.T) { 89 expected := fmt.Sprintf("Deleted namespace %s", "") 90 actual, err := deleteNamespace(newTestService(nil), newTestDeleteRequest()) 91 require.NoError(t, err) 92 require.Equal(t, expected, actual) 93 } 94 95 func TestFetchMappingRuleSuccess(t *testing.T) { 96 expected := view.MappingRule{} 97 actual, err := fetchMappingRule(newTestService(nil), newTestGetRequest()) 98 require.NoError(t, err) 99 require.Equal(t, expected, actual) 100 } 101 102 func TestCreateMappingRuleSuccess(t *testing.T) { 103 expected := view.MappingRule{} 104 actual, err := createMappingRule(newTestService(nil), newTestPostRequest( 105 []byte(`{"filter": "key:val", "name": "name", "policies": []}`), 106 )) 107 require.NoError(t, err) 108 require.Equal(t, expected, actual) 109 } 110 111 func TestUpdateMappingRuleSuccess(t *testing.T) { 112 expected := view.MappingRule{} 113 actual, err := updateMappingRule(newTestService(nil), newTestPutRequest( 114 []byte(`{"filter": "key:val", "name": "name", "policies": []}`), 115 )) 116 require.NoError(t, err) 117 require.Equal(t, expected, actual) 118 } 119 120 func TestDeleteMappingRuleSuccess(t *testing.T) { 121 expected := fmt.Sprintf("Deleted mapping rule: %s in namespace %s", "", "") 122 actual, err := deleteMappingRule(newTestService(nil), newTestDeleteRequest()) 123 require.NoError(t, err) 124 require.Equal(t, expected, actual) 125 } 126 127 func TestFetchMappingRuleHistorySuccess(t *testing.T) { 128 expected := view.MappingRuleSnapshots{MappingRules: make([]view.MappingRule, 0)} 129 actual, err := fetchMappingRuleHistory(newTestService(nil), newTestGetRequest()) 130 require.NoError(t, err) 131 require.Equal(t, expected, actual) 132 } 133 134 func TestFetchRollupRuleSuccess(t *testing.T) { 135 expected := view.RollupRule{} 136 actual, err := fetchRollupRule(newTestService(nil), newTestGetRequest()) 137 require.NoError(t, err) 138 require.Equal(t, expected, actual) 139 } 140 141 func TestCreateRollupRuleSuccess(t *testing.T) { 142 expected := view.RollupRule{} 143 actual, err := createRollupRule(newTestService(nil), newTestPostRequest( 144 []byte(`{"filter": "key:val", "name": "name", "targets": []}`), 145 )) 146 require.NoError(t, err) 147 require.Equal(t, expected, actual) 148 } 149 150 func TestUpdateRollupRuleSuccess(t *testing.T) { 151 expected := view.RollupRule{} 152 actual, err := updateRollupRule(newTestService(nil), newTestPutRequest( 153 []byte(`{"filter": "key:val", "name": "name", "targets": []}`), 154 )) 155 require.NoError(t, err) 156 require.Equal(t, expected, actual) 157 } 158 159 func TestDeleteRollupRuleSuccess(t *testing.T) { 160 expected := fmt.Sprintf("Deleted rollup rule: %s in namespace %s", "", "") 161 actual, err := deleteRollupRule(newTestService(nil), newTestDeleteRequest()) 162 require.NoError(t, err) 163 require.Equal(t, expected, actual) 164 } 165 166 func TestFetchRollupRuleHistorySuccess(t *testing.T) { 167 expected := view.RollupRuleSnapshots{RollupRules: []view.RollupRule{}} 168 actual, err := fetchRollupRuleHistory(newTestService(nil), newTestGetRequest()) 169 require.NoError(t, err) 170 require.Equal(t, expected, actual) 171 } 172 173 func TestRulesetUpdateRuleSet(t *testing.T) { 174 namespaceID := "testNamespace" 175 bulkReqBody := newTestBulkReqBody() 176 bodyBytes, err := json.Marshal(bulkReqBody) 177 require.NoError(t, err) 178 req, err := http.NewRequest( 179 http.MethodPost, 180 fmt.Sprintf("/namespaces/%s/ruleset/update", namespaceID), 181 bytes.NewBuffer(bodyBytes), 182 ) 183 require.NoError(t, err) 184 req = mux.SetURLVars( 185 req, 186 map[string]string{ 187 "namespaceID": namespaceID, 188 }, 189 ) 190 191 ctrl := gomock.NewController(t) 192 defer ctrl.Finish() 193 storeMock := store.NewMockStore(ctrl) 194 storeMock.EXPECT().UpdateRuleSet(gomock.Any(), 1, gomock.Any()).Return( 195 view.RuleSet{ 196 Version: 2, 197 }, 198 nil, 199 ) 200 201 service := newTestService(storeMock) 202 resp, err := updateRuleSet(service, req) 203 require.NoError(t, err) 204 typedResp := resp.(view.RuleSet) 205 require.Equal(t, typedResp.Version, 2) 206 } 207 208 func TestUpdateRuleSetStoreUpdateFailure(t *testing.T) { 209 namespaceID := "testNamespace" 210 bulkReqBody := newTestBulkReqBody() 211 bodyBytes, err := json.Marshal(bulkReqBody) 212 require.NoError(t, err) 213 req, err := http.NewRequest( 214 http.MethodPost, 215 fmt.Sprintf("/namespaces/%s/ruleset/bulk", namespaceID), 216 bytes.NewBuffer(bodyBytes), 217 ) 218 require.NoError(t, err) 219 req = mux.SetURLVars( 220 req, 221 map[string]string{ 222 "namespaceID": namespaceID, 223 }, 224 ) 225 226 ctrl := gomock.NewController(t) 227 defer ctrl.Finish() 228 storeMock := store.NewMockStore(ctrl) 229 storeMock.EXPECT().UpdateRuleSet(gomock.Any(), 1, gomock.Any()).Return( 230 view.RuleSet{}, 231 NewConflictError("something horrible has happened"), 232 ) 233 234 service := newTestService(storeMock) 235 resp, err := updateRuleSet(service, req) 236 require.Equal(t, view.RuleSet{}, resp) 237 require.Error(t, err) 238 require.IsType(t, NewConflictError(""), err) 239 } 240 241 func TestUpdateRuleSetInvalidJSON(t *testing.T) { 242 namespaceID := "testNamespace" 243 req, err := http.NewRequest( 244 http.MethodPost, 245 fmt.Sprintf("/namespaces/%s/ruleset/bulk", namespaceID), 246 bytes.NewBuffer([]byte("invalid josn")), 247 ) 248 require.NoError(t, err) 249 req = mux.SetURLVars( 250 req, 251 map[string]string{ 252 "namespaceID": namespaceID, 253 }, 254 ) 255 256 ctrl := gomock.NewController(t) 257 defer ctrl.Finish() 258 storeMock := store.NewMockStore(ctrl) 259 service := newTestService(storeMock) 260 resp, err := updateRuleSet(service, req) 261 require.Nil(t, resp) 262 require.Error(t, err) 263 require.IsType(t, NewBadInputError(""), err) 264 } 265 266 func TestUpdateRuleSetEmptyRequest(t *testing.T) { 267 namespaceID := "testNamespace" 268 body := &updateRuleSetRequest{ 269 RuleSetChanges: changes.RuleSetChanges{}, 270 } 271 bodyBytes, err := json.Marshal(body) 272 require.NoError(t, err) 273 req, err := http.NewRequest( 274 http.MethodPost, 275 fmt.Sprintf("/namespaces/%s/ruleset/bulk", namespaceID), 276 bytes.NewBuffer(bodyBytes), 277 ) 278 require.NoError(t, err) 279 req = mux.SetURLVars( 280 req, 281 map[string]string{ 282 "namespaceID": namespaceID, 283 }, 284 ) 285 286 ctrl := gomock.NewController(t) 287 defer ctrl.Finish() 288 storeMock := store.NewMockStore(ctrl) 289 service := newTestService(storeMock) 290 resp, err := updateRuleSet(service, req) 291 require.Nil(t, resp) 292 require.Error(t, err) 293 require.IsType(t, NewBadInputError(""), err) 294 println(err.Error()) 295 } 296 297 func newTestService(store store.Store) *service { 298 if store == nil { 299 store = newMockStore() 300 } 301 iOpts := instrument.NewOptions() 302 return &service{ 303 metrics: newServiceMetrics(iOpts.MetricsScope(), iOpts.TimerOptions()), 304 nowFn: clock.NewOptions().NowFn(), 305 store: store, 306 authService: auth.NewNoopAuth(), 307 logger: iOpts.Logger(), 308 } 309 } 310 311 func newTestGetRequest() *http.Request { 312 req, _ := http.NewRequest("GET", "/route", nil) 313 return req.WithContext(context.Background()) 314 } 315 316 func newTestPostRequest(bodyBuff []byte) *http.Request { 317 req, _ := http.NewRequest("POST", "/route", bytes.NewReader(bodyBuff)) 318 return req.WithContext(context.Background()) 319 } 320 321 func newTestPutRequest(bodyBuff []byte) *http.Request { 322 req, _ := http.NewRequest("PUT", "/route", bytes.NewReader(bodyBuff)) 323 return req.WithContext(context.Background()) 324 } 325 326 func newTestDeleteRequest() *http.Request { 327 req, _ := http.NewRequest("DELETE", "/route", nil) 328 return req.WithContext(context.Background()) 329 } 330 331 func newTestInstrumentMethodMetrics() instrument.MethodMetrics { 332 return instrument.NewMethodMetrics(tally.NoopScope, "testRoute", instrument.TimerOptions{}) 333 } 334 335 func newTestBulkReqBody() updateRuleSetRequest { 336 return updateRuleSetRequest{ 337 RuleSetVersion: 1, 338 RuleSetChanges: changes.RuleSetChanges{ 339 Namespace: "testNamespace", 340 RollupRuleChanges: []changes.RollupRuleChange{ 341 changes.RollupRuleChange{ 342 Op: changes.AddOp, 343 RuleData: &view.RollupRule{ 344 Name: "rollupRule3", 345 }, 346 }, 347 }, 348 MappingRuleChanges: []changes.MappingRuleChange{ 349 changes.MappingRuleChange{ 350 Op: changes.AddOp, 351 RuleData: &view.MappingRule{ 352 Name: "mappingRule3", 353 }, 354 }, 355 }, 356 }, 357 } 358 } 359 360 // nolint: unparam 361 func newTestRuleSet(version int) rules.RuleSet { 362 helper := rules.NewRuleSetUpdateHelper(time.Minute) 363 // For testing all updates happen at the 0 epoch 364 meta := helper.NewUpdateMetadata(0, "originalAuthor") 365 366 mrs := rules.NewEmptyRuleSet("testNamespace", rules.UpdateMetadata{}) 367 mrs.AddRollupRule( 368 view.RollupRule{ 369 Name: "rollupRule1", 370 }, 371 meta, 372 ) 373 mrs.AddRollupRule( 374 view.RollupRule{ 375 Name: "rollupRule2", 376 }, 377 meta, 378 ) 379 mrs.AddMappingRule( 380 view.MappingRule{ 381 Name: "mappingRule1", 382 }, 383 meta, 384 ) 385 mrs.AddMappingRule( 386 view.MappingRule{ 387 Name: "mappingRule2", 388 }, 389 meta, 390 ) 391 proto, _ := mrs.Proto() 392 rs, _ := rules.NewRuleSetFromProto(version, proto, rules.NewOptions()) 393 return rs 394 } 395 396 // TODO(jskelcy): Migrate to use mockgen mock for testing. 397 type mockStore struct{} 398 399 func newMockStore() store.Store { 400 return mockStore{} 401 } 402 403 func (s mockStore) FetchNamespaces() (view.Namespaces, error) { 404 return view.Namespaces{}, nil 405 } 406 407 func (s mockStore) ValidateRuleSet(rs view.RuleSet) error { 408 return nil 409 } 410 411 func (s mockStore) UpdateRuleSet(rsChanges changes.RuleSetChanges, version int, uOpts store.UpdateOptions) (view.RuleSet, error) { 412 return view.RuleSet{}, nil 413 } 414 415 func (s mockStore) CreateNamespace(namespaceID string, uOpts store.UpdateOptions) (view.Namespace, error) { 416 return view.Namespace{}, nil 417 } 418 419 func (s mockStore) DeleteNamespace(namespaceID string, uOpts store.UpdateOptions) error { 420 return nil 421 } 422 423 //nolint: unparam 424 func (s mockStore) FetchRuleSet(namespaceID string) (rules.RuleSet, error) { 425 return newTestRuleSet(1), nil 426 } 427 428 func (s mockStore) FetchRuleSetSnapshot(namespaceID string) (view.RuleSet, error) { 429 return view.RuleSet{}, nil 430 } 431 432 func (s mockStore) FetchMappingRule(namespaceID, mappingRuleID string) (view.MappingRule, error) { 433 return view.MappingRule{}, nil 434 } 435 436 func (s mockStore) CreateMappingRule(namespaceID string, mrv view.MappingRule, uOpts store.UpdateOptions) (view.MappingRule, error) { 437 return view.MappingRule{}, nil 438 } 439 440 func (s mockStore) UpdateMappingRule(namespaceID, mappingRuleID string, mrv view.MappingRule, uOpts store.UpdateOptions) (view.MappingRule, error) { 441 return view.MappingRule{}, nil 442 } 443 444 func (s mockStore) DeleteMappingRule(namespaceID, mappingRuleID string, uOpts store.UpdateOptions) error { 445 return nil 446 } 447 448 func (s mockStore) FetchMappingRuleHistory(namespaceID, mappingRuleID string) ([]view.MappingRule, error) { 449 return make([]view.MappingRule, 0), nil 450 } 451 452 func (s mockStore) FetchRollupRule(namespaceID, rollupRuleID string) (view.RollupRule, error) { 453 return view.RollupRule{}, nil 454 } 455 456 func (s mockStore) CreateRollupRule(namespaceID string, rrv view.RollupRule, uOpts store.UpdateOptions) (view.RollupRule, error) { 457 return view.RollupRule{}, nil 458 } 459 460 func (s mockStore) UpdateRollupRule(namespaceID, rollupRuleID string, rrv view.RollupRule, uOpts store.UpdateOptions) (view.RollupRule, error) { 461 return view.RollupRule{}, nil 462 } 463 464 func (s mockStore) DeleteRollupRule(namespaceID, rollupRuleID string, uOpts store.UpdateOptions) error { 465 return nil 466 } 467 468 func (s mockStore) FetchRollupRuleHistory(namespaceID, rollupRuleID string) ([]view.RollupRule, error) { 469 return make([]view.RollupRule, 0), nil 470 } 471 472 func (s mockStore) Close() {}