From 48d5c0f3e2e362ee9f2f5e0e590a34a5fb55efe2 Mon Sep 17 00:00:00 2001 From: Frank Jogeleit Date: Sun, 28 Apr 2024 10:07:24 +0200 Subject: [PATCH] Add API Tests Signed-off-by: Frank Jogeleit --- pkg/api/v1/api.go | 1 + pkg/api/v1/api_test.go | 287 +++++++++++++++++++++++++++++++ pkg/api/v1/model_test.go | 43 +++++ pkg/api/v2/api_test.go | 35 ++++ pkg/api/v2/views.go | 20 +-- pkg/api/v2/views_test.go | 358 +++++++++++++++++++++++++++++++++++++++ pkg/database/bun.go | 1 + 7 files changed, 735 insertions(+), 10 deletions(-) create mode 100644 pkg/api/v1/api_test.go create mode 100644 pkg/api/v1/model_test.go create mode 100644 pkg/api/v2/views_test.go diff --git a/pkg/api/v1/api.go b/pkg/api/v1/api.go index 5057e678..92e44653 100644 --- a/pkg/api/v1/api.go +++ b/pkg/api/v1/api.go @@ -24,6 +24,7 @@ func (h *APIHandler) Register(engine *gin.RouterGroup) error { engine.GET("namespaces", h.ListNamespaces) engine.GET("policy-reports", h.ListPolicyReports) engine.GET("cluster-policy-reports", h.ListClusterPolicyReports) + engine.GET("rule-status-count", h.RuleStatusCounts) ns := engine.Group("namespaced-resources") ns.GET("sources", h.ListNamespacedFilter("source")) diff --git a/pkg/api/v1/api_test.go b/pkg/api/v1/api_test.go new file mode 100644 index 00000000..2b232a5f --- /dev/null +++ b/pkg/api/v1/api_test.go @@ -0,0 +1,287 @@ +package v1_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + + "github.com/kyverno/policy-reporter/pkg/api" + v1 "github.com/kyverno/policy-reporter/pkg/api/v1" + "github.com/kyverno/policy-reporter/pkg/database" + "github.com/kyverno/policy-reporter/pkg/fixtures" + "github.com/kyverno/policy-reporter/pkg/report" + "github.com/kyverno/policy-reporter/pkg/target" + "github.com/kyverno/policy-reporter/pkg/target/webhook" +) + +func TestV1(t *testing.T) { + db, err := database.NewSQLiteDB("db_v2.db") + if err != nil { + assert.Fail(t, "failed to init SQLite DB") + } + + store, err := database.NewStore(db, "1.0") + if err != nil { + assert.Fail(t, "failed to init Store") + } + + if err := store.PrepareDatabase(context.Background()); err != nil { + assert.Fail(t, "failed to prepare Store") + } + + store.Add(context.Background(), fixtures.DefaultPolicyReport) + store.Add(context.Background(), fixtures.KyvernoPolicyReport) + store.Add(context.Background(), fixtures.KyvernoClusterPolicyReport) + + gin.SetMode(gin.ReleaseMode) + + server := api.NewServer(gin.New(), v1.WithAPI(store, []target.Client{ + webhook.NewClient(webhook.Options{ + ClientOptions: target.ClientOptions{ + Name: "Webhook", + SkipExistingOnStartup: true, + ResultFilter: &report.ResultFilter{ + MinimumPriority: "", + Sources: []string{"Kyverno"}, + }, + }, + Host: "http://localhost:8080", + }), + })) + + t.Run("TargetResponse", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/targets", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.Target, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 1, len(resp)) + assert.Contains(t, resp, v1.Target{ + Name: "Webhook", + MinimumPriority: "debug", + Sources: []string{"Kyverno"}, + SkipExistingOnStartup: true, + }) + } + }) + + t.Run("ListPolicyReports", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/policy-reports", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := api.Paginated[v1.PolicyReport]{} + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 2, resp.Count) + } + }) + + t.Run("ListClusterPolicyReports", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/cluster-policy-reports", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := api.Paginated[v1.PolicyReport]{} + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 1, resp.Count) + } + }) + + t.Run("ListNamespaces", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/namespaces", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]string, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 2, len(resp)) + } + }) + + t.Run("RuleStatusCounts", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/rule-status-count?policy=required-limit&rule=resource-limit-required", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.StatusCount, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 5, len(resp)) + assert.Contains(t, resp, v1.StatusCount{Status: "pass", Count: 1}) + assert.Contains(t, resp, v1.StatusCount{Status: "warn", Count: 1}) + } + }) + + t.Run("ListClusterFilter(Source)", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/cluster-resources/sources", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]string, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 1, len(resp)) + assert.Contains(t, resp, "Kyverno") + } + }) + + t.Run("ListNamespacedFilter(Source)", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/namespaced-resources/sources", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]string, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 2, len(resp)) + assert.Contains(t, resp, "Kyverno") + } + }) + + t.Run("ListClusterResources", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/cluster-resources/resources", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.Resource, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 1, len(resp)) + } + }) + + t.Run("ListNamespacedResources", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/namespaced-resources/resources", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.Resource, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 4, len(resp)) + } + }) + + t.Run("ListClusterStatusCounts", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/cluster-resources/status-counts", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.StatusCount, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 5, len(resp)) + } + }) + + t.Run("ListNamespacedStatusCounts", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/namespaced-resources/status-counts", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := make([]v1.NamespaceCount, 0) + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 5, len(resp)) + } + }) + + t.Run("ListClusterResults", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/cluster-resources/results", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := api.Paginated[v1.Result]{} + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 1, resp.Count) + } + }) + + t.Run("ListNamespacedResults", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/namespaced-resources/results", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + assert.Equal(t, http.StatusOK, w.Code) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := api.Paginated[v1.Result]{} + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 5, resp.Count) + } + }) +} diff --git a/pkg/api/v1/model_test.go b/pkg/api/v1/model_test.go new file mode 100644 index 00000000..b8ded810 --- /dev/null +++ b/pkg/api/v1/model_test.go @@ -0,0 +1,43 @@ +package v1_test + +import ( + "testing" + + v1 "github.com/kyverno/policy-reporter/pkg/api/v1" + "github.com/kyverno/policy-reporter/pkg/database" + "github.com/stretchr/testify/assert" +) + +func TestMapping(t *testing.T) { + t.Run("MapClusterStatusCounts", func(t *testing.T) { + result := v1.MapClusterStatusCounts([]database.StatusCount{ + {Source: "kyverno", Status: "pass", Count: 3}, + {Source: "kyverno", Status: "fail", Count: 4}, + }, []string{"pass", "fail"}) + + assert.Equal(t, 2, len(result)) + assert.Contains(t, result, v1.StatusCount{Status: "pass", Count: 3}) + assert.Contains(t, result, v1.StatusCount{Status: "fail", Count: 4}) + }) + + t.Run("MapNamespaceStatusCounts", func(t *testing.T) { + result := v1.MapNamespaceStatusCounts([]database.StatusCount{ + {Source: "kyverno", Status: "pass", Count: 3, Namespace: "default"}, + {Source: "kyverno", Status: "fail", Count: 4, Namespace: "default"}, + {Source: "kyverno", Status: "pass", Count: 2, Namespace: "user"}, + {Source: "kyverno", Status: "fail", Count: 2, Namespace: "user"}, + }, []string{"pass", "fail"}) + + assert.Equal(t, 2, len(result)) + + assert.Contains(t, result, v1.NamespaceStatusCount{Status: "pass", Items: []v1.NamespaceCount{ + {Namespace: "default", Count: 3, Status: "pass"}, + {Namespace: "user", Count: 2, Status: "pass"}, + }}) + + assert.Contains(t, result, v1.NamespaceStatusCount{Status: "fail", Items: []v1.NamespaceCount{ + {Namespace: "default", Count: 4, Status: "fail"}, + {Namespace: "user", Count: 2, Status: "fail"}, + }}) + }) +} diff --git a/pkg/api/v2/api_test.go b/pkg/api/v2/api_test.go index 394f6ba9..ddd37bfb 100644 --- a/pkg/api/v2/api_test.go +++ b/pkg/api/v2/api_test.go @@ -440,4 +440,39 @@ func TestV2(t *testing.T) { assert.True(t, resp["resources"]) } }) + + t.Run("ListFindings", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v2/findings", nil) + w := httptest.NewRecorder() + + server.Serve(w, req) + + if ok := assert.Equal(t, http.StatusOK, w.Code); ok { + resp := v2.Findings{} + + json.NewDecoder(w.Body).Decode(&resp) + + assert.Equal(t, 6, resp.Total) + assert.Equal(t, 4, resp.PerResult["fail"]) + assert.Equal(t, 1, resp.PerResult["pass"]) + assert.Equal(t, 1, resp.PerResult["warn"]) + assert.Equal(t, 2, len(resp.Counts)) + assert.Contains(t, resp.Counts, &v2.FindingCounts{ + Total: 3, + Source: "Kyverno", + Counts: map[string]int{ + "fail": 1, + "pass": 1, + "warn": 1, + }, + }) + assert.Contains(t, resp.Counts, &v2.FindingCounts{ + Total: 3, + Source: "test", + Counts: map[string]int{ + "fail": 3, + }, + }) + } + }) } diff --git a/pkg/api/v2/views.go b/pkg/api/v2/views.go index 1fe6f209..b94b5f4b 100644 --- a/pkg/api/v2/views.go +++ b/pkg/api/v2/views.go @@ -346,13 +346,9 @@ func MapResourceCategoryToSourceDetails(categories []db.ResourceCategory) []*Sou } type ValueFilter struct { - Include []string `json:"include,omitempty"` - Exclude []string `json:"exclude,omitempty"` - Selector map[string]string `json:"selector,omitempty"` -} - -func (v ValueFilter) Empty() bool { - return len(v.Exclude)+len(v.Include)+len(v.Selector) == 0 + Include []string `json:"include,omitempty"` + Exclude []string `json:"exclude,omitempty"` + Selector map[string]any `json:"selector,omitempty"` } type TargetFilter struct { @@ -379,13 +375,14 @@ type Target struct { } func MapValueFilter(f config.ValueFilter) *ValueFilter { - if len(f.Exclude)+len(f.Include) == 0 { + if len(f.Exclude)+len(f.Include) == 0+len(f.Selector) { return nil } return &ValueFilter{ - Include: f.Include, - Exclude: f.Exclude, + Include: f.Include, + Exclude: f.Exclude, + Selector: f.Selector, } } @@ -432,6 +429,8 @@ func MapLokiToTarget(ta *config.Target[config.LokiOptions]) *Target { if v, ok := ta.Config.Headers["Authorization"]; ok && v != "" { t.Auth = true + } else if ta.Config.Username != "" && ta.Config.Password != "" { + t.Auth = true } return t @@ -513,6 +512,7 @@ func MapSecurityHubToTarget(ta *config.Target[config.SecurityHubOptions]) *Targe t.Type = "SecurityHub" t.Host = ta.Config.Endpoint t.Properties["region"] = ta.Config.Region + t.Properties["cleanup"] = ta.Config.Cleanup t.Auth = true return t diff --git a/pkg/api/v2/views_test.go b/pkg/api/v2/views_test.go new file mode 100644 index 00000000..0d4f3dad --- /dev/null +++ b/pkg/api/v2/views_test.go @@ -0,0 +1,358 @@ +package v2_test + +import ( + "testing" + + v2 "github.com/kyverno/policy-reporter/pkg/api/v2" + "github.com/kyverno/policy-reporter/pkg/config" + "github.com/kyverno/policy-reporter/pkg/database" + "github.com/stretchr/testify/assert" +) + +func TestV2Views(t *testing.T) { + t.Run("MapValueFilter", func(t *testing.T) { + empty := v2.MapValueFilter(config.ValueFilter{}) + + assert.Nil(t, empty) + + original := config.ValueFilter{ + Include: []string{"default"}, + Exclude: []string{"kube-system"}, + Selector: map[string]any{"team": "marketing"}, + } + + filter := v2.MapValueFilter(original) + + assert.Equal(t, original.Include, filter.Include) + assert.Equal(t, original.Exclude, filter.Exclude) + assert.Equal(t, original.Selector, filter.Selector) + }) + + t.Run("MapResourceCategoryToSourceDetails", func(t *testing.T) { + result := v2.MapResourceCategoryToSourceDetails([]database.ResourceCategory{ + { + Source: "Kyverno", + Name: "PSS Baseline", + Pass: 8, + Fail: 3, + }, + { + Source: "Kyverno", + Name: "PSS Restricted", + Pass: 4, + Fail: 1, + }, + { + Source: "Trivy", + Name: "Vulnr", + Pass: 0, + Fail: 2, + Warn: 4, + }, + }) + + assert.Equal(t, 2, len(result)) + assert.Contains(t, result, &v2.SourceDetails{Name: "Kyverno", Categories: []*v2.Category{ + { + Name: "PSS Baseline", + Pass: 8, + Fail: 3, + }, + { + Name: "PSS Restricted", + Pass: 4, + Fail: 1, + }, + }}) + assert.Contains(t, result, &v2.SourceDetails{Name: "Trivy", Categories: []*v2.Category{ + { + Name: "Vulnr", + Pass: 0, + Fail: 2, + Warn: 4, + }, + }}) + }) + + t.Run("MapBaseToTarget", func(t *testing.T) { + target := v2.MapBaseToTarget(&config.Target[config.WebhookOptions]{ + Name: "Webhook", + MinimumPriority: "warning", + SecretRef: "ref", + MountedSecret: "mounted", + Sources: []string{"Kyverno"}, + SkipExisting: true, + Valid: true, + }) + + assert.Equal(t, "Webhook", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + assert.Equal(t, "ref", target.SecretRef) + assert.Equal(t, "mounted", target.MountedSecret) + assert.NotNil(t, target.CustomFields) + assert.NotNil(t, target.Properties) + assert.Equal(t, []string{"Kyverno"}, target.Filter.Sources.Include) + }) + + t.Run("MapSlackToTarget", func(t *testing.T) { + target := v2.MapSlackToTarget(&config.Target[config.SlackOptions]{ + Name: "Slack", + MinimumPriority: "warning", + Config: &config.SlackOptions{ + Channel: "general", + WebhookOptions: config.WebhookOptions{ + Webhook: "http://slack.com/xxxx", + }, + }, + Valid: true, + }) + + assert.Equal(t, "Slack", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + assert.Equal(t, "Slack", target.Type) + assert.Equal(t, "general", target.Properties["channel"]) + }) + + t.Run("MapLokiToTarget", func(t *testing.T) { + target := v2.MapLokiToTarget(&config.Target[config.LokiOptions]{ + Name: "Loki 1", + MinimumPriority: "warning", + Config: &config.LokiOptions{ + HostOptions: config.HostOptions{ + Host: "http://loki.monitoring:3000", + Certificate: "cert", + SkipTLS: true, + }, + Username: "user", + Password: "password", + Path: "v1/push", + }, + Valid: true, + }) + + assert.Equal(t, "Loki 1", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "Loki", target.Type) + assert.Equal(t, "v1/push", target.Properties["api"]) + assert.Equal(t, "http://loki.monitoring:3000", target.Host) + assert.True(t, target.SkipTLS) + assert.True(t, target.UseTLS) + assert.True(t, target.Auth) + }) + + t.Run("MapElasticsearchToTarget", func(t *testing.T) { + target := v2.MapElasticsearchToTarget(&config.Target[config.ElasticsearchOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.ElasticsearchOptions{ + HostOptions: config.HostOptions{ + Host: "http://elasticsearch.monitoring:3000", + Certificate: "cert", + SkipTLS: true, + Headers: map[string]string{ + "Authorization": "Bearer 123456", + }, + }, + Index: "policy-reporter", + Rotation: "daily", + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "Elasticsearch", target.Type) + assert.Equal(t, "policy-reporter", target.Properties["index"]) + assert.Equal(t, "daily", target.Properties["rotation"]) + assert.Equal(t, "http://elasticsearch.monitoring:3000", target.Host) + assert.True(t, target.SkipTLS) + assert.True(t, target.UseTLS) + assert.True(t, target.Auth) + }) + + t.Run("MapWebhhokToTarget", func(t *testing.T) { + target := v2.MapWebhhokToTarget("Discord")(&config.Target[config.WebhookOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.WebhookOptions{ + Webhook: "http://discord.com/12345/888XABC", + Certificate: "cert", + SkipTLS: true, + Headers: map[string]string{ + "Authorization": "Bearer 123456", + }, + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "Discord", target.Type) + assert.Equal(t, "http://discord.com", target.Host) + assert.True(t, target.SkipTLS) + assert.True(t, target.UseTLS) + assert.True(t, target.Auth) + }) + + t.Run("MapTelegramToTarget", func(t *testing.T) { + target := v2.MapTelegramToTarget(&config.Target[config.TelegramOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.TelegramOptions{ + Token: "ABCDE", + ChatID: "1234567", + WebhookOptions: config.WebhookOptions{ + Webhook: "http://telegram.com", + Certificate: "cert", + SkipTLS: true, + }, + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "Telegram", target.Type) + assert.Equal(t, "http://telegram.com", target.Host) + assert.Equal(t, "1234567", target.Properties["chatID"]) + assert.True(t, target.SkipTLS) + assert.True(t, target.UseTLS) + assert.False(t, target.Auth) + }) + + t.Run("MapS3ToTarget", func(t *testing.T) { + target := v2.MapS3ToTarget(&config.Target[config.S3Options]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.S3Options{ + Prefix: "policy-reporter", + Bucket: "kyverno", + AWSConfig: config.AWSConfig{ + Region: "eu-central-1", + Endpoint: "https://s3.aws.com", + }, + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "S3", target.Type) + assert.Equal(t, "https://s3.aws.com", target.Host) + assert.Equal(t, "kyverno", target.Properties["bucket"]) + assert.Equal(t, "policy-reporter", target.Properties["prefix"]) + assert.Equal(t, "eu-central-1", target.Properties["region"]) + assert.True(t, target.Auth) + }) + + t.Run("MapKinesisToTarget", func(t *testing.T) { + target := v2.MapKinesisToTarget(&config.Target[config.KinesisOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.KinesisOptions{ + StreamName: "policy-reporter", + AWSConfig: config.AWSConfig{ + Region: "eu-central-1", + Endpoint: "https://kinesis.aws.com", + }, + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "Kinesis", target.Type) + assert.Equal(t, "https://kinesis.aws.com", target.Host) + assert.Equal(t, "policy-reporter", target.Properties["stream"]) + assert.Equal(t, "eu-central-1", target.Properties["region"]) + assert.True(t, target.Auth) + }) + + t.Run("MapSecurityHubToTarget", func(t *testing.T) { + target := v2.MapSecurityHubToTarget(&config.Target[config.SecurityHubOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.SecurityHubOptions{ + AccountID: "policy-reporter", + Cleanup: true, + AWSConfig: config.AWSConfig{ + Region: "eu-central-1", + Endpoint: "https://securityhub.aws.com", + }, + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "SecurityHub", target.Type) + assert.Equal(t, "https://securityhub.aws.com", target.Host) + assert.Equal(t, "eu-central-1", target.Properties["region"]) + assert.Equal(t, true, target.Properties["cleanup"]) + assert.True(t, target.Auth) + }) + + t.Run("MapGCSToTarget", func(t *testing.T) { + target := v2.MapGCSToTarget(&config.Target[config.GCSOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.GCSOptions{ + Prefix: "policy-reporter", + Bucket: "kyverno", + }, + Valid: true, + }) + + assert.Equal(t, "Target", target.Name) + assert.Equal(t, "warning", target.MinimumPriority) + + assert.Equal(t, "GoogleCloudStore", target.Type) + assert.Equal(t, "kyverno", target.Properties["bucket"]) + assert.Equal(t, "policy-reporter", target.Properties["prefix"]) + assert.True(t, target.Auth) + }) + + t.Run("MapTargets", func(t *testing.T) { + targets := v2.MapTargets(&config.Target[config.GCSOptions]{ + Name: "Target", + MinimumPriority: "warning", + Config: &config.GCSOptions{ + Prefix: "policy-reporter", + Bucket: "kyverno", + }, + Valid: true, + Channels: []*config.Target[config.GCSOptions]{ + { + + Name: "Target 2", + MinimumPriority: "warning", + Config: &config.GCSOptions{ + Prefix: "policy-reporter", + Bucket: "trivy", + }, + Valid: true, + }, + { + + Name: "Target 2", + MinimumPriority: "warning", + Config: &config.GCSOptions{ + Prefix: "policy-reporter", + Bucket: "trivy", + }, + Valid: false, + }, + }, + }, v2.MapGCSToTarget) + + assert.Equal(t, 2, len(targets)) + }) + +} diff --git a/pkg/database/bun.go b/pkg/database/bun.go index 5233314c..27d95b37 100644 --- a/pkg/database/bun.go +++ b/pkg/database/bun.go @@ -680,6 +680,7 @@ func (s *Store) FetchFindingCounts(ctx context.Context, filter Filter) ([]Status FilterReportLabels(filter.ReportLabel). Exclude(filter, "f"). Group("f.source", "status"). + Order("f.source"). Scan(ctx, &results) return results, err