From 008b501718c8f86b1858d474e6ca4433867f2dec Mon Sep 17 00:00:00 2001 From: nicufk <89570185+nicufk@users.noreply.github.com> Date: Thu, 18 Nov 2021 13:36:13 +0200 Subject: [PATCH] feat: Improve filters and add integration tests (reloaded). (#521) * feat: Improve filters and add integration tests. * fix: Use random name for execution id in mongo tests. * fix: Pass by value for filter dates. --- .github/workflows/test.yml | 7 +- internal/pkg/api/repository/result/filter.go | 2 +- internal/pkg/api/repository/result/mongo.go | 72 ++++-- .../pkg/api/repository/result/mongo_test.go | 229 ++++++++++++++++++ 4 files changed, 283 insertions(+), 27 deletions(-) create mode 100644 internal/pkg/api/repository/result/mongo_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e2527c4f3f9..d7ed952a622 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,12 @@ jobs: build: runs-on: ubuntu-latest - + services: + mongo: + image: bitnami/mongodb + ports: + - 27017:27017 + steps: - uses: actions/checkout@v2 diff --git a/internal/pkg/api/repository/result/filter.go b/internal/pkg/api/repository/result/filter.go index 0e499fbf100..1ec424b39e1 100644 --- a/internal/pkg/api/repository/result/filter.go +++ b/internal/pkg/api/repository/result/filter.go @@ -75,7 +75,7 @@ func (f filter) EndDate() time.Time { } func (f filter) StatusDefined() bool { - return f.endDate != nil + return f.status != nil } func (f filter) Status() testkube.ExecutionStatus { diff --git a/internal/pkg/api/repository/result/mongo.go b/internal/pkg/api/repository/result/mongo.go index 8cd9d9c0664..8b1d3feb539 100644 --- a/internal/pkg/api/repository/result/mongo.go +++ b/internal/pkg/api/repository/result/mongo.go @@ -48,7 +48,6 @@ func (r *MongoRepository) GetNewestExecutions(ctx context.Context, limit int) (r func (r *MongoRepository) GetExecutions(ctx context.Context, filter Filter) (result []testkube.Execution, err error) { query, opts := composeQueryAndOpts(filter) cursor, err := r.Coll.Find(ctx, query, opts) - if err != nil { return } @@ -59,34 +58,57 @@ func (r *MongoRepository) GetExecutions(ctx context.Context, filter Filter) (res func (r *MongoRepository) GetExecutionTotals(ctx context.Context, filter Filter) (result testkube.ExecutionsTotals, err error) { query, _ := composeQueryAndOpts(filter) - query["scriptType"] = bson.M{"$exists": true} total, err := r.Coll.CountDocuments(ctx, query) if err != nil { - return + return result, err } result.Results = int32(total) - query["executionResult"] = bson.M{"status": testkube.QUEUED_ExecutionStatus} - queued, err := r.Coll.CountDocuments(ctx, query) - if err != nil { - return - } - result.Queued = int32(queued) - - query["executionResult"] = bson.M{"status": testkube.PENDING_ExecutionStatus} - pending, err := r.Coll.CountDocuments(ctx, query) - if err != nil { - return + if status, ok := query["executionresult.status"]; ok { + count, err := r.Coll.CountDocuments(ctx, query) + if err != nil { + return result, err + } + switch status { + case testkube.QUEUED_ExecutionStatus: + result.Queued = int32(count) + case testkube.PENDING_ExecutionStatus: + result.Pending = int32(count) + case testkube.SUCCESS_ExecutionStatus: + result.Passed = int32(count) + case testkube.ERROR__ExecutionStatus: + result.Failed = int32(count) + } + } else { + query["executionresult.status"] = testkube.ExecutionStatusQueued + queued, err := r.Coll.CountDocuments(ctx, query) + if err != nil { + return result, err + } + result.Queued = int32(queued) + + query["executionresult.status"] = testkube.ExecutionStatusPending + pending, err := r.Coll.CountDocuments(ctx, query) + if err != nil { + return result, err + } + result.Pending = int32(pending) + + query["executionresult.status"] = testkube.ExecutionStatusSuccess + passed, err := r.Coll.CountDocuments(ctx, query) + if err != nil { + return result, err + } + result.Passed = int32(passed) + + query["executionresult.status"] = testkube.ExecutionStatusError + failed, err := r.Coll.CountDocuments(ctx, query) + if err != nil { + return result, err + } + result.Failed = int32(failed) } - result.Pending = int32(pending) - - query["executionResult"] = bson.M{"status": testkube.ERROR__ExecutionStatus} - failed, err := r.Coll.CountDocuments(ctx, query) - if err != nil { - return - } - result.Failed = int32(failed) - return + return result, err } func (r *MongoRepository) Insert(ctx context.Context, result testkube.Execution) (err error) { @@ -135,11 +157,11 @@ func composeQueryAndOpts(filter Filter) (bson.M, *options.FindOptions) { } if len(startTimeQuery) > 0 { - query["startTime"] = startTimeQuery + query["starttime"] = startTimeQuery } if filter.StatusDefined() { - query["executionResult"] = bson.M{"status": filter.Status()} + query["executionresult.status"] = filter.Status() } opts.SetSkip(int64(filter.Page() * filter.PageSize())) diff --git a/internal/pkg/api/repository/result/mongo_test.go b/internal/pkg/api/repository/result/mongo_test.go new file mode 100644 index 00000000000..d72eeac72d0 --- /dev/null +++ b/internal/pkg/api/repository/result/mongo_test.go @@ -0,0 +1,229 @@ +package result + +import ( + "context" + "testing" + "time" + + "github.com/kubeshop/testkube/internal/pkg/api/repository/storage" + "github.com/kubeshop/testkube/pkg/api/v1/testkube" + "github.com/kubeshop/testkube/pkg/rand" + "github.com/stretchr/testify/require" +) + +const ( + mongoDns = "mongodb://localhost:27017" + mongoDbName = "testkube" +) + +func TestFilters(t *testing.T) { + assert := require.New(t) + + repository, err := getRepository() + assert.NoError(err) + + err = repository.Coll.Drop(context.TODO()) + assert.NoError(err) + + oneDayAgo := time.Now().Add(-24 * time.Hour) + twoDaysAgo := time.Now().Add(-48 * time.Hour) + defaultName := "name" + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.SUCCESS_ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.QUEUED_ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.PENDING_ExecutionStatus, time.Now()) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.SUCCESS_ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.QUEUED_ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.PENDING_ExecutionStatus, oneDayAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.ERROR__ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.SUCCESS_ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.QUEUED_ExecutionStatus, twoDaysAgo) + assert.NoError(err) + err = repository.insertExecutionResult(defaultName, testkube.PENDING_ExecutionStatus, twoDaysAgo) + assert.NoError(err) + + t.Run("filter with status should return only executions with that status", func(t *testing.T) { + + executions, err := repository.GetExecutions(context.Background(), NewExecutionsFilter().WithStatus(testkube.ERROR__ExecutionStatus)) + assert.NoError(err) + assert.Len(executions, 12) + assert.Equal(*executions[0].ExecutionResult.Status, testkube.ERROR__ExecutionStatus) + }) + + t.Run("filter with status should return only totals with that status", func(t *testing.T) { + filteredTotals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter().WithStatus(testkube.ERROR__ExecutionStatus)) + + assert.NoError(err) + assert.Equal(int32(12), filteredTotals.Results) + assert.Equal(int32(12), filteredTotals.Failed) + assert.Equal(int32(0), filteredTotals.Passed) + assert.Equal(int32(0), filteredTotals.Queued) + assert.Equal(int32(0), filteredTotals.Pending) + }) + + t.Run("getting totals without filters should return all the executions", func(t *testing.T) { + totals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter()) + + assert.NoError(err) + assert.Equal(int32(21), totals.Results) + assert.Equal(int32(12), totals.Failed) + assert.Equal(int32(3), totals.Passed) + assert.Equal(int32(3), totals.Queued) + assert.Equal(int32(3), totals.Pending) + }) + + t.Run("filter with startDate should return only executions after that day", func(t *testing.T) { + + executions, err := repository.GetExecutions(context.Background(), NewExecutionsFilter().WithStartDate(oneDayAgo)) + assert.NoError(err) + assert.Len(executions, 14) + assert.True(executions[0].StartTime.After(oneDayAgo) || executions[0].StartTime.Equal(oneDayAgo)) + }) + + t.Run("getting totals with filter by date start date should return only the results after this date", func(t *testing.T) { + totals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter().WithStartDate(oneDayAgo)) + + assert.NoError(err) + assert.Equal(int32(14), totals.Results) + assert.Equal(int32(8), totals.Failed) + assert.Equal(int32(2), totals.Passed) + assert.Equal(int32(2), totals.Queued) + assert.Equal(int32(2), totals.Pending) + }) + + t.Run("filter with endDate should return only executions before that day", func(t *testing.T) { + + executions, err := repository.GetExecutions(context.Background(), NewExecutionsFilter().WithEndDate(oneDayAgo)) + assert.NoError(err) + assert.Len(executions, 14) + assert.True(executions[0].StartTime.Before(oneDayAgo) || executions[0].StartTime.Equal(oneDayAgo)) + }) + + t.Run("getting totals with filter by date start date should return only the results before this date", func(t *testing.T) { + totals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter().WithEndDate(oneDayAgo)) + + assert.NoError(err) + assert.Equal(int32(14), totals.Results) + assert.Equal(int32(8), totals.Failed) + assert.Equal(int32(2), totals.Passed) + assert.Equal(int32(2), totals.Queued) + assert.Equal(int32(2), totals.Pending) + }) + + t.Run("filter with script name that doesn't exist should return 0 results", func(t *testing.T) { + + executions, err := repository.GetExecutions(context.Background(), NewExecutionsFilter().WithScriptName("noneExisting")) + assert.NoError(err) + assert.Empty(executions) + }) + + t.Run("getting totals with script name that doesn't exist should return 0 results", func(t *testing.T) { + totals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter().WithScriptName("noneExisting")) + + assert.NoError(err) + assert.Equal(int32(0), totals.Results) + assert.Equal(int32(0), totals.Failed) + assert.Equal(int32(0), totals.Passed) + assert.Equal(int32(0), totals.Queued) + assert.Equal(int32(0), totals.Pending) + }) + + t.Run("filter with ccombined filter should return corresponding results", func(t *testing.T) { + filter := NewExecutionsFilter(). + WithStatus(testkube.SUCCESS_ExecutionStatus). + WithStartDate(twoDaysAgo). + WithEndDate(oneDayAgo). + WithScriptName(defaultName) + executions, err := repository.GetExecutions(context.Background(), filter) + assert.NoError(err) + assert.Len(executions, 2) + }) + + t.Run("getting totals with ccombined filter should return corresponding results", func(t *testing.T) { + filter := NewExecutionsFilter(). + WithStatus(testkube.SUCCESS_ExecutionStatus). + WithStartDate(twoDaysAgo). + WithEndDate(oneDayAgo). + WithScriptName(defaultName) + totals, err := repository.GetExecutionTotals(context.Background(), filter) + + assert.NoError(err) + assert.Equal(int32(2), totals.Results) + assert.Equal(int32(0), totals.Failed) + assert.Equal(int32(2), totals.Passed) + assert.Equal(int32(0), totals.Queued) + assert.Equal(int32(0), totals.Pending) + }) + + name := "someDifferentName" + err = repository.insertExecutionResult(name, testkube.PENDING_ExecutionStatus, twoDaysAgo) + assert.NoError(err) + + t.Run("filter with script name should return result only for that script name", func(t *testing.T) { + + executions, err := repository.GetExecutions(context.Background(), NewExecutionsFilter().WithScriptName(name)) + assert.NoError(err) + assert.Len(executions, 1) + assert.Equal(executions[0].ScriptName, name) + }) + + t.Run("getting totals with script name should return result only for that script name", func(t *testing.T) { + totals, err := repository.GetExecutionTotals(context.Background(), NewExecutionsFilter().WithScriptName(name)) + + assert.NoError(err) + assert.Equal(int32(1), totals.Results) + assert.Equal(int32(0), totals.Failed) + assert.Equal(int32(0), totals.Passed) + assert.Equal(int32(0), totals.Queued) + assert.Equal(int32(1), totals.Pending) + }) + +} + +func getRepository() (*MongoRepository, error) { + db, err := storage.GetMongoDataBase(mongoDns, mongoDbName) + repository := NewMongoRespository(db) + return repository, err +} + +func (repository *MongoRepository) insertExecutionResult(scriptName string, execStatus testkube.ExecutionStatus, startTime time.Time) error { + return repository.Insert(context.Background(), + testkube.Execution{ + Id: rand.Name(), + ScriptName: scriptName, + Name: "dummyName", + ScriptType: "test/curl", + StartTime: startTime, + EndTime: time.Now(), + ExecutionResult: &testkube.ExecutionResult{Status: &execStatus}, + }) +}