/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package ad import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" mockController "opensearch-cli/controller/platform/mocks" entity "opensearch-cli/entity/ad" gateway "opensearch-cli/gateway/ad/mocks" "opensearch-cli/mapper" "os" "path/filepath" "testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) const mockDetectorID = "m4ccEnIBTXsGi3mvMt9p" const mockDetectorName = "detector" func helperLoadBytes(t *testing.T, name string) []byte { path := filepath.Join("testdata", name) // relative path contents, err := ioutil.ReadFile(path) if err != nil { t.Fatal(err) } return contents } func helperConvertToInterface(input []string) []interface{} { s := make([]interface{}, len(input)) for i, v := range input { s[i] = v } return s } func getRawFilter() []byte { return []byte(`{ "bool": { "filter": [{ "exists": { "field": "value", "boost": 1 } }], "adjust_pure_negative": true, "boost": 1 } }`) } func getFinalFilter(additionalFilters ...json.RawMessage) []byte { filter1 := []byte(`{ "bool": { "filter": { "term": { "ip" : "localhost" } } } }`) if len(additionalFilters) < 1 { return filter1 } filter := entity.Query{ Bool: entity.Bool{ Must: []json.RawMessage{ filter1, }, }, } filter.Bool.Must = append(filter.Bool.Must, additionalFilters...) marshal, _ := json.Marshal(filter) return marshal } func getCreateDetectorRequest() entity.CreateDetectorRequest { return entity.CreateDetectorRequest{ Name: "testdata-detector", Description: "Test detector", TimeField: "timestamp", Index: []string{"order*"}, Features: []entity.FeatureRequest{{ AggregationType: []string{"sum"}, Enabled: true, Field: []string{"value"}, }}, Filter: getRawFilter(), Interval: "1m", Delay: "1m", Start: true, PartitionField: mapper.StringToStringPtr("ip"), } } func getRawFeatureAggregation() []byte { return []byte(`{ "sum_value": { "sum": { "field": "value" } } }`) } func getCreateDetector() *entity.CreateDetector { return &entity.CreateDetector{ Name: "testdata-detector", Description: "Test detector", TimeField: "timestamp", Index: []string{"order*"}, Features: []entity.Feature{ { Name: "sum_value", Enabled: true, AggregationQuery: getRawFeatureAggregation(), }, }, Filter: getRawFilter(), Interval: entity.Interval{ Period: entity.Period{ Duration: 1, Unit: "Minutes", }, }, Delay: entity.Interval{ Period: entity.Period{ Duration: 1, Unit: "Minutes", }, }, } } func TestController_StartDetector(t *testing.T) { t.Run("start empty detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() ctrl := New(os.Stdin, mockESController, mockADGateway) assert.Error(t, ctrl.StartDetector(ctx, "")) }) t.Run("start detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StartDetector(ctx, "detectorID").Return(errors.New("no connection")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) assert.Error(t, ctrl.StartDetector(ctx, "detectorID")) }) t.Run("start detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StartDetector(ctx, "detectorID").Return(nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) assert.NoError(t, ctrl.StartDetector(ctx, "detectorID")) }) } func TestController_StopDetector(t *testing.T) { t.Run("stop empty detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.StopDetector(ctx, "") assert.Error(t, err) }) t.Run("stop detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(nil, errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.StopDetector(ctx, "detectorID") assert.Error(t, err) }) t.Run("stop detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(mapper.StringToStringPtr("Stopped Detector"), nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.StopDetector(ctx, "detectorID") assert.NoError(t, err) }) } func TestController_CreateAnomalyDetector(t *testing.T) { t.Run("gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().CreateDetector(ctx, getCreateDetector()).Return(nil, errors.New("failed to connect")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateAnomalyDetector(ctx, r) assert.EqualError(t, err, "failed to connect") }) t.Run("entity failed to create", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().CreateDetector(ctx, getCreateDetector()).Return(nil, errors.New(string(helperLoadBytes(t, "create_failed_response.json")))) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateAnomalyDetector(ctx, r) assert.EqualError(t, err, "Cannot create anomaly detector with name [testdata-detector] as it's already used by detector [wR_1XXMBs3q1IVz33Sk-]") }) t.Run("entity succeeded without starting", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() r.Start = false mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().CreateDetector(ctx, getCreateDetector()).Return(helperLoadBytes(t, "create_response.json"), nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) detectorID, err := ctrl.CreateAnomalyDetector(ctx, r) assert.NoError(t, err) assert.NotNil(t, detectorID) assert.EqualValues(t, mockDetectorID, *detectorID) }) t.Run("entity succeeded", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().CreateDetector(ctx, getCreateDetector()).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) detectorID, err := ctrl.CreateAnomalyDetector(ctx, r) assert.NoError(t, err) assert.NotNil(t, detectorID) assert.EqualValues(t, mockDetectorID, *detectorID) }) t.Run("entity failed because of failed to start", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().CreateDetector(ctx, getCreateDetector()).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(errors.New("error")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateAnomalyDetector(ctx, r) assert.EqualError(t, err, fmt.Sprintf("detector is created with id: %s, but failed to start due to error", mockDetectorID)) }) } func TestController_DeleteDetector(t *testing.T) { t.Run("invalid detector Id", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, "", false, false) assert.EqualError(t, err, "detector Id cannot be empty") }) t.Run("delete gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, false, false) assert.EqualError(t, err, "gateway failed") }) t.Run("delete gateway succeeded", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, false, false) assert.NoError(t, err) }) t.Run("stop gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StopDetector(ctx, mockDetectorID).Return(nil, errors.New("failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, false, true) assert.EqualError(t, err, "failed") }) t.Run("stop gateway succeeded", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StopDetector(ctx, mockDetectorID).Return(mapper.StringToStringPtr("Stopped Detector"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, false, true) assert.NoError(t, err) }) t.Run("cancelled from user", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("no\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, true, false) assert.NoError(t, err) }) t.Run("agreed by user", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(nil) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetector(ctx, mockDetectorID, true, false) assert.NoError(t, err) }) } func TestController_CreateMultiEntityAnomalyDetector(t *testing.T) { t.Run("create one detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(helperConvertToInterface([]string{"localhost"}), nil) ctrl := New(os.Stdin, mockESController, mockADGateway) detectorID, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, false, false) assert.NoError(t, err) assert.NotNil(t, detectorID) assert.EqualValues(t, gatewayRequest.Name, detectorID[0]) }) t.Run("create detector failed due to second detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(nil, errors.New(string(helperLoadBytes(t, "create_failed_response.json")))) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(nil) mockADGateway.EXPECT().StopDetector(ctx, mockDetectorID).Return(mapper.StringToStringPtr("stopped"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(helperConvertToInterface([]string{"localhost", "localhost"}), nil) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, false, false) assert.EqualError(t, err, "Cannot create anomaly detector with name [testdata-detector] as it's already used by detector [wR_1XXMBs3q1IVz33Sk-]") }) t.Run("create one detector with no filter", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() r.Filter = nil mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter() mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(nil) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(helperConvertToInterface([]string{"localhost"}), nil) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) detectorID, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, true, false) assert.NoError(t, err) assert.NotNil(t, detectorID) assert.EqualValues(t, gatewayRequest.Name, detectorID[0]) }) t.Run("create one detector interactive rejected", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(helperConvertToInterface([]string{"localhost"}), nil) var stdin bytes.Buffer stdin.Write([]byte("no\n")) ctrl := New(&stdin, mockESController, mockADGateway) detectorID, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, true, false) assert.NoError(t, err) assert.Nil(t, detectorID) }) t.Run("create detector failed since no values in partition field", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(nil, nil) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, false, false) assert.EqualError(t, err, "failed to get values for partition field: ip, check whether any data is available in index [order*]") }) t.Run("create detector failed since platform controller failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(nil, errors.New("failed")) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, false, false) assert.EqualError(t, err, "failed") }) t.Run("create detector failed due to second detector, failed to cleanup automatically", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() r := getCreateDetectorRequest() mockADGateway := gateway.NewMockGateway(mockCtrl) gatewayRequest := getCreateDetector() gatewayRequest.Name = gatewayRequest.Name + "-" + "localhost" gatewayRequest.Filter = getFinalFilter(getRawFilter()) mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(helperLoadBytes(t, "create_response.json"), nil) mockADGateway.EXPECT().CreateDetector(ctx, gatewayRequest).Return(nil, errors.New(string(helperLoadBytes(t, "create_failed_response.json")))) mockADGateway.EXPECT().StartDetector(ctx, mockDetectorID).Return(nil) mockADGateway.EXPECT().StopDetector(ctx, mockDetectorID).Return(mapper.StringToStringPtr("stopped"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, mockDetectorID).Return(errors.New("failed")) mockESController := mockController.NewMockController(mockCtrl) mockESController.EXPECT().GetDistinctValues(ctx, r.Index[0], *r.PartitionField).Return(helperConvertToInterface([]string{"localhost", "localhost"}), nil) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.CreateMultiEntityAnomalyDetector(ctx, r, false, false) assert.EqualError(t, err, "Cannot create anomaly detector with name [testdata-detector] as it's already used by detector [wR_1XXMBs3q1IVz33Sk-]") }) } func getSearchPayload(name string) entity.SearchRequest { return entity.SearchRequest{ Query: entity.SearchQuery{ Match: entity.Match{ Name: name, }, }, } } func TestController_StopDetectorByName(t *testing.T) { t.Run("stop empty detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StopDetectorByName(ctx, "", false) assert.Error(t, err) }) t.Run("stop detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(nil, errors.New("gateway failed")) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StopDetectorByName(ctx, "detector", false) assert.NoError(t, err) }) t.Run("search detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return(nil, errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.StopDetectorByName(ctx, "detector", false) assert.Error(t, err) }) t.Run("stop detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(mapper.StringToStringPtr("Stopped Detector"), nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StopDetectorByName(ctx, "detector", false) assert.NoError(t, err) }) } func TestController_StartDetectorByName(t *testing.T) { t.Run("start empty detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StartDetectorByName(ctx, "", false) assert.Error(t, err) }) t.Run("start detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, "detectorID").Return(errors.New("gateway failed")) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StartDetectorByName(ctx, "detector", false) assert.NoError(t, err) }) t.Run("search detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return(nil, errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.StartDetectorByName(ctx, "detector", false) assert.Error(t, err) }) t.Run("start detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StartDetector(ctx, "detectorID").Return(nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.StartDetectorByName(ctx, "detector", false) assert.NoError(t, err) }) } func TestController_DeleteDetectorByName(t *testing.T) { t.Run("invalid detector name", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, "", false, false) assert.EqualError(t, err, "name cannot be empty") }) t.Run("delete gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, "detectorID").Return(errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, false, false) assert.NoError(t, err) }) t.Run("delete gateway succeeded", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, "detectorID").Return(nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, false, false) assert.NoError(t, err) }) t.Run("stop gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(nil, errors.New("failed")) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, true, false) assert.NoError(t, err) }) t.Run("stop gateway succeeded", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(mapper.StringToStringPtr("Stopped Detector"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, "detectorID").Return(nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, true, false) assert.NoError(t, err) }) t.Run("cancelled from user", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("no\n")) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, true, false) assert.NoError(t, err) }) t.Run("agreed by user", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload(mockDetectorName)).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().StopDetector(ctx, "detectorID").Return(mapper.StringToStringPtr("Stopped Detector"), nil) mockADGateway.EXPECT().DeleteDetector(ctx, "detectorID").Return(nil) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.DeleteDetectorByName(ctx, mockDetectorName, true, false) assert.NoError(t, err) }) } func TestController_GetDetectorByName(t *testing.T) { detectorOutput := &entity.DetectorOutput{ ID: "detectorID", Name: "detector", Description: "Test detector", TimeField: "timestamp", Index: []string{"order*"}, Features: []entity.Feature{ { Name: "total_order", Enabled: true, AggregationQuery: []byte(`{"total_order":{"sum":{"field":"value"}}}`), }, }, Filter: []byte(`{"bool" : {"filter" : [{"exists" : {"field" : "value","boost" : 1.0}}],"adjust_pure_negative" : true,"boost" : 1.0}}`), Interval: "5m", Delay: "1m", LastUpdatedAt: 1589441737319, SchemaVersion: 0, } t.Run("get empty detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.GetDetectorsByName(ctx, "", false) assert.Error(t, err) }) t.Run("search detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return(nil, errors.New("gateway failed")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) _, err := ctrl.GetDetectorsByName(ctx, "detector", false) assert.EqualError(t, err, "gateway failed") }) t.Run("search detector gateway returned empty", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return([]byte(`{}`), nil) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) actual, err := ctrl.GetDetectorsByName(ctx, "detector", false) assert.NoError(t, err) assert.Nil(t, actual) }) t.Run("get detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().GetDetector(ctx, "detectorID").Return(nil, errors.New("gateway failed")) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(&stdin, mockESController, mockADGateway) _, err := ctrl.GetDetectorsByName(ctx, "detector", false) assert.EqualError(t, err, "gateway failed") }) t.Run("get detector", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().SearchDetector(ctx, getSearchPayload("detector")).Return( helperLoadBytes(t, "search_response.json"), nil) mockADGateway.EXPECT().GetDetector(ctx, "detectorID").Return(helperLoadBytes(t, "get_response.json"), nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) res, err := ctrl.GetDetectorsByName(ctx, "detector", false) assert.NoError(t, err) assert.EqualValues(t, *res[0], *detectorOutput) }) } func TestController_UpdateDetector(t *testing.T) { input := entity.UpdateDetectorUserInput{ ID: "m4ccEnIBTXsGi3mvMt9p", Name: "test-detector", Description: "Test detector", TimeField: "timestamp", Index: []string{"order*"}, Features: []entity.Feature{ { Name: "total_order", Enabled: true, AggregationQuery: []byte(`{"total_order":{"sum":{"field":"value"}}}`), }, }, Filter: []byte(`{"bool" : {"filter" : [{"exists" : {"field" : "value","boost" : 1.0}}],"adjust_pure_negative" : true,"boost" : 1.0}}`), Interval: "5m", Delay: "1m", LastUpdatedAt: 1589441737319, SchemaVersion: 0, } request := entity.UpdateDetector{ Name: "test-detector", Description: "Test detector", TimeField: "timestamp", Index: []string{"order*"}, Features: []entity.Feature{ { Name: "total_order", Enabled: true, AggregationQuery: []byte(`{"total_order":{"sum":{"field":"value"}}}`), }, }, Filter: []byte(`{"bool" : {"filter" : [{"exists" : {"field" : "value","boost" : 1.0}}],"adjust_pure_negative" : true,"boost" : 1.0}}`), Interval: entity.Interval{Period: entity.Period{ Duration: 5, Unit: "Minutes", }}, Delay: entity.Interval{Period: entity.Period{ Duration: 1, Unit: "Minutes", }}, } t.Run("update detector without ID", func(t *testing.T) { invalidInput := input invalidInput.ID = "" mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) ctx := context.Background() ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, invalidInput, true, true) assert.EqualError(t, err, "detector Id cannot be empty") }) t.Run("stale detector update failure", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(helperLoadBytes(t, "get_response.json"), nil) mockESController := mockController.NewMockController(mockCtrl) staleDetector := input staleDetector.LastUpdatedAt = input.LastUpdatedAt - 100 var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, staleDetector, false, true) assert.EqualError(t, err, "new version for detector is available. Please fetch latest version and then merge your changes") }) t.Run("don't update if user says no", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(helperLoadBytes(t, "get_response.json"), nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("no\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, false, true) assert.NoError(t, err) }) t.Run("stop detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().StopDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(nil, errors.New("failed to stop detector")) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, true, true) assert.EqualError(t, err, "failed to stop detector") }) t.Run("get detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return( nil, errors.New("failed to get detector")) mockESController := mockController.NewMockController(mockCtrl) ctrl := New(os.Stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, false, false) assert.EqualError(t, err, "failed to get detector") }) t.Run("update detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(helperLoadBytes(t, "get_response.json"), nil) mockADGateway.EXPECT().UpdateDetector(ctx, "m4ccEnIBTXsGi3mvMt9p", &request).Return(errors.New("failed to update")) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, false, true) assert.EqualError(t, err, "failed to update") }) t.Run("start detector gateway failed", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(helperLoadBytes(t, "get_response.json"), nil) mockADGateway.EXPECT().UpdateDetector(ctx, "m4ccEnIBTXsGi3mvMt9p", &request).Return(nil) mockADGateway.EXPECT().StartDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(errors.New("failed to start")) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, false, true) assert.EqualError(t, err, "failed to start") }) t.Run("force detector update success", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockESController := mockController.NewMockController(mockCtrl) staleDetector := input staleDetector.LastUpdatedAt = input.LastUpdatedAt - 100 mockADGateway.EXPECT().StopDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(nil, nil) mockADGateway.EXPECT().UpdateDetector(ctx, "m4ccEnIBTXsGi3mvMt9p", &request).Return(nil) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, true, false) assert.NoError(t, err) }) t.Run("update detector and start", func(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() ctx := context.Background() mockADGateway := gateway.NewMockGateway(mockCtrl) mockADGateway.EXPECT().GetDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(helperLoadBytes(t, "get_response.json"), nil) mockADGateway.EXPECT().UpdateDetector(ctx, "m4ccEnIBTXsGi3mvMt9p", &request).Return(nil) mockADGateway.EXPECT().StartDetector(ctx, "m4ccEnIBTXsGi3mvMt9p").Return(nil) mockESController := mockController.NewMockController(mockCtrl) var stdin bytes.Buffer stdin.Write([]byte("yes\n")) ctrl := New(&stdin, mockESController, mockADGateway) err := ctrl.UpdateDetector(ctx, input, false, true) assert.NoError(t, err) }) }