diff --git a/chaoscenter/graphql/server/pkg/database/mongodb/mocks/mongo_operations.go b/chaoscenter/graphql/server/pkg/database/mongodb/mocks/mongo_operations.go index 028ac7f2d6e..92dc3a3268e 100644 --- a/chaoscenter/graphql/server/pkg/database/mongodb/mocks/mongo_operations.go +++ b/chaoscenter/graphql/server/pkg/database/mongodb/mocks/mongo_operations.go @@ -17,91 +17,91 @@ type MongoOperator struct { } // WatchEvents implements mongodb.MongoOperator. -func (m MongoOperator) WatchEvents(ctx context.Context, client *mongo.Client, collectionType int, pipeline mongo.Pipeline, opts ...*options.ChangeStreamOptions) (*mongo.ChangeStream, error) { +func (m *MongoOperator) WatchEvents(ctx context.Context, client *mongo.Client, collectionType int, pipeline mongo.Pipeline, opts ...*options.ChangeStreamOptions) (*mongo.ChangeStream, error) { args := m.Called(ctx, client, collectionType, pipeline, opts) return args.Get(0).(*mongo.ChangeStream), args.Error(1) } // GetAuthConfig implements mongodb.MongoOperator. -func (m MongoOperator) GetAuthConfig(ctx context.Context, key string) (*mongodb.AuthConfig, error) { +func (m *MongoOperator) GetAuthConfig(ctx context.Context, key string) (*mongodb.AuthConfig, error) { args := m.Called(ctx, key) return args.Get(0).(*mongodb.AuthConfig), args.Error(1) } // Create provides a mock function with given fields: ctx, collectionType, document -func (m MongoOperator) Create(ctx context.Context, collectionType int, document interface{}) error { +func (m *MongoOperator) Create(ctx context.Context, collectionType int, document interface{}) error { args := m.Called(ctx, collectionType, document) return args.Error(0) } // CreateMany provides a mock function with given fields: ctx, collectionType, documents -func (m MongoOperator) CreateMany(ctx context.Context, collectionType int, documents []interface{}) error { +func (m *MongoOperator) CreateMany(ctx context.Context, collectionType int, documents []interface{}) error { args := m.Called(ctx, collectionType, documents) return args.Error(0) } // Get provides a mock function with given fields: ctx, collectionType, query -func (m MongoOperator) Get(ctx context.Context, collectionType int, query bson.D) (*mongo.SingleResult, error) { +func (m *MongoOperator) Get(ctx context.Context, collectionType int, query bson.D) (*mongo.SingleResult, error) { args := m.Called(ctx, collectionType, query) return args.Get(0).(*mongo.SingleResult), args.Error(1) } // List provides a mock function with given fields: ctx, collectionType, query -func (m MongoOperator) List(ctx context.Context, collectionType int, query bson.D, opts ...*options.FindOptions) (*mongo.Cursor, error) { +func (m *MongoOperator) List(ctx context.Context, collectionType int, query bson.D, opts ...*options.FindOptions) (*mongo.Cursor, error) { args := m.Called(ctx, collectionType, query) return args.Get(0).(*mongo.Cursor), args.Error(1) } // Update provides a mock function with given fields: ctx, collectionType, query, update, opts -func (m MongoOperator) Update(ctx context.Context, collectionType int, query, update bson.D, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { +func (m *MongoOperator) Update(ctx context.Context, collectionType int, query, update bson.D, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { args := m.Called(ctx, collectionType, query, update, opts) return args.Get(0).(*mongo.UpdateResult), args.Error(1) } // UpdateMany provides a mock function with given fields: ctx, collectionType, query, update, opts -func (m MongoOperator) UpdateMany(ctx context.Context, collectionType int, query, update bson.D, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { +func (m *MongoOperator) UpdateMany(ctx context.Context, collectionType int, query, update bson.D, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { args := m.Called(ctx, collectionType, query, update, opts) return args.Get(0).(*mongo.UpdateResult), args.Error(1) } // Replace provides a mock function with given fields: ctx, collectionType, query, replacement -func (m MongoOperator) Replace(ctx context.Context, collectionType int, query bson.D, replacement interface{}) (*mongo.UpdateResult, error) { +func (m *MongoOperator) Replace(ctx context.Context, collectionType int, query bson.D, replacement interface{}) (*mongo.UpdateResult, error) { args := m.Called(ctx, collectionType, query, replacement) return args.Get(0).(*mongo.UpdateResult), args.Error(1) } // Delete provides a mock function with given fields: ctx, collectionType, query, opts -func (m MongoOperator) Delete(ctx context.Context, collectionType int, query bson.D, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { +func (m *MongoOperator) Delete(ctx context.Context, collectionType int, query bson.D, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { args := m.Called(ctx, collectionType, query, opts) return args.Get(0).(*mongo.DeleteResult), args.Error(1) } // CountDocuments provides a mock function with given fields: ctx, collectionType, query, opts -func (m MongoOperator) CountDocuments(ctx context.Context, collectionType int, query bson.D, opts ...*options.CountOptions) (int64, error) { +func (m *MongoOperator) CountDocuments(ctx context.Context, collectionType int, query bson.D, opts ...*options.CountOptions) (int64, error) { args := m.Called(ctx, collectionType, query, opts) return args.Get(0).(int64), args.Error(1) } // Aggregate provides a mock function with given fields: ctx, collectionType, pipeline, opts -func (m MongoOperator) Aggregate(ctx context.Context, collectionType int, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) { +func (m *MongoOperator) Aggregate(ctx context.Context, collectionType int, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) { args := m.Called(ctx, collectionType, pipeline, opts) return args.Get(0).(*mongo.Cursor), args.Error(1) } // GetCollection provides a mock function with given fields: collectionType -func (m MongoOperator) GetCollection(collectionType int) (*mongo.Collection, error) { +func (m *MongoOperator) GetCollection(collectionType int) (*mongo.Collection, error) { args := m.Called(collectionType) return args.Get(0).(*mongo.Collection), args.Error(1) } // ListCollection provides a mock function with given fields: ctx, mclient -func (m MongoOperator) ListCollection(ctx context.Context, mclient *mongo.Client) ([]string, error) { +func (m *MongoOperator) ListCollection(ctx context.Context, mclient *mongo.Client) ([]string, error) { args := m.Called(ctx, mclient) return args.Get(0).([]string), args.Error(1) } // ListDataBase provides a mock function with given fields: ctx, mclient -func (m MongoOperator) ListDataBase(ctx context.Context, mclient *mongo.Client) ([]string, error) { +func (m *MongoOperator) ListDataBase(ctx context.Context, mclient *mongo.Client) ([]string, error) { args := m.Called(ctx, mclient) return args.Get(0).([]string), args.Error(1) } diff --git a/chaoscenter/graphql/server/pkg/probe/handler/handler.go b/chaoscenter/graphql/server/pkg/probe/handler/handler.go index 2bde576753f..26b54883b49 100644 --- a/chaoscenter/graphql/server/pkg/probe/handler/handler.go +++ b/chaoscenter/graphql/server/pkg/probe/handler/handler.go @@ -58,7 +58,13 @@ func Error(logFields logrus.Fields, message string) error { // AddProbe - Create a new Probe func (p *probeService) AddProbe(ctx context.Context, probe model.ProbeRequest, projectID string) (*model.Probe, error) { - // TODO: Add check if probe exists + isUnique, err := p.ValidateUniqueProbe(ctx, probe.Name, projectID) + if err != nil { + return nil, err + } + if !isUnique { + return nil, errors.New("probe already exists") + } var ( currTime = time.Now().UnixMilli() diff --git a/chaoscenter/graphql/server/pkg/probe/handler/handler_test.go b/chaoscenter/graphql/server/pkg/probe/handler/handler_test.go new file mode 100644 index 00000000000..846d1f2a074 --- /dev/null +++ b/chaoscenter/graphql/server/pkg/probe/handler/handler_test.go @@ -0,0 +1,77 @@ +package handler + +import ( + "context" + "errors" + "testing" + + "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model" + "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb" + dbMocks "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/mocks" + dbSchemaProbe "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/probe" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func newProbeServiceWithMock(mockOp *dbMocks.MongoOperator) Service { + return NewProbeService(dbSchemaProbe.NewChaosProbeOperator(mockOp)) +} + +func TestValidateUniqueProbe_ReturnsTrue_WhenUnique(t *testing.T) { + mockOp := new(dbMocks.MongoOperator) + svc := newProbeServiceWithMock(mockOp) + + mockOp.On("CountDocuments", mock.Anything, mongodb.ChaosProbeCollection, mock.Anything, mock.Anything). + Return(int64(0), nil).Once() + + unique, err := svc.ValidateUniqueProbe(context.Background(), "my-probe", "project-1") + + assert.NoError(t, err) + assert.True(t, unique) + mockOp.AssertExpectations(t) +} + +func TestValidateUniqueProbe_ReturnsFalse_WhenDuplicate(t *testing.T) { + mockOp := new(dbMocks.MongoOperator) + svc := newProbeServiceWithMock(mockOp) + + mockOp.On("CountDocuments", mock.Anything, mongodb.ChaosProbeCollection, mock.Anything, mock.Anything). + Return(int64(1), nil).Once() + + unique, err := svc.ValidateUniqueProbe(context.Background(), "my-probe", "project-1") + + assert.NoError(t, err) + assert.False(t, unique) + mockOp.AssertExpectations(t) +} + +func TestValidateUniqueProbe_ReturnsError_WhenDBFails(t *testing.T) { + mockOp := new(dbMocks.MongoOperator) + svc := newProbeServiceWithMock(mockOp) + + dbErr := errors.New("db down") + mockOp.On("CountDocuments", mock.Anything, mongodb.ChaosProbeCollection, mock.Anything, mock.Anything). + Return(int64(0), dbErr).Once() + + unique, err := svc.ValidateUniqueProbe(context.Background(), "my-probe", "project-1") + + assert.Error(t, err) + assert.Equal(t, dbErr, err) + assert.False(t, unique) + mockOp.AssertExpectations(t) +} + +func TestAddProbe_DuplicateName(t *testing.T) { + mockOp := new(dbMocks.MongoOperator) + svc := newProbeServiceWithMock(mockOp) + + mockOp.On("CountDocuments", mock.Anything, mongodb.ChaosProbeCollection, mock.Anything, mock.Anything). + Return(int64(1), nil).Once() + + _, err := svc.AddProbe(context.Background(), model.ProbeRequest{Name: "postman-test-probe-1"}, "project-1") + + assert.Error(t, err) + assert.Equal(t, "probe already exists", err.Error()) + mockOp.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything) + mockOp.AssertExpectations(t) +}