From a36c1a602ad81c48a02fc30686de0326ab1ffae6 Mon Sep 17 00:00:00 2001 From: namkyu1999 Date: Mon, 1 May 2023 23:56:38 +0900 Subject: [PATCH 1/3] feat: add unit tests to chaos-workflow package Signed-off-by: namkyu1999 --- litmus-portal/graphql-server/go.mod | 5 +- litmus-portal/graphql-server/go.sum | 23 +- .../pkg/chaos-workflow/handler/handler.go | 2 +- .../chaos-workflow/handler/handler_test.go | 1399 +++++++++++++++++ .../pkg/chaos-workflow/model/mocks/service.go | 76 + .../pkg/chaos-workflow/service_test.go | 88 ++ .../pkg/chaos-workflow/util_test.go | 62 + .../pkg/cluster/model/mocks/service.go | 88 ++ .../graphql-server/pkg/cluster/service.go | 1 + .../graphql-server/pkg/data-store/store.go | 2 +- .../pkg/database/mongodb/model/mocks.go | 87 + .../mongodb/workflowtemplate/operations.go | 6 +- .../pkg/gitops/model/mocks/service.go | 62 + 13 files changed, 1886 insertions(+), 15 deletions(-) create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/service.go create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/util_test.go create mode 100644 litmus-portal/graphql-server/pkg/cluster/model/mocks/service.go create mode 100644 litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go create mode 100644 litmus-portal/graphql-server/pkg/gitops/model/mocks/service.go diff --git a/litmus-portal/graphql-server/go.mod b/litmus-portal/graphql-server/go.mod index 8c7987253fa..2bc33539b47 100644 --- a/litmus-portal/graphql-server/go.mod +++ b/litmus-portal/graphql-server/go.mod @@ -23,11 +23,12 @@ require ( github.com/prometheus/client_golang v1.12.1 github.com/prometheus/common v0.32.1 github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/tidwall/gjson v1.14.0 github.com/tidwall/sjson v1.2.4 github.com/vektah/gqlparser/v2 v2.1.0 - go.mongodb.org/mongo-driver v1.8.2 - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd + go.mongodb.org/mongo-driver v1.11.4 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 diff --git a/litmus-portal/graphql-server/go.sum b/litmus-portal/graphql-server/go.sum index 2851a6590f3..9d002ff0d90 100644 --- a/litmus-portal/graphql-server/go.sum +++ b/litmus-portal/graphql-server/go.sum @@ -738,7 +738,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-swagger/go-swagger v0.29.0/go.mod h1:Z4GJzI+bHKKkGB2Ji1rawpi3/ldXX8CkzGIa9HAC5EE= github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= @@ -1381,6 +1380,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= @@ -1730,6 +1730,9 @@ github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1Sd github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1738,9 +1741,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stripe/stripe-go v70.15.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1813,10 +1819,12 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.0 h1:d70R37I0HrDLsafRrMBXyrD4lmQbCHE873t00Vr0gm0= github.com/xdg-go/scram v1.1.0/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= @@ -1885,8 +1893,9 @@ go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.8.2 h1:8ssUXufb90ujcIvR6MyE1SchaNj0SFxsakiZgxIyrMk= go.mongodb.org/mongo-driver v1.8.2/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1977,8 +1986,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler.go b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler.go index 48503631568..9455063b8f1 100644 --- a/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler.go +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler.go @@ -365,7 +365,7 @@ func (c *ChaosWorkflowHandler) ListWorkflowRuns(request model.ListWorkflowRunsRe // Filtering based on date range if request.Filter.DateRange != nil { - endDate := string(time.Now().Unix()) + endDate := strconv.FormatInt(time.Now().Unix(), 10) if request.Filter.DateRange.EndDate != nil { endDate = *request.Filter.DateRange.EndDate } diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go new file mode 100644 index 00000000000..d367eee927b --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go @@ -0,0 +1,1399 @@ +package handler_test + +import ( + "context" + "errors" + "io/ioutil" + "os" + "strconv" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt" + "github.com/google/uuid" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/authorization" + chaosWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow/handler" + chaosWorkflowMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks" + clusterMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/cluster/model/mocks" + store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" + dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" + mongodbMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model" + dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + dbOperationsWorkflowTemplate "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate" + gitOpsMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/gitops/model/mocks" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/utils" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +var ( + mongoOperator = new(mongodbMocks.MongoOperator) + clusterService = new(clusterMocks.ClusterService) + chaosWorkflowService = new(chaosWorkflowMocks.ChaosWorkflowService) + gitOpsService = new(gitOpsMocks.GitOpsService) + chaosWorkflowOperator = dbOperationsWorkflow.NewChaosWorkflowOperator(mongoOperator) + chaosWorkflowTemplateOperator = dbOperationsWorkflowTemplate.NewWorkflowTemplateOperator(mongoOperator) + chaosWorkflowHandler = handler.NewChaosWorkflowHandler( + chaosWorkflowService, + clusterService, + gitOpsService, + chaosWorkflowOperator, + chaosWorkflowTemplateOperator, + mongoOperator, + ) +) + +// TestMain is the entry point for testing +func TestMain(m *testing.M) { + gin.SetMode(gin.TestMode) + log.SetOutput(ioutil.Discard) + os.Exit(m.Run()) +} + +// TestChaosWorkflowHandler_CreateChaosWorkflow is used to test the CreateChaosWorkflow method +func TestChaosWorkflowHandler_CreateChaosWorkflow(t *testing.T) { + // given + state := store.NewStore() + username, _ := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{"username": "test"}).SignedString([]byte(utils.Config.JwtSecret)) + ctx := context.Background() + workflowID := uuid.NewString() + workflowType := dbOperationsWorkflow.Workflow + testcases := []struct { + name string + request *model.ChaosWorkFlowRequest + given func(request *model.ChaosWorkFlowRequest) + isError bool + }{ + { + name: "workflow creation successful", + request: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + WorkflowID: &workflowID, + }, + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowCreation", request, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(nil).Once() + }, + }, + { + name: "workflow creation failed: process workflow failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow creation failed: gitOps upsert workflow to git failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow creation failed: process workflow creation failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowCreation", request, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("")).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(nil).Once() + }, + isError: true, + }, + { + name: "workflow creation failed: invalid jwt", + request: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + WorkflowID: &workflowID, + }, + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, "invalid jwt") + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowCreation", request, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(nil).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given(tc.request) + // when + _, err := chaosWorkflowHandler.CreateChaosWorkflow(ctx, tc.request, state) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_DeleteChaosWorkflow is used to test the DeleteChaosWorkflow method +func TestChaosWorkflowHandler_DeleteChaosWorkflow(t *testing.T) { + // given + state := store.NewStore() + username, _ := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{"username": "test"}).SignedString([]byte(utils.Config.JwtSecret)) + ctx := context.Background() + workflowID := uuid.NewString() + emptyWorkflowID := "" + workflowRunID := uuid.NewString() + emptyWorkflowRunID := "" + testcases := []struct { + name string + workflowID *string + workflowRunID *string + given func() + isError bool + }{ + { + name: "workflow deletion successful: workflowID and workflowRunID both provided", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunDelete", mock.Anything, mock.Anything, mock.Anything, mock.Anything, state).Return(nil).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + }, + { + name: "workflow deletion successful: workflowID and workflowRunID both empty", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + workflowID: &emptyWorkflowID, + workflowRunID: &emptyWorkflowRunID, + }, + { + name: "workflow deletion successful: workflowID provided", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + gitOpsService.On("DeleteWorkflowFromGit", mock.Anything, mock.Anything).Return(nil).Once() + chaosWorkflowService.On("ProcessWorkflowDelete", mock.Anything, mock.Anything, mock.Anything, state).Return(nil).Once() + }, + workflowRunID: &emptyWorkflowRunID, + workflowID: &workflowID, + }, + { + name: "workflow deletion failed: GetWorkflow failed", + given: func() { + singleResult := mongo.NewSingleResultFromDocument(nil, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow deletion failed: invalid jwt", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, "invalid jwt") + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + isError: true, + }, + { + name: "workflow deletion failed: workflowID and workflowRunID both provided but ProcessWorkflowRunDelete failed", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunDelete", mock.Anything, mock.Anything, mock.Anything, mock.Anything, state).Return(errors.New("")).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + isError: true, + }, + { + name: "workflow deletion failed: workflowID provided but gitOps DeleteWorkflowFromGit failed", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + gitOpsService.On("DeleteWorkflowFromGit", mock.Anything, mock.Anything).Return(errors.New("")).Once() + }, + workflowRunID: &emptyWorkflowRunID, + workflowID: &workflowID, + isError: true, + }, + { + name: "workflow deletion failed: workflowID provided but ProcessWorkflowDelete failed", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + gitOpsService.On("DeleteWorkflowFromGit", mock.Anything, mock.Anything).Return(nil).Once() + chaosWorkflowService.On("ProcessWorkflowDelete", mock.Anything, mock.Anything, mock.Anything, state).Return(errors.New("")).Once() + }, + workflowRunID: &emptyWorkflowRunID, + workflowID: &workflowID, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.DeleteChaosWorkflow(ctx, uuid.NewString(), tc.workflowID, tc.workflowRunID, state) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_TerminateChaosWorkflow is used to test the TerminateChaosWorkflow method +func TestChaosWorkflowHandler_TerminateChaosWorkflow(t *testing.T) { + // given + state := store.NewStore() + username, _ := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{"username": "test"}).SignedString([]byte(utils.Config.JwtSecret)) + ctx := context.Background() + workflowID := uuid.NewString() + emptyWorkflowID := "" + workflowRunID := uuid.NewString() + emptyWorkflowRunID := "" + testcases := []struct { + name string + workflowID *string + workflowRunID *string + given func() + isError bool + }{ + { + name: "workflow deletion successful: workflowID and workflowRunID both provided", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunDelete", mock.Anything, mock.Anything, mock.Anything, mock.Anything, state).Return(nil).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + }, + { + name: "workflow deletion failed: GetWorkflow failed", + given: func() { + singleResult := mongo.NewSingleResultFromDocument(nil, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow deletion failed: invalid jwt", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, "invalid jwt") + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + isError: true, + }, + { + name: "workflow deletion failed: workflowRunID empty", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + workflowRunID: &emptyWorkflowRunID, + workflowID: &workflowID, + isError: true, + }, + { + name: "workflow deletion failed: workflowID empty", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + workflowRunID: &workflowID, + workflowID: &emptyWorkflowID, + isError: true, + }, + { + name: "workflow deletion failed: ProcessWorkflowRunDelete failed", + given: func() { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + findResult := bson.D{{"workflow_id", workflowID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunDelete", mock.Anything, mock.Anything, mock.Anything, mock.Anything, state).Return(errors.New("")).Once() + }, + workflowRunID: &workflowRunID, + workflowID: &workflowID, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.TerminateChaosWorkflow(ctx, uuid.NewString(), tc.workflowID, tc.workflowRunID, state) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_UpdateChaosWorkflow is used to test the UpdateChaosWorkflow method +func TestChaosWorkflowHandler_UpdateChaosWorkflow(t *testing.T) { + // given + state := store.NewStore() + username, _ := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{"username": "test"}).SignedString([]byte(utils.Config.JwtSecret)) + ctx := context.Background() + workflowID := uuid.NewString() + workflowType := dbOperationsWorkflow.Workflow + testcases := []struct { + name string + request *model.ChaosWorkFlowRequest + given func(request *model.ChaosWorkFlowRequest) + isError bool + }{ + { + name: "workflow update successful", + request: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + WorkflowID: &workflowID, + }, + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowUpdate", request, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(nil).Once() + }, + }, + { + name: "workflow update failed: process workflow failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow update failed: gitOps upsert workflow to git failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(errors.New("")).Once() + }, + isError: true, + }, + { + name: "workflow update failed: ProcessWorkflowUpdate failed", + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, username) + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowUpdate", request, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("")).Once() + gitOpsService.On("UpsertWorkflowToGit", ctx, request).Return(nil).Once() + }, + isError: true, + }, + { + name: "workflow update failed: invalid jwt", + request: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + WorkflowID: &workflowID, + }, + given: func(request *model.ChaosWorkFlowRequest) { + ctx = context.WithValue(ctx, authorization.AuthKey, "invalid jwt") + chaosWorkflowService.On("ProcessWorkflow", request).Return(request, &workflowType, nil).Once() + chaosWorkflowService.On("ProcessWorkflowCreation", request, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given(tc.request) + // when + _, err := chaosWorkflowHandler.UpdateChaosWorkflow(ctx, tc.request, state) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_ListWorkflowRuns is used to test the ListWorkflowRuns method +func TestChaosWorkflowHandler_ListWorkflowRuns(t *testing.T) { + // given + workflowID := uuid.NewString() + workflowName := uuid.NewString() + workflowRunID := uuid.NewString() + clusterName := uuid.NewString() + workflowStatus := model.WorkflowRunStatusRunning + endDate := strconv.FormatInt(time.Now().Unix(), 10) + testcases := []struct { + name string + request model.ListWorkflowRunsRequest + given func() + isError bool + }{ + { + name: "success", + request: model.ListWorkflowRunsRequest{ + ProjectID: uuid.NewString(), + WorkflowRunIDs: []*string{&workflowRunID}, + WorkflowIDs: []*string{&workflowID}, + Filter: &model.WorkflowRunFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + WorkflowStatus: &workflowStatus, + DateRange: &model.DateRange{ + StartDate: strconv.FormatInt(time.Now().Unix(), 10), + EndDate: &endDate, + }, + }, + Pagination: &model.Pagination{ + Page: 1, + }, + }, + given: func() { + findResult := []interface{}{bson.D{ + {"total_filtered_workflow_runs", []dbOperationsWorkflow.TotalFilteredData{ + { + Count: 1, + }, + }}, + {"flattened_workflow_runs", []dbOperationsWorkflow.FlattenedWorkflowRun{ + { + WorkflowRuns: dbOperationsWorkflow.ChaosWorkflowRun{ + WorkflowRunID: workflowRunID, + }, + }, + }}}, + } + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "success: mongo return empty cursor", + request: model.ListWorkflowRunsRequest{ + ProjectID: uuid.NewString(), + WorkflowRunIDs: []*string{&workflowRunID}, + WorkflowIDs: []*string{&workflowID}, + Filter: &model.WorkflowRunFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + WorkflowStatus: &workflowStatus, + DateRange: &model.DateRange{ + StartDate: strconv.FormatInt(time.Now().Unix(), 10), + EndDate: &endDate, + }, + }, + Pagination: &model.Pagination{ + Page: 1, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "failure: GetAggregateWorkflows failed", + request: model.ListWorkflowRunsRequest{ + ProjectID: uuid.NewString(), + WorkflowRunIDs: []*string{&workflowRunID}, + WorkflowIDs: []*string{&workflowID}, + Filter: &model.WorkflowRunFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + WorkflowStatus: &workflowStatus, + DateRange: &model.DateRange{ + StartDate: strconv.FormatInt(time.Now().Unix(), 10), + EndDate: &endDate, + }, + }, + Pagination: &model.Pagination{ + Page: 1, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + { + name: "success: with sort by descending order of time", + request: model.ListWorkflowRunsRequest{ + ProjectID: uuid.NewString(), + WorkflowRunIDs: []*string{&workflowRunID}, + WorkflowIDs: []*string{&workflowID}, + Filter: &model.WorkflowRunFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + WorkflowStatus: &workflowStatus, + DateRange: &model.DateRange{ + StartDate: strconv.FormatInt(time.Now().Unix(), 10), + EndDate: &endDate, + }, + }, + Sort: &model.WorkflowRunSortInput{ + Field: model.WorkflowSortingFieldTime, + }, + }, + given: func() { + findResult := []interface{}{bson.D{ + {"total_filtered_workflow_runs", []dbOperationsWorkflow.TotalFilteredData{ + { + Count: 1, + }, + }}, + {"flattened_workflow_runs", []dbOperationsWorkflow.FlattenedWorkflowRun{ + { + WorkflowRuns: dbOperationsWorkflow.ChaosWorkflowRun{ + WorkflowRunID: workflowRunID, + }, + }, + }}}, + } + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "success: with sort by descending order of name", + request: model.ListWorkflowRunsRequest{ + ProjectID: uuid.NewString(), + WorkflowRunIDs: []*string{&workflowRunID}, + WorkflowIDs: []*string{&workflowID}, + Filter: &model.WorkflowRunFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + WorkflowStatus: &workflowStatus, + DateRange: &model.DateRange{ + StartDate: strconv.FormatInt(time.Now().Unix(), 10), + EndDate: &endDate, + }, + }, + Sort: &model.WorkflowRunSortInput{ + Field: model.WorkflowSortingFieldName, + }, + }, + given: func() { + findResult := []interface{}{bson.D{ + {"total_filtered_workflow_runs", []dbOperationsWorkflow.TotalFilteredData{ + { + Count: 1, + }, + }}, + {"flattened_workflow_runs", []dbOperationsWorkflow.FlattenedWorkflowRun{ + { + WorkflowRuns: dbOperationsWorkflow.ChaosWorkflowRun{ + WorkflowRunID: workflowRunID, + }, + }, + }}}, + } + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.ListWorkflowRuns(tc.request) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_ListWorkflows is used to test the ListWorkflows method +func TestChaosWorkflowHandler_ListWorkflows(t *testing.T) { + // given + workflowName := uuid.NewString() + workflowID := uuid.NewString() + clusterName := uuid.NewString() + cluster := dbSchemaCluster.Cluster{ClusterName: clusterName, ClusterID: uuid.NewString(), ClusterType: uuid.NewString()} + findResult := []interface{}{bson.D{ + {"total_filtered_workflows", []dbOperationsWorkflow.TotalFilteredData{ + { + Count: 1, + }, + }}, + {"scheduled_workflows", []dbOperationsWorkflow.ChaosWorkFlowRequest{ + { + WorkflowRuns: []*dbOperationsWorkflow.ChaosWorkflowRun{ + { + WorkflowRunID: workflowID, + }, + }, + }, + }}}, + } + testcases := []struct { + name string + request model.ListWorkflowsRequest + given func() + isError bool + }{ + { + name: "success", + request: model.ListWorkflowsRequest{ + ProjectID: uuid.NewString(), + WorkflowIDs: []*string{&workflowID}, + Pagination: &model.Pagination{Page: 1}, + Filter: &model.WorkflowFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(cluster, nil).Once() + }, + }, + { + name: "success: MongoOperator Aggregate returns empty cursor", + request: model.ListWorkflowsRequest{ + ProjectID: uuid.NewString(), + WorkflowIDs: []*string{&workflowID}, + Pagination: &model.Pagination{Page: 1}, + Filter: &model.WorkflowFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "success: with sort by descending order of name", + request: model.ListWorkflowsRequest{ + ProjectID: uuid.NewString(), + WorkflowIDs: []*string{&workflowID}, + Pagination: &model.Pagination{Page: 1}, + Filter: &model.WorkflowFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + }, + Sort: &model.WorkflowSortInput{ + Field: model.WorkflowSortingFieldName, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(cluster, nil).Once() + }, + }, + { + name: "success: with sort by descending order of time", + request: model.ListWorkflowsRequest{ + ProjectID: uuid.NewString(), + WorkflowIDs: []*string{&workflowID}, + Pagination: &model.Pagination{Page: 1}, + Filter: &model.WorkflowFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + }, + Sort: &model.WorkflowSortInput{ + Field: model.WorkflowSortingFieldTime, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(cluster, nil).Once() + }, + }, + { + name: "failure: GetAggregateWorkflows failed", + request: model.ListWorkflowsRequest{ + ProjectID: uuid.NewString(), + WorkflowIDs: []*string{&workflowID}, + Pagination: &model.Pagination{Page: 1}, + Filter: &model.WorkflowFilterInput{ + WorkflowName: &workflowName, + ClusterName: &clusterName, + }, + }, + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("Aggregate", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.ListWorkflows(tc.request) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_PodLog is used to test the PodLog method +func TestChaosWorkflowHandler_PodLog(t *testing.T) { + // given + state := store.NewStore() + cluster := dbSchemaCluster.Cluster{ProjectID: uuid.NewString(), ClusterID: uuid.NewString()} + testcases := []struct { + name string + request model.PodLog + given func() + isError bool + }{ + { + name: "success", + request: model.PodLog{ + RequestID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + ClusterID: &model.ClusterIdentity{ClusterID: cluster.ClusterID}, + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(&cluster, nil).Once() + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + action := make(chan *model.PodLogResponse, 1) + t.Cleanup(func() { delete(state.WorkflowLog, tc.request.RequestID) }) + state.WorkflowLog[tc.request.RequestID] = action + // when + _, err := chaosWorkflowHandler.PodLog(tc.request, *state) + // then + select { + case result := <-action: + assert.Equal(t, tc.request.WorkflowRunID, result.WorkflowRunID) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_GetLogs is used to test the GetLogs method +func TestChaosWorkflowHandler_GetLogs(t *testing.T) { + // given + state := store.NewStore() + requestID := uuid.NewString() + testcases := []struct { + name string + pod model.PodLogRequest + isError bool + }{ + { + name: "success", + pod: model.PodLogRequest{ + WorkflowRunID: uuid.NewString(), + ClusterID: uuid.NewString(), + PodName: uuid.NewString(), + PodType: uuid.NewString(), + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + action := make(chan *model.PodLogResponse, 1) + t.Cleanup(func() { delete(state.WorkflowLog, requestID) }) + state.WorkflowLog[requestID] = action + // when + chaosWorkflowHandler.GetLogs(requestID, tc.pod, *state) + // then + select { + case result := <-action: + assert.Equal(t, tc.pod.WorkflowRunID, result.WorkflowRunID) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + }) + } +} + +// TestChaosWorkflowHandler_ChaosWorkflowRun is used to test the ChaosWorkflowRun method +func TestChaosWorkflowHandler_ChaosWorkflowRun(t *testing.T) { + // given + clusterID := uuid.NewString() + cluster := &dbSchemaCluster.Cluster{ + ProjectID: uuid.NewString(), + ClusterID: clusterID, + } + state := store.NewStore() + testcases := []struct { + name string + request model.WorkflowRunRequest + given func() + isError bool + }{ + { + name: "success", + request: model.WorkflowRunRequest{ + ClusterID: &model.ClusterIdentity{ClusterID: clusterID}, + WorkflowID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(cluster, nil).Once() + mongoOperator.On("CountDocuments", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(int64(1), nil).Once() + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + chaosWorkflowService.On("SendWorkflowEvent", mock.Anything, state).Return().Once() + }, + }, + { + name: "success: workflow execution completed", + request: model.WorkflowRunRequest{ + ClusterID: &model.ClusterIdentity{ClusterID: clusterID}, + WorkflowID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + Completed: true, + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(cluster, nil).Once() + mongoOperator.On("CountDocuments", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(int64(1), nil).Once() + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + chaosWorkflowService.On("SendWorkflowEvent", mock.Anything, state).Return().Once() + chaosWorkflowService.On("ProcessCompletedWorkflowRun", mock.Anything, mock.Anything).Return(chaosWorkflow.WorkflowRunMetrics{}, nil).Once() + }, + }, + { + name: "failure: cannot verify cluster", + request: model.WorkflowRunRequest{ + ClusterID: &model.ClusterIdentity{ClusterID: clusterID}, + WorkflowID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(cluster, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: cannot Update workflowRun", + request: model.WorkflowRunRequest{ + ClusterID: &model.ClusterIdentity{ClusterID: clusterID}, + WorkflowID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(cluster, nil).Once() + mongoOperator.On("CountDocuments", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(int64(1), errors.New("")).Once() + }, + isError: true, + }, + { + name: "success: discard duplicated event", + request: model.WorkflowRunRequest{ + ClusterID: &model.ClusterIdentity{ClusterID: clusterID}, + WorkflowID: uuid.NewString(), + WorkflowRunID: uuid.NewString(), + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(cluster, nil).Once() + mongoOperator.On("CountDocuments", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything).Return(int64(1), nil).Once() + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 0}, nil).Once() + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.ChaosWorkflowRun(tc.request, *state) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_ReRunChaosWorkFlow is used to test the ReRunChaosWorkFlow method +func TestChaosWorkflowHandler_ReRunChaosWorkFlow(t *testing.T) { + // given + projectID := uuid.NewString() + workflowID := uuid.NewString() + clusterID := uuid.NewString() + cluster := dbSchemaCluster.Cluster{ + ClusterID: clusterID, + IsActive: true, + } + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}, {"workflow_manifest", "{\"kind\":\"job\""}, {"project_id", projectID}, {"cluster_id", clusterID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(cluster, nil).Once() + }, + }, + { + name: "failure: cannot get workflow", + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: workflow count 0", + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + }, + isError: true, + }, + { + name: "failure: cron job cannot be re-run", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}, {"workflow_manifest", "{\"kind\":\"cronworkflow\""}, {"project_id", projectID}, {"cluster_id", clusterID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + }, + isError: true, + }, + { + name: "failure: mongo operator cannot GetCluster", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}, {"workflow_manifest", "{\"kind\":\"job\""}, {"project_id", projectID}, {"cluster_id", clusterID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(cluster, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: mongo operator cannot GetCluster", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}, {"workflow_manifest", "{\"kind\":\"job\""}, {"project_id", projectID}, {"cluster_id", clusterID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + notActivatedCluster := dbSchemaCluster.Cluster{IsActive: false} + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + clusterService.On("GetCluster", mock.Anything).Return(notActivatedCluster, nil).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := chaosWorkflowHandler.ReRunChaosWorkFlow(projectID, workflowID, uuid.NewString()) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_KubeObj is used to test the KubeObj method +func TestChaosWorkflowHandler_KubeObj(t *testing.T) { + // given + state := store.NewStore() + cluster := dbSchemaCluster.Cluster{ProjectID: uuid.NewString(), ClusterID: uuid.NewString()} + testcases := []struct { + name string + request model.KubeObjectData + given func() + isError bool + }{ + { + name: "success", + request: model.KubeObjectData{ + RequestID: uuid.NewString(), + ClusterID: &model.ClusterIdentity{ClusterID: cluster.ClusterID}, + KubeObj: uuid.NewString(), + }, + given: func() { + clusterService.On("VerifyCluster", mock.Anything).Return(&cluster, nil).Once() + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + action := make(chan *model.KubeObjectResponse, 1) + t.Cleanup(func() { delete(state.KubeObjectData, tc.request.RequestID) }) + state.KubeObjectData[tc.request.RequestID] = action + // when + _, err := chaosWorkflowHandler.KubeObj(tc.request, *state) + // then + select { + case result := <-action: + assert.Equal(t, tc.request.KubeObj, result.KubeObj) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestChaosWorkflowHandler_GetKubeObjData is used to test the GetKubeObjData method +func TestChaosWorkflowHandler_GetKubeObjData(t *testing.T) { + // given + state := store.NewStore() + requestID := uuid.NewString() + testcases := []struct { + name string + pod model.KubeObjectRequest + isError bool + }{ + { + name: "success", + pod: model.KubeObjectRequest{ + ClusterID: uuid.NewString(), + }, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + action := make(chan *model.KubeObjectResponse, 1) + t.Cleanup(func() { delete(state.KubeObjectData, requestID) }) + state.KubeObjectData[requestID] = action + // when + chaosWorkflowHandler.GetKubeObjData(requestID, tc.pod, *state) + // then + select { + case result := <-action: + assert.Equal(t, tc.pod.ClusterID, result.ClusterID) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + }) + } +} + +// TestChaosWorkflowHandler_CreateWorkflowTemplate is used to test the CreateWorkflowTemplate method +func TestChaosWorkflowHandler_CreateWorkflowTemplate(t *testing.T) { + // TODO: add test after refactoring grpc code +} + +// TestChaosWorkflowHandler_ListWorkflowManifests is used to test the ListWorkflowManifests method +func TestChaosWorkflowHandler_ListWorkflowManifests(t *testing.T) { + // given + ctx := context.Background() + projectID := uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := []interface{}{bson.D{{"project_id", projectID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "failure: mongo operator cannot list workflow templates", + given: func() { + findResult := []interface{}{bson.D{{"project_id", projectID}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + manifests, err := chaosWorkflowHandler.ListWorkflowManifests(ctx, projectID) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, 1, len(manifests)) + assert.Equal(t, projectID, manifests[0].ProjectID) + } + }) + } +} + +// TestChaosWorkflowHandler_GetWorkflowManifestByID is used to test the GetWorkflowManifestByID method +func TestChaosWorkflowHandler_GetWorkflowManifestByID(t *testing.T) { + // given + ctx := context.Background() + templateID := uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := bson.D{{"template_id", templateID}, {"project_id", uuid.NewString()}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything).Return(singleResult, nil).Once() + }, + }, + { + name: "failure: mongo operator cannot get workflow template", + given: func() { + singleResult := mongo.NewSingleResultFromDocument(nil, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + manifest, err := chaosWorkflowHandler.GetWorkflowManifestByID(ctx, templateID) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, templateID, manifest.TemplateID) + } + }) + } +} + +// TestChaosWorkflowHandler_DeleteWorkflowTemplate is used to test the DeleteWorkflowTemplate method +func TestChaosWorkflowHandler_DeleteWorkflowTemplate(t *testing.T) { + // given + ctx := context.Background() + templateID := uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + }, + }, + { + name: "failure: mongo operator cannot update workflow template", + given: func() { + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowTemplateCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{}, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + success, err := chaosWorkflowHandler.DeleteWorkflowTemplate(ctx, uuid.NewString(), templateID) + // then + if tc.isError { + assert.Error(t, err) + assert.False(t, success) + } else { + assert.NoError(t, err) + assert.True(t, success) + } + }) + } +} + +// TestChaosWorkflowHandler_SyncWorkflowRuns is used to test the SyncWorkflowRuns method +func TestChaosWorkflowHandler_SyncWorkflowRuns(t *testing.T) { + // given + ctx := context.Background() + state := store.NewStore() + projectID := uuid.NewString() + workflowID := uuid.NewString() + workflowRunID := uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := bson.D{{"workflow_id", workflowID}, {"project_id", projectID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunSync", workflowID, &workflowRunID, mock.Anything, state).Return(nil).Once() + }, + }, + { + name: "failure: mongo operator cannot get workflow", + given: func() { + singleResult := mongo.NewSingleResultFromDocument(nil, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: workflow has been removed", + given: func() { + findResult := bson.D{{"workflow_id", workflowID}, {"isRemoved", true}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + isError: true, + }, + { + name: "failure: ProcessWorkflowRunSync failed", + given: func() { + findResult := bson.D{{"workflow_id", workflowID}, {"project_id", projectID}, {"workflow_runs", []*dbOperationsWorkflow.ChaosWorkflowRun{{WorkflowRunID: workflowRunID}}}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + chaosWorkflowService.On("ProcessWorkflowRunSync", workflowID, &workflowRunID, mock.Anything, state).Return(errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + success, err := chaosWorkflowHandler.SyncWorkflowRun(ctx, projectID, workflowID, workflowRunID, state) + // then + if tc.isError { + assert.Error(t, err) + assert.False(t, success) + } else { + assert.NoError(t, err) + assert.True(t, success) + } + }) + } +} + +// TestChaosWorkflowHandler_QueryServerVersion is used to test the QueryServerVersion method +func TestChaosWorkflowHandler_QueryServerVersion(t *testing.T) { + // given + ctx := context.Background() + key, value := uuid.NewString(), uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := bson.D{{"key", key}, {"value", value}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.ServerConfigCollection, mock.Anything).Return(singleResult, nil).Once() + }, + }, + { + name: "failure: mongo operator cannot get workflow template", + given: func() { + singleResult := mongo.NewSingleResultFromDocument(nil, nil, nil) + mongoOperator.On("Get", mock.Anything, mongodb.ServerConfigCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + response, err := chaosWorkflowHandler.QueryServerVersion(ctx) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, key, response.Key) + assert.Equal(t, value, response.Value) + } + }) + } +} diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/service.go b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/service.go new file mode 100644 index 00000000000..d4bc03d6893 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/service.go @@ -0,0 +1,76 @@ +// Package mocks ... +package mocks + +import ( + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" + chaosWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow" + store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" + dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + dbSchemaWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + workflowDBOps "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" +) + +type ChaosWorkflowService struct { + mock.Mock +} + +// ProcessWorkflow mocks the ProcessWorkflow of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflow(workflow *model.ChaosWorkFlowRequest) (*model.ChaosWorkFlowRequest, *dbSchemaWorkflow.ChaosWorkflowType, error) { + args := c.Called(workflow) + return args.Get(0).(*model.ChaosWorkFlowRequest), args.Get(1).(*dbSchemaWorkflow.ChaosWorkflowType), args.Error(2) +} + +// ProcessWorkflowCreation mocks the ProcessWorkflowCreation of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflowCreation(input *model.ChaosWorkFlowRequest, username string, wfType *dbSchemaWorkflow.ChaosWorkflowType, r *store.StateData) error { + args := c.Called(input, username, wfType, r) + return args.Error(0) +} + +// ProcessWorkflowUpdate mocks the ProcessWorkflowUpdate of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflowUpdate(workflow *model.ChaosWorkFlowRequest, username string, wfType *dbSchemaWorkflow.ChaosWorkflowType, r *store.StateData) error { + args := c.Called(workflow, username, wfType, r) + return args.Error(0) +} + +// ProcessWorkflowDelete mocks the ProcessWorkflowDelete of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflowDelete(query bson.D, workflow workflowDBOps.ChaosWorkFlowRequest, username string, r *store.StateData) error { + args := c.Called(query, workflow, username, r) + return args.Error(0) +} + +// ProcessWorkflowRunDelete mocks the ProcessWorkflowRunDelete of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflowRunDelete(query bson.D, workflowRunID *string, workflow workflowDBOps.ChaosWorkFlowRequest, username string, r *store.StateData) error { + args := c.Called(query, workflowRunID, workflow, username, r) + return args.Error(0) +} + +// ProcessWorkflowRunSync mocks the ProcessWorkflowRunSync of chaos-workflow service +func (c *ChaosWorkflowService) ProcessWorkflowRunSync(workflowID string, workflowRunID *string, workflow workflowDBOps.ChaosWorkFlowRequest, r *store.StateData) error { + args := c.Called(workflowID, workflowRunID, workflow, r) + return args.Error(0) +} + +// SendWorkflowEvent mocks the SendWorkflowEvent of chaos-workflow service +func (c *ChaosWorkflowService) SendWorkflowEvent(wfRun model.WorkflowRun, r *store.StateData) { + c.Called(wfRun, r) +} + +// ProcessCompletedWorkflowRun mocks the ProcessCompletedWorkflowRun of chaos-workflow service +func (c *ChaosWorkflowService) ProcessCompletedWorkflowRun(execData chaosWorkflow.ExecutionData, wfID string) (chaosWorkflow.WorkflowRunMetrics, error) { + args := c.Called(execData, wfID) + return args.Get(0).(chaosWorkflow.WorkflowRunMetrics), args.Error(1) +} + +// GetWorkflow mocks the GetWorkflow of chaos-workflow service +func (c *ChaosWorkflowService) GetWorkflow(query bson.D) (dbOperationsWorkflow.ChaosWorkFlowRequest, error) { + args := c.Called(query) + return args.Get(0).(dbOperationsWorkflow.ChaosWorkFlowRequest), args.Error(1) +} + +// GetWorkflows mocks the GetWorkflows of chaos-workflow service +func (c *ChaosWorkflowService) GetWorkflows(query bson.D) ([]dbOperationsWorkflow.ChaosWorkFlowRequest, error) { + args := c.Called(query) + return args.Get(0).([]dbOperationsWorkflow.ChaosWorkFlowRequest), args.Error(1) +} diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go new file mode 100644 index 00000000000..a50962efa3e --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go @@ -0,0 +1,88 @@ +package chaos_workflow_test + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/gin-gonic/gin" + chaosWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow" + dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" + mocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model" + dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +var ( + mongoOperator = new(mocks.MongoOperator) + mockChaosWorkflowOperator = dbOperationsWorkflow.NewChaosWorkflowOperator(mongoOperator) + mockClusterOperator = dbSchemaCluster.NewClusterOperator(mongoOperator) + mockService = chaosWorkflow.NewService(mockChaosWorkflowOperator, mockClusterOperator) +) + +// TestMain is the entry point for testing +func TestMain(m *testing.M) { + gin.SetMode(gin.TestMode) + log.SetOutput(ioutil.Discard) + os.Exit(m.Run()) +} + +// TestNewService tests the NewService function +func TestNewService(t *testing.T) { + // then + t.Run("success", func(t *testing.T) { + assert.Equal(t, mockService, chaosWorkflow.NewService(mockChaosWorkflowOperator, mockClusterOperator)) + assert.Equal(t, mockService, chaosWorkflow.NewService(mockChaosWorkflowOperator, mockClusterOperator)) + }) +} + +// TestChaosWorkflowService_ProcessWorkflow is used to test the ProcessWorkflow method +func TestChaosWorkflowService_ProcessWorkflow(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessWorkflowCreation is used to test the ProcessWorkflowCreation method +func TestChaosWorkflowService_ProcessWorkflowCreation(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessWorkflowUpdate is used to test the ProcessWorkflowUpdate method +func TestChaosWorkflowService_ProcessWorkflowUpdate(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessWorkflowDelete is used to test the ProcessWorkflowDelete method +func TestChaosWorkflowService_ProcessWorkflowDelete(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessWorkflowRunDelete is used to test the ProcessWorkflowRunDelete method +func TestChaosWorkflowService_ProcessWorkflowRunDelete(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessWorkflowRunSync is used to test the ProcessWorkflowRunSync method +func TestChaosWorkflowService_ProcessWorkflowRunSync(t *testing.T) { + +} + +// TestChaosWorkflowService_SendWorkflowEvent is used to test the SendWorkflowEvent method +func TestChaosWorkflowService_SendWorkflowEvent(t *testing.T) { + +} + +// TestChaosWorkflowService_ProcessCompletedWorkflowRun is used to test the ProcessCompletedWorkflowRun method +func TestChaosWorkflowService_ProcessCompletedWorkflowRun(t *testing.T) { + +} + +// TestChaosWorkflowService_GetWorkflow is used to test the GetWorkflow method +func TestChaosWorkflowService_GetWorkflow(t *testing.T) { + +} + +// TestChaosWorkflowService_GetWorkflows is used to test the GetWorkflows method +func TestChaosWorkflowService_GetWorkflows(t *testing.T) { + +} diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/util_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/util_test.go new file mode 100644 index 00000000000..740b1b01675 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/util_test.go @@ -0,0 +1,62 @@ +package chaos_workflow_test + +import ( + "testing" + "time" + + "github.com/google/uuid" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" + chaosWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow" + dataStore "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" + "github.com/stretchr/testify/assert" +) + +// TestSendWorkflowToSubscriber +func TestSendWorkflowToSubscriber(t *testing.T) { + // given + state := dataStore.NewStore() + username := "username" + requestType := "type" + testcases := []struct { + workflow *model.ChaosWorkFlowRequest + }{ + { + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + }, + }, + { + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + }, + }, + { + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + }, + }, + } + for _, tc := range testcases { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, tc.workflow.ClusterID) }) + state.ConnectedCluster[tc.workflow.ClusterID] = action + // when + chaosWorkflow.SendWorkflowToSubscriber(tc.workflow, &username, nil, requestType, state) + // then + select { + case result := <-action: + assert.Equal(t, tc.workflow.ProjectID, result.ProjectID) + assert.Equal(t, requestType, result.Action.RequestType) + assert.Equal(t, username, *result.Action.Username) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + } +} diff --git a/litmus-portal/graphql-server/pkg/cluster/model/mocks/service.go b/litmus-portal/graphql-server/pkg/cluster/model/mocks/service.go new file mode 100644 index 00000000000..36193d78a53 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/cluster/model/mocks/service.go @@ -0,0 +1,88 @@ +// Package mocks ... +package mocks + +import ( + "context" + + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" + store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" + dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" +) + +// ClusterService is a mock type for model.ClusterService +type ClusterService struct { + mock.Mock +} + +// RegisterCluster mocks the RegisterCluster of ClusterService +func (c *ClusterService) RegisterCluster(request model.RegisterClusterRequest) (*model.RegisterClusterResponse, error) { + args := c.Called(request) + return args.Get(0).(*model.RegisterClusterResponse), args.Error(1) +} + +// UpdateCluster mocks the UpdateCluster of ClusterService +func (c *ClusterService) UpdateCluster(query bson.D, update bson.D) error { + args := c.Called(query, update) + return args.Error(0) +} + +// ConfirmClusterRegistration mocks the ConfirmClusterRegistration of ClusterService +func (c *ClusterService) ConfirmClusterRegistration(request model.ClusterIdentity, r store.StateData) (*model.ConfirmClusterRegistrationResponse, error) { + args := c.Called(request, r) + return args.Get(0).(*model.ConfirmClusterRegistrationResponse), args.Error(1) +} + +// NewClusterEvent mocks the NewClusterEvent of ClusterService +func (c *ClusterService) NewClusterEvent(request model.NewClusterEventRequest, r store.StateData) (string, error) { + args := c.Called(request, r) + return args.String(0), args.Error(1) +} + +// DeleteClusters mocks the DeleteClusters of ClusterService +func (c *ClusterService) DeleteClusters(ctx context.Context, projectID string, clusterIds []*string, r store.StateData) (string, error) { + args := c.Called(ctx, projectID, clusterIds, r) + return args.String(0), args.Error(1) +} + +// ListClusters mocks the ListClusters of ClusterService +func (c *ClusterService) ListClusters(projectID string, clusterType *string) ([]*model.Cluster, error) { + args := c.Called(projectID, clusterType) + return args.Get(0).([]*model.Cluster), args.Error(1) +} + +// GetAgentDetails mocks the GetAgentDetails of ClusterService +func (c *ClusterService) GetAgentDetails(ctx context.Context, clusterID string, projectID string) (*model.Cluster, error) { + args := c.Called(ctx, clusterID, projectID) + return args.Get(0).(*model.Cluster), args.Error(1) +} + +// GetManifestWithClusterID mocks the GetManifestWithClusterID of ClusterService +func (c *ClusterService) GetManifestWithClusterID(clusterID string, accessKey string) ([]byte, error) { + args := c.Called(clusterID, accessKey) + return args.Get(0).([]byte), args.Error(1) +} + +// SendClusterEvent mocks the SendClusterEvent of ClusterService +func (c *ClusterService) SendClusterEvent(eventType, eventName, description string, cluster model.Cluster, r store.StateData) { + c.Called(eventType, eventName, description, cluster, r) +} + +// VerifyCluster mocks the VerifyCluster of ClusterService +func (c *ClusterService) VerifyCluster(identity model.ClusterIdentity) (*dbSchemaCluster.Cluster, error) { + args := c.Called(identity) + return args.Get(0).(*dbSchemaCluster.Cluster), args.Error(1) +} + +// GetManifest mocks the GetManifest of ClusterService +func (c *ClusterService) GetManifest(token string) ([]byte, int, error) { + args := c.Called(token) + return args.Get(0).([]byte), args.Int(1), args.Error(2) +} + +// GetCluster mocks the GetCluster of ClusterService +func (c *ClusterService) GetCluster(clusterID string) (dbSchemaCluster.Cluster, error) { + args := c.Called(clusterID) + return args.Get(0).(dbSchemaCluster.Cluster), args.Error(1) +} diff --git a/litmus-portal/graphql-server/pkg/cluster/service.go b/litmus-portal/graphql-server/pkg/cluster/service.go index c8eee4f3a8e..6d3f9f66367 100644 --- a/litmus-portal/graphql-server/pkg/cluster/service.go +++ b/litmus-portal/graphql-server/pkg/cluster/service.go @@ -437,6 +437,7 @@ func (c *clusterService) GetManifest(token string) ([]byte, int, error) { } } +// GetCluster returns cluster details for a given clusterID func (c *clusterService) GetCluster(clusterID string) (dbSchemaCluster.Cluster, error) { return c.clusterOperator.GetCluster(clusterID) } diff --git a/litmus-portal/graphql-server/pkg/data-store/store.go b/litmus-portal/graphql-server/pkg/data-store/store.go index ebbf1ee38e5..a92f86220a6 100644 --- a/litmus-portal/graphql-server/pkg/data-store/store.go +++ b/litmus-portal/graphql-server/pkg/data-store/store.go @@ -6,7 +6,7 @@ import ( "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" ) -// Application state, contains channels and mutexes used for subscriptions +// StateData has an application state, contains channels and mutexes used for subscriptions type StateData struct { ClusterEventPublish map[string][]chan *model.ClusterEventResponse ConnectedCluster map[string]chan *model.ClusterActionResponse diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go b/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go new file mode 100644 index 00000000000..2e08ec1e774 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go @@ -0,0 +1,87 @@ +package mocks + +import ( + "context" + + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// MongoOperator is an autogenerated mock type for the MongoOperator type +type MongoOperator struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, collectionType, document +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 { + 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) { + 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) (*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) { + 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) { + 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) { + 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) { + 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) { + 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) { + args := m.Called(ctx, collectionType, pipeline, opts) + return args.Get(0).(*mongo.Cursor), 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) { + 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) { + args := m.Called(ctx, mclient) + return args.Get(0).([]string), args.Error(1) +} diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate/operations.go b/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate/operations.go index f3cee041398..32c5af09541 100644 --- a/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate/operations.go +++ b/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate/operations.go @@ -5,7 +5,6 @@ import ( "errors" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" - log "github.com/sirupsen/logrus" "go.mongodb.org/mongo-driver/bson" ) @@ -25,7 +24,7 @@ func NewWorkflowTemplateOperator(mongodbOperator mongodb.MongoOperator) *Operato func (w *Operator) CreateWorkflowTemplate(ctx context.Context, template *WorkflowTemplate) error { err := w.operator.Create(ctx, mongodb.WorkflowTemplateCollection, template) if err != nil { - log.Error("error while creating template: ", err) + return err } return nil } @@ -35,13 +34,12 @@ func (w *Operator) GetTemplatesByProjectID(ctx context.Context, projectID string query := bson.D{{"project_id", projectID}, {"is_removed", false}} results, err := w.operator.List(ctx, mongodb.WorkflowTemplateCollection, query) if err != nil { - log.Error("error getting template: ", err) + return []WorkflowTemplate{}, err } var templates []WorkflowTemplate err = results.All(ctx, &templates) if err != nil { - log.Error(err) return []WorkflowTemplate{}, err } return templates, nil diff --git a/litmus-portal/graphql-server/pkg/gitops/model/mocks/service.go b/litmus-portal/graphql-server/pkg/gitops/model/mocks/service.go new file mode 100644 index 00000000000..b6e89133d07 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/gitops/model/mocks/service.go @@ -0,0 +1,62 @@ +// Package mocks ... +package mocks + +import ( + "context" + + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" + dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" + "github.com/stretchr/testify/mock" +) + +// GitOpsService is an autogenerated mock type for the GitOpsService type +type GitOpsService struct { + mock.Mock +} + +// GitOpsNotificationHandler provides a mock function with given fields: ctx, cluster, workflowID +func (g *GitOpsService) GitOpsNotificationHandler(ctx context.Context, cluster *dbSchemaCluster.Cluster, workflowID string) (string, error) { + args := g.Called(ctx, cluster, workflowID) + return args.String(0), args.Error(1) +} + +// EnableGitOpsHandler provides a mock function with given fields: ctx, config +func (g *GitOpsService) EnableGitOpsHandler(ctx context.Context, config model.GitConfig) (bool, error) { + args := g.Called(ctx, config) + return args.Bool(0), args.Error(1) +} + +// DisableGitOpsHandler provides a mock function with given fields: ctx, projectID +func (g *GitOpsService) DisableGitOpsHandler(ctx context.Context, projectID string) (bool, error) { + args := g.Called(ctx, projectID) + return args.Bool(0), args.Error(1) +} + +// UpdateGitOpsDetailsHandler provides a mock function with given fields: ctx, config +func (g *GitOpsService) UpdateGitOpsDetailsHandler(ctx context.Context, config model.GitConfig) (bool, error) { + args := g.Called(ctx, config) + return args.Bool(0), args.Error(1) +} + +// GetGitOpsDetails provides a mock function with given fields: ctx, projectID +func (g *GitOpsService) GetGitOpsDetails(ctx context.Context, projectID string) (*model.GitConfigResponse, error) { + args := g.Called(ctx, projectID) + return args.Get(0).(*model.GitConfigResponse), args.Error(1) +} + +// UpsertWorkflowToGit provides a mock function with given fields: ctx, workflow +func (g *GitOpsService) UpsertWorkflowToGit(ctx context.Context, workflow *model.ChaosWorkFlowRequest) error { + args := g.Called(ctx, workflow) + return args.Error(0) +} + +// DeleteWorkflowFromGit provides a mock function with given fields: ctx, workflow +func (g *GitOpsService) DeleteWorkflowFromGit(ctx context.Context, workflow *model.ChaosWorkFlowRequest) error { + args := g.Called(ctx, workflow) + return args.Error(0) +} + +// GitOpsSyncHandler provides a mock function with given fields: singleRun +func (g *GitOpsService) GitOpsSyncHandler(singleRun bool) { + g.Called(singleRun) +} From b2f0ebf0fb0b7c620bccbdd772586190e80bbde4 Mon Sep 17 00:00:00 2001 From: namkyu1999 Date: Wed, 3 May 2023 02:46:33 +0900 Subject: [PATCH 2/3] feat: add unit tests to chaosWorkflowService Signed-off-by: namkyu1999 --- .../chaos-workflow/model/mocks/engine.yaml | 34 + .../chaos-workflow/model/mocks/schedule.yaml | 33 + .../chaos-workflow/model/mocks/workflow.yaml | 164 +++++ .../model/mocks/workflow_cron.yaml | 177 +++++ .../model/mocks/wrong_type.yaml | 4 + .../pkg/chaos-workflow/service.go | 4 +- .../pkg/chaos-workflow/service_test.go | 623 +++++++++++++++++- 7 files changed, 1027 insertions(+), 12 deletions(-) create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/engine.yaml create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/schedule.yaml create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow.yaml create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow_cron.yaml create mode 100644 litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/wrong_type.yaml diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/engine.yaml b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/engine.yaml new file mode 100644 index 00000000000..78f67a13e3b --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/engine.yaml @@ -0,0 +1,34 @@ +apiVersion: litmuschaos.io/v1alpha1 +kind: ChaosEngine +metadata: + generateName: nginx-chaos + name: nginx-chaos + namespace: default +spec: + appinfo: + appns: 'default' + applabel: 'app=nginx' + appkind: 'deployment' + # It can be active/stop + engineState: 'active' + chaosServiceAccount: pod-delete-sa + experiments: + - name: pod-delete + spec: + components: + env: + # set chaos duration (in sec) as desired + - name: TOTAL_CHAOS_DURATION + value: '30' + + # set chaos interval (in sec) as desired + - name: CHAOS_INTERVAL + value: '10' + + # pod failures without '--force' & default terminationGracePeriodSeconds + - name: FORCE + value: 'false' + + ## percentage of total pods to target + - name: PODS_AFFECTED_PERC + value: '' \ No newline at end of file diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/schedule.yaml b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/schedule.yaml new file mode 100644 index 00000000000..1d089f30b0b --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/schedule.yaml @@ -0,0 +1,33 @@ +apiVersion: litmuschaos.io/v1alpha1 +kind: ChaosSchedule +metadata: + name: schedule-nginx + generateName: schedule-nginx +spec: + schedule: + now: true + engineTemplateSpec: + engineState: 'active' + appinfo: + appns: 'default' + applabel: 'app=nginx' + appkind: 'deployment' + annotationCheck: 'true' + chaosServiceAccount: pod-delete-sa + jobCleanUpPolicy: 'delete' + experiments: + - name: pod-delete + spec: + components: + env: + # set chaos duration (in sec) as desired + - name: TOTAL_CHAOS_DURATION + value: '30' + + # set chaos interval (in sec) as desired + - name: CHAOS_INTERVAL + value: '10' + + # pod failures without '--force' & default terminationGracePeriodSeconds + - name: FORCE + value: 'false' \ No newline at end of file diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow.yaml b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow.yaml new file mode 100644 index 00000000000..994bc5891ba --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow.yaml @@ -0,0 +1,164 @@ +kind: Workflow +apiVersion: argoproj.io/v1alpha1 +metadata: + name: test-podtato-head-1682669740 + namespace: litmus + creationTimestamp: null + labels: + subject: podtato-head_litmus + workflows.argoproj.io/controller-instanceid: 83c20016-f7ec-4c5d-bb84-3a067a7010c2 +spec: + templates: + - name: argowf-chaos + inputs: {} + outputs: {} + metadata: {} + steps: + - - name: install-application + template: install-application + arguments: {} + - - name: install-chaos-experiments + template: install-chaos-experiments + arguments: {} + - - name: pod-delete + template: pod-delete + arguments: {} + - - name: revert-chaos + template: revert-chaos + arguments: {} + - name: delete-application + template: delete-application + arguments: {} + - name: install-application + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/litmus-app-deployer:latest + args: + - -namespace={{workflow.parameters.adminModeNamespace}} + - -typeName=resilient + - -operation=apply + - -timeout=400 + - -app=podtato-head + - -scope=namespace + resources: {} + - name: install-chaos-experiments + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/k8s:latest + command: + - sh + - -c + args: + - kubectl apply -f + https://hub.litmuschaos.io/api/chaos/3.0.0-beta3?file=charts/generic/experiments.yaml + -n {{workflow.parameters.adminModeNamespace}} ; sleep 30 + resources: {} + - name: pod-delete + inputs: + artifacts: + - name: pod-delete + path: /tmp/chaosengine.yaml + raw: + data: > + apiVersion: litmuschaos.io/v1alpha1 + + kind: ChaosEngine + + metadata: + namespace: "{{workflow.parameters.adminModeNamespace}}" + labels: + workflow_run_id: "{{workflow.uid}}" + generateName: podtato-main-pod-delete-chaos + spec: + appinfo: + appns: "{{workflow.parameters.adminModeNamespace}}" + applabel: name=podtato-main + appkind: deployment + engineState: active + chaosServiceAccount: litmus-admin + jobCleanUpPolicy: retain + components: + runner: + imagePullPolicy: Always + experiments: + - name: pod-delete + spec: + probe: + - name: check-podtato-main-access-url + type: httpProbe + httpProbe/inputs: + url: http://podtato-main.{{workflow.parameters.adminModeNamespace}}.svc.cluster.local:9000 + insecureSkipVerify: false + method: + get: + criteria: == + responseCode: "200" + mode: Continuous + runProperties: + probeTimeout: 1 + interval: 1 + retry: 1 + components: + env: + - name: TOTAL_CHAOS_DURATION + value: "30" + - name: CHAOS_INTERVAL + value: "10" + - name: FORCE + value: "false" + outputs: {} + metadata: + labels: + weight: "10" + container: + name: "" + image: litmuschaos/litmus-checker:latest + args: + - -file=/tmp/chaosengine.yaml + - -saveName=/tmp/engine-name + resources: {} + - name: delete-application + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/litmus-app-deployer:latest + args: + - -namespace={{workflow.parameters.adminModeNamespace}} + - -typeName=resilient + - -operation=delete + - -app=podtato-head + resources: {} + - name: revert-chaos + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/k8s:latest + command: + - sh + - -c + args: + - kubectl delete chaosengine -l workflow_run_id={{workflow.uid}} -n + {{workflow.parameters.adminModeNamespace}} + resources: {} + entrypoint: argowf-chaos + arguments: + parameters: + - name: adminModeNamespace + value: litmus + serviceAccountName: argo-chaos + securityContext: + runAsUser: 1000 + runAsNonRoot: true +status: + ? startedAt + ? finishedAt diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow_cron.yaml b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow_cron.yaml new file mode 100644 index 00000000000..d5797bb818c --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/workflow_cron.yaml @@ -0,0 +1,177 @@ +kind: CronWorkflow +apiVersion: argoproj.io/v1alpha1 +metadata: + name: test-podtato-head-1682669740 + namespace: litmus + creationTimestamp: null + labels: + subject: podtato-head_litmus + workflows.argoproj.io/controller-instanceid: 83c20016-f7ec-4c5d-bb84-3a067a7010c2 +spec: + workflowSpec: + templates: + - name: argowf-chaos + inputs: {} + outputs: {} + metadata: {} + steps: + - - name: install-application + template: install-application + arguments: {} + - - name: install-chaos-experiments + template: install-chaos-experiments + arguments: {} + - - name: pod-delete + template: pod-delete + arguments: {} + - - name: revert-chaos + template: revert-chaos + arguments: {} + - name: delete-application + template: delete-application + arguments: {} + - name: install-application + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/litmus-app-deployer:latest + args: + - -namespace={{workflow.parameters.adminModeNamespace}} + - -typeName=resilient + - -operation=apply + - -timeout=400 + - -app=podtato-head + - -scope=namespace + resources: {} + - name: install-chaos-experiments + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/k8s:latest + command: + - sh + - -c + args: + - kubectl apply -f + https://hub.litmuschaos.io/api/chaos/3.0.0-beta3?file=charts/generic/experiments.yaml + -n {{workflow.parameters.adminModeNamespace}} ; sleep 30 + resources: {} + - name: pod-delete + inputs: + artifacts: + - name: pod-delete + path: /tmp/chaosengine.yaml + raw: + data: > + apiVersion: litmuschaos.io/v1alpha1 + + kind: ChaosEngine + + metadata: + namespace: "{{workflow.parameters.adminModeNamespace}}" + labels: + workflow_run_id: "{{workflow.uid}}" + workflow_name: podtato-head-1683012309 + generateName: podtato-main-pod-delete-chaos + spec: + appinfo: + appns: "{{workflow.parameters.adminModeNamespace}}" + applabel: name=podtato-main + appkind: deployment + engineState: active + chaosServiceAccount: litmus-admin + jobCleanUpPolicy: retain + components: + runner: + imagePullPolicy: Always + experiments: + - name: pod-delete + spec: + probe: + - name: check-podtato-main-access-url + type: httpProbe + httpProbe/inputs: + url: http://podtato-main.{{workflow.parameters.adminModeNamespace}}.svc.cluster.local:9000 + insecureSkipVerify: false + method: + get: + criteria: == + responseCode: "200" + mode: Continuous + runProperties: + probeTimeout: 1 + interval: 1 + retry: 1 + components: + env: + - name: TOTAL_CHAOS_DURATION + value: "30" + - name: CHAOS_INTERVAL + value: "10" + - name: FORCE + value: "false" + outputs: {} + metadata: + labels: + weight: "10" + container: + name: "" + image: litmuschaos/litmus-checker:latest + args: + - -file=/tmp/chaosengine.yaml + - -saveName=/tmp/engine-name + resources: {} + - name: delete-application + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/litmus-app-deployer:latest + args: + - -namespace={{workflow.parameters.adminModeNamespace}} + - -typeName=resilient + - -operation=delete + - -app=podtato-head + resources: {} + - name: revert-chaos + inputs: {} + outputs: {} + metadata: {} + container: + name: "" + image: litmuschaos/k8s:latest + command: + - sh + - -c + args: + - kubectl delete chaosengine -l workflow_run_id={{workflow.uid}} -n + {{workflow.parameters.adminModeNamespace}} + resources: {} + entrypoint: argowf-chaos + arguments: + parameters: + - name: adminModeNamespace + value: litmus + serviceAccountName: argo-chaos + securityContext: + runAsUser: 1000 + runAsNonRoot: true + schedule: 28 16 * * 0-6 + concurrencyPolicy: Forbid + startingDeadlineSeconds: 0 + timezone: Asia/Seoul + workflowMetadata: + creationTimestamp: null + labels: + cluster_id: 83c20016-f7ec-4c5d-bb84-3a067a7010c2 + workflow_id: eb7eb07f-5a60-470a-89e8-7b98ab7e6b11 + workflows.argoproj.io/controller-instanceid: 83c20016-f7ec-4c5d-bb84-3a067a7010c2 +status: + ? active + ? lastScheduledTime + ? conditions diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/wrong_type.yaml b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/wrong_type.yaml new file mode 100644 index 00000000000..b1dab642dc0 --- /dev/null +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/model/mocks/wrong_type.yaml @@ -0,0 +1,4 @@ +apiVersion: litmuschaos.io/v1alpha1 +kind: Unknown +metadata: + name: wrong \ No newline at end of file diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/service.go b/litmus-portal/graphql-server/pkg/chaos-workflow/service.go index 2d5dc914fbf..d6a6d8aa028 100644 --- a/litmus-portal/graphql-server/pkg/chaos-workflow/service.go +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/service.go @@ -110,7 +110,7 @@ func (c *chaosWorkflowService) ProcessWorkflow(workflow *model.ChaosWorkFlowRequ case "chaosengine": { wfType = dbSchemaWorkflow.ChaosEngine - err = processChaosengineManifest(workflow, weights) + err = processChaosEngineManifest(workflow, weights) if err != nil { return nil, nil, err } @@ -529,7 +529,7 @@ func processCronWorkflowManifest(workflow *model.ChaosWorkFlowRequest, weights m return nil } -func processChaosengineManifest(workflow *model.ChaosWorkFlowRequest, weights map[string]int) error { +func processChaosEngineManifest(workflow *model.ChaosWorkFlowRequest, weights map[string]int) error { var ( newWeights []*model.WeightagesInput workflowManifest chaosTypes.ChaosEngine diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go index a50962efa3e..be0c36f1dcf 100644 --- a/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go @@ -1,17 +1,40 @@ package chaos_workflow_test import ( + "errors" "io/ioutil" "os" "testing" + "time" + "github.com/ghodss/yaml" "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model" chaosWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/chaos-workflow" + store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" mocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model" dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" + workflowDBOps "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +const ( + workflowName = "test-podtato-head-1682669740" + wrongTypeName = "wrong" + chaosEngineName = "nginx-chaos" + chaosScheduleName = "schedule-nginx" + workflowManifestPath = "./model/mocks/workflow.yaml" + cronWorkflowManifestPath = "./model/mocks/workflow_cron.yaml" + wrongTypeManifestPath = "./model/mocks/wrong_type.yaml" + chaosEngineManifestPath = "./model/mocks/engine.yaml" + chaosScheduleManifestPath = "./model/mocks/schedule.yaml" ) var ( @@ -37,52 +60,632 @@ func TestNewService(t *testing.T) { }) } +func loadYAMLData(path string) (string, error) { + YAMLData, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + jsonData, err := yaml.YAMLToJSON(YAMLData) + if err != nil { + return "", err + } + return string(jsonData), nil +} + // TestChaosWorkflowService_ProcessWorkflow is used to test the ProcessWorkflow method func TestChaosWorkflowService_ProcessWorkflow(t *testing.T) { - + // given + projectID := uuid.NewString() + singleResult := mongo.NewSingleResultFromDocument(bson.D{{"project_id", projectID}}, nil, nil) + testcases := []struct { + name string + workflow *model.ChaosWorkFlowRequest + isError bool + given func(workflow *model.ChaosWorkFlowRequest) + }{ + { + name: "success: object kind is workflow", + workflow: &model.ChaosWorkFlowRequest{ + WorkflowName: workflowName, + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(workflowManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + }, + { + name: "success: object kind is cron workflow", + workflow: &model.ChaosWorkFlowRequest{ + WorkflowName: workflowName, + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(cronWorkflowManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + }, + { + name: "success: object kind is chaos engine", + workflow: &model.ChaosWorkFlowRequest{ + WorkflowName: chaosEngineName, + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(chaosEngineManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + }, + { + name: "success: object kind is chaos schedule", + workflow: &model.ChaosWorkFlowRequest{ + WorkflowName: chaosScheduleName, + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(chaosScheduleManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + }, + { + name: "failure: object kind is unknown", + workflow: &model.ChaosWorkFlowRequest{ + WorkflowName: wrongTypeName, + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(wrongTypeManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + isError: true, + }, + { + name: "failure: cannot get cluster", + workflow: &model.ChaosWorkFlowRequest{}, + given: func(workflow *model.ChaosWorkFlowRequest) { + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: cluster's projectID is not equal to workflow's projectID", + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return( + mongo.NewSingleResultFromDocument(bson.D{{"project_id", uuid.NewString()}}, nil, nil), nil, + ).Once() + }, + isError: true, + }, + { + name: "failure: cannot unmarshal object metadata", + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: projectID, + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + workflow.WorkflowManifest = "invalid json" + }, + isError: true, + }, + { + name: "failure: workflow name not matched with object metadata name", + workflow: &model.ChaosWorkFlowRequest{ + ProjectID: projectID, + WorkflowName: uuid.NewString(), + }, + given: func(workflow *model.ChaosWorkFlowRequest) { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + manifest, err := loadYAMLData(workflowManifestPath) + if err != nil { + t.FailNow() + } + workflow.WorkflowManifest = manifest + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given(tc.workflow) + // when + _, _, err := mockService.ProcessWorkflow(tc.workflow) + if tc.isError { + // then + assert.Error(t, err) + } else { + // then + assert.NoError(t, err) + } + }) + } } // TestChaosWorkflowService_ProcessWorkflowCreation is used to test the ProcessWorkflowCreation method func TestChaosWorkflowService_ProcessWorkflowCreation(t *testing.T) { - + // given + state := store.NewStore() + workflowID, username := uuid.NewString(), uuid.NewString() + workflow := &model.ChaosWorkFlowRequest{ + WorkflowID: &workflowID, + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + } + findResult := bson.D{{"project_id", workflow.ProjectID}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + wfType := dbOperationsWorkflow.Workflow + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success: workflow created successfully", + given: func() { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + mongoOperator.On("Create", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(nil).Once() + }, + }, + { + name: "failure: cannot get cluster", + given: func() { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: cannot insert workflow", + given: func() { + // given + mongoOperator.On("Get", mock.Anything, mongodb.ClusterCollection, mock.Anything).Return(singleResult, nil).Once() + mongoOperator.On("Create", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + if tc.isError { + // when + err := mockService.ProcessWorkflowCreation(workflow, username, &wfType, state) + // then + assert.Error(t, err) + } else { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, workflow.ClusterID) }) + state.ConnectedCluster[workflow.ClusterID] = action + // when + err := mockService.ProcessWorkflowCreation(workflow, username, &wfType, state) + // then + assert.NoError(t, err) + select { + case result := <-action: + assert.Equal(t, workflow.ProjectID, result.ProjectID) + assert.Equal(t, username, *result.Action.Username) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + } + }) + } } // TestChaosWorkflowService_ProcessWorkflowUpdate is used to test the ProcessWorkflowUpdate method func TestChaosWorkflowService_ProcessWorkflowUpdate(t *testing.T) { - + // given + state := store.NewStore() + workflowID, username := uuid.NewString(), uuid.NewString() + wfType := dbOperationsWorkflow.Workflow + workflow := &model.ChaosWorkFlowRequest{ + WorkflowID: &workflowID, + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + } + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success: workflow updated successfully", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + }, + }, + { + name: "failure: cannot update workflow", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + if tc.isError { + // when + err := mockService.ProcessWorkflowUpdate(workflow, username, &wfType, state) + // then + assert.Error(t, err) + } else { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, workflow.ClusterID) }) + state.ConnectedCluster[workflow.ClusterID] = action + // when + err := mockService.ProcessWorkflowUpdate(workflow, username, &wfType, state) + // then + assert.NoError(t, err) + select { + case result := <-action: + assert.Equal(t, workflow.ProjectID, result.ProjectID) + assert.Equal(t, username, *result.Action.Username) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + } + }) + } } // TestChaosWorkflowService_ProcessWorkflowDelete is used to test the ProcessWorkflowDelete method func TestChaosWorkflowService_ProcessWorkflowDelete(t *testing.T) { - + // given + state := store.NewStore() + username := uuid.NewString() + workflow := workflowDBOps.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + } + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success: workflow deleted successfully", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + }, + }, + { + name: "failure: cannot delete workflow", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + if tc.isError { + // when + err := mockService.ProcessWorkflowDelete(bson.D{}, workflow, username, state) + // then + assert.Error(t, err) + } else { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, workflow.ClusterID) }) + state.ConnectedCluster[workflow.ClusterID] = action + // when + err := mockService.ProcessWorkflowDelete(bson.D{}, workflow, username, state) + // then + assert.NoError(t, err) + select { + case result := <-action: + assert.Equal(t, workflow.ProjectID, result.ProjectID) + assert.Equal(t, username, *result.Action.Username) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + } + }) + } } // TestChaosWorkflowService_ProcessWorkflowRunDelete is used to test the ProcessWorkflowRunDelete method func TestChaosWorkflowService_ProcessWorkflowRunDelete(t *testing.T) { - + // given + state := store.NewStore() + username, workflowRunID := uuid.NewString(), uuid.NewString() + workflow := workflowDBOps.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + } + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success: workflowRun deleted successfully", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, nil).Once() + }, + }, + { + name: "failure: cannot delete workflowRun", + given: func() { + // given + mongoOperator.On("Update", mock.Anything, mongodb.WorkflowCollection, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{MatchedCount: 1}, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + if tc.isError { + // when + err := mockService.ProcessWorkflowRunDelete(bson.D{}, &workflowRunID, workflow, username, state) + // then + assert.Error(t, err) + } else { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, workflow.ClusterID) }) + state.ConnectedCluster[workflow.ClusterID] = action + // when + err := mockService.ProcessWorkflowRunDelete(bson.D{}, &workflowRunID, workflow, username, state) + // then + assert.NoError(t, err) + select { + case result := <-action: + assert.Equal(t, workflow.ProjectID, result.ProjectID) + assert.Equal(t, username, *result.Action.Username) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + } + }) + } } // TestChaosWorkflowService_ProcessWorkflowRunSync is used to test the ProcessWorkflowRunSync method func TestChaosWorkflowService_ProcessWorkflowRunSync(t *testing.T) { - + // given + state := store.NewStore() + workflowID, workflowRunID := uuid.NewString(), uuid.NewString() + workflow := workflowDBOps.ChaosWorkFlowRequest{ + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + WorkflowManifest: uuid.NewString(), + } + t.Run("success: workflowRun deleted successfully", func(t *testing.T) { + // given + action := make(chan *model.ClusterActionResponse, 1) + t.Cleanup(func() { delete(state.ConnectedCluster, workflow.ClusterID) }) + state.ConnectedCluster[workflow.ClusterID] = action + // when + err := mockService.ProcessWorkflowRunSync(workflowID, &workflowRunID, workflow, state) + // then + assert.NoError(t, err) + select { + case result := <-action: + assert.Equal(t, workflow.ProjectID, result.ProjectID) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + }) } // TestChaosWorkflowService_SendWorkflowEvent is used to test the SendWorkflowEvent method func TestChaosWorkflowService_SendWorkflowEvent(t *testing.T) { - + // given + state := store.NewStore() + workflowRun := model.WorkflowRun{ + WorkflowID: uuid.NewString(), + ProjectID: uuid.NewString(), + ClusterID: uuid.NewString(), + } + t.Run("success: SendWorkflowEvent executed successfully", func(t *testing.T) { + // given + action := make(chan *model.WorkflowRun, 1) + t.Cleanup(func() { close(state.WorkflowEventPublish[workflowRun.ProjectID][0]) }) + state.WorkflowEventPublish[workflowRun.ProjectID] = append(state.WorkflowEventPublish[workflowRun.ProjectID], action) + // when + mockService.SendWorkflowEvent(workflowRun, state) + // then + select { + case result := <-action: + assert.Equal(t, workflowRun.ProjectID, result.ProjectID) + case <-time.After(5 * time.Second): + t.Errorf("timeout") + } + }) } // TestChaosWorkflowService_ProcessCompletedWorkflowRun is used to test the ProcessCompletedWorkflowRun method func TestChaosWorkflowService_ProcessCompletedWorkflowRun(t *testing.T) { - + // given + executionData := chaosWorkflow.ExecutionData{ + Nodes: map[string]chaosWorkflow.Node{ + "node1": { + ChaosExp: &chaosWorkflow.ChaosData{ + EngineName: uuid.NewString(), + ExperimentVerdict: "Pass", + }, + Type: "ChaosEngine", + }, + }, + } + workflowID := uuid.NewString() + testcases := []struct { + name string + given func() + isError bool + }{ + { + name: "success", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}, {"weightages", []*model.WeightagesInput{{ExperimentName: uuid.NewString(), Weightage: 10}}}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "failure: cannot find workflowRun", + given: func() { + cursor, _ := mongo.NewCursorFromDocuments(nil, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + { + name: "failure: couldn't find the unique workflow", + given: func() { + findResult := []interface{}{bson.D{{"workflow_id", workflowID}}, bson.D{{"workflow_id", uuid.NewString()}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + if tc.isError { + // when + _, err := mockService.ProcessCompletedWorkflowRun(executionData, workflowID) + // then + assert.Error(t, err) + } else { + // when + _, err := mockService.ProcessCompletedWorkflowRun(executionData, workflowID) + // then + assert.NoError(t, err) + } + }) + } } // TestChaosWorkflowService_GetWorkflow is used to test the GetWorkflow method func TestChaosWorkflowService_GetWorkflow(t *testing.T) { - + // given + findResult := bson.D{{"workflow_id", uuid.NewString()}} + singleResult := mongo.NewSingleResultFromDocument(findResult, nil, nil) + testcases := []struct { + name string + isError bool + given func() + }{ + { + name: "success", + given: func() { + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, nil).Once() + }, + }, + { + name: "failure: mongo error", + given: func() { + mongoOperator.On("Get", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(singleResult, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := mockService.GetWorkflow(bson.D{}) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } } // TestChaosWorkflowService_GetWorkflows is used to test the GetWorkflows method func TestChaosWorkflowService_GetWorkflows(t *testing.T) { - + // given + findResult := []interface{}{bson.D{{"workflow_id", uuid.NewString()}}} + cursor, _ := mongo.NewCursorFromDocuments(findResult, nil, nil) + testcases := []struct { + name string + isError bool + given func() + }{ + { + name: "success", + given: func() { + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, nil).Once() + }, + }, + { + name: "failure: mongo error", + given: func() { + mongoOperator.On("List", mock.Anything, mongodb.WorkflowCollection, mock.Anything).Return(cursor, errors.New("")).Once() + }, + isError: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // given + tc.given() + // when + _, err := mockService.GetWorkflows(bson.D{}) + // then + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } } From c4c79f33a307f1b39910a3a7eb444c44d701c7f3 Mon Sep 17 00:00:00 2001 From: namkyu1999 Date: Sun, 7 May 2023 02:28:41 +0900 Subject: [PATCH 3/3] fix: change package name Signed-off-by: namkyu1999 --- .../graphql-server/pkg/chaos-workflow/handler/handler_test.go | 2 +- litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go | 2 +- .../mongodb/model/{mocks.go => mocks/mongo_operator.go} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename litmus-portal/graphql-server/pkg/database/mongodb/model/{mocks.go => mocks/mongo_operator.go} (100%) diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go index d367eee927b..6d6e53393e0 100644 --- a/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/handler/handler_test.go @@ -21,7 +21,7 @@ import ( store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" - mongodbMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model" + mongodbMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks" dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" dbOperationsWorkflowTemplate "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflowtemplate" gitOpsMocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/gitops/model/mocks" diff --git a/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go index be0c36f1dcf..6aa445d646d 100644 --- a/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go +++ b/litmus-portal/graphql-server/pkg/chaos-workflow/service_test.go @@ -15,7 +15,7 @@ import ( store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store" "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb" dbSchemaCluster "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/cluster" - mocks "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model" + "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks" dbOperationsWorkflow "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" workflowDBOps "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/workflow" log "github.com/sirupsen/logrus" diff --git a/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go b/litmus-portal/graphql-server/pkg/database/mongodb/model/mocks/mongo_operator.go similarity index 100% rename from litmus-portal/graphql-server/pkg/database/mongodb/model/mocks.go rename to litmus-portal/graphql-server/pkg/database/mongodb/model/mocks/mongo_operator.go