From bc40b1f0f7d6c1bda11dca69660658d9169dc8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Mon, 16 Dec 2024 13:21:23 +0100 Subject: [PATCH] chore: add unit tests (#2240) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/engine/clusters/cluster.go | 4 +- pkg/engine/clusters/cluster_test.go | 23 +- pkg/runner/context/context.go | 34 +-- pkg/runner/context/context_test.go | 311 ++++++++++++++++++++++++++++ pkg/runner/context/init.go | 10 +- pkg/runner/context/init_test.go | 35 ++++ pkg/runner/operation_test.go | 3 +- pkg/runner/runner_test.go | 4 +- pkg/runner/step_test.go | 3 +- 9 files changed, 383 insertions(+), 44 deletions(-) create mode 100644 pkg/runner/context/context_test.go diff --git a/pkg/engine/clusters/cluster.go b/pkg/engine/clusters/cluster.go index 731cd384b..ec2dc8144 100644 --- a/pkg/engine/clusters/cluster.go +++ b/pkg/engine/clusters/cluster.go @@ -16,10 +16,10 @@ type fromConfig struct { config *rest.Config } -func NewClusterFromConfig(config *rest.Config) (Cluster, error) { +func NewClusterFromConfig(config *rest.Config) Cluster { return &fromConfig{ config: config, - }, nil + } } func (c *fromConfig) Config() (*rest.Config, error) { diff --git a/pkg/engine/clusters/cluster_test.go b/pkg/engine/clusters/cluster_test.go index d451dd31e..c9df1e9a1 100644 --- a/pkg/engine/clusters/cluster_test.go +++ b/pkg/engine/clusters/cluster_test.go @@ -9,10 +9,9 @@ import ( func TestNewClusterFromConfig(t *testing.T) { tests := []struct { - name string - config *rest.Config - want Cluster - wantErr bool + name string + config *rest.Config + want Cluster }{{ name: "nil", want: &fromConfig{ @@ -27,17 +26,11 @@ func TestNewClusterFromConfig(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewClusterFromConfig(tt.config) - if tt.wantErr { - assert.Error(t, err) - assert.Nil(t, got) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - got, err := got.Config() - assert.NoError(t, err) - assert.Same(t, tt.config, got) - } + got := NewClusterFromConfig(tt.config) + assert.Equal(t, tt.want, got) + config, err := got.Config() + assert.NoError(t, err) + assert.Same(t, tt.config, config) }) } } diff --git a/pkg/runner/context/context.go b/pkg/runner/context/context.go index 4638d2058..855b4a0d8 100644 --- a/pkg/runner/context/context.go +++ b/pkg/runner/context/context.go @@ -13,6 +13,7 @@ import ( "github.com/kyverno/kyverno-json/pkg/core/compilers" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" + "k8s.io/utils/clock" ) type TestContext struct { @@ -34,21 +35,22 @@ type TestContext struct { timeouts v1alpha1.DefaultTimeouts } -func MakeContext(bindings apis.Bindings, registry clusters.Registry) TestContext { +func MakeContext(clock clock.PassiveClock, bindings apis.Bindings, registry clusters.Registry) TestContext { return TestContext{ Summary: &model.Summary{}, Report: &model.Report{ Name: "chainsaw-report", - StartTime: time.Now(), + StartTime: clock.Now(), }, - bindings: bindings, - clusters: registry, - compilers: apis.DefaultCompilers, + bindings: bindings, + clusters: registry, + compilers: apis.DefaultCompilers, + deletionPropagation: metav1.DeletePropagationBackground, } } -func EmptyContext() TestContext { - return MakeContext(apis.NewBindings(), clusters.NewRegistry(nil)) +func EmptyContext(clock clock.PassiveClock) TestContext { + return MakeContext(clock, apis.NewBindings(), clusters.NewRegistry(nil)) } func (tc *TestContext) Bindings() apis.Bindings { @@ -59,10 +61,6 @@ func (tc *TestContext) Catch() []v1alpha1.CatchFinally { return tc.catch } -func (tc *TestContext) Compilers() compilers.Compilers { - return tc.compilers -} - func (tc *TestContext) Cluster(name string) clusters.Cluster { return tc.clusters.Lookup(name) } @@ -71,6 +69,10 @@ func (tc *TestContext) Clusters() clusters.Registry { return tc.clusters } +func (tc *TestContext) Compilers() compilers.Compilers { + return tc.compilers +} + func (tc *TestContext) CurrentCluster() clusters.Cluster { return tc.cluster } @@ -129,11 +131,6 @@ func (tc TestContext) WithCatch(catch ...v1alpha1.CatchFinally) TestContext { return tc } -func (tc TestContext) WithDefaultCompiler(name string) TestContext { - tc.compilers = tc.compilers.WithDefaultCompiler(name) - return tc -} - func (tc TestContext) WithCluster(name string, cluster clusters.Cluster) TestContext { tc.clusters = tc.clusters.Register(name, cluster) return tc @@ -144,6 +141,11 @@ func (tc TestContext) WithCurrentCluster(name string) TestContext { return tc } +func (tc TestContext) WithDefaultCompiler(name string) TestContext { + tc.compilers = tc.compilers.WithDefaultCompiler(name) + return tc +} + func (tc TestContext) WithDelayBeforeCleanup(delayBeforeCleanup *time.Duration) TestContext { tc.delayBeforeCleanup = delayBeforeCleanup return tc diff --git a/pkg/runner/context/context_test.go b/pkg/runner/context/context_test.go new file mode 100644 index 000000000..d42d55eb2 --- /dev/null +++ b/pkg/runner/context/context_test.go @@ -0,0 +1,311 @@ +package context + +import ( + "testing" + "time" + + "github.com/kyverno/chainsaw/pkg/apis" + "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" + "github.com/kyverno/chainsaw/pkg/engine/clusters" + "github.com/kyverno/chainsaw/pkg/loaders/config" + "github.com/kyverno/chainsaw/pkg/model" + "github.com/kyverno/kyverno-json/pkg/core/expression" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" + "k8s.io/utils/clock" + tclock "k8s.io/utils/clock/testing" + "k8s.io/utils/ptr" +) + +func TestEmptyContext(t *testing.T) { + clock := tclock.NewFakePassiveClock(time.Time{}) + tests := []struct { + name string + want TestContext + }{{ + want: TestContext{ + Summary: &model.Summary{}, + Report: &model.Report{ + Name: "chainsaw-report", + StartTime: clock.Now(), + }, + bindings: apis.NewBindings(), + clusters: clusters.NewRegistry(nil), + compilers: apis.DefaultCompilers, + deletionPropagation: metav1.DeletePropagationBackground, + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := EmptyContext(clock) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestTestContext_Bindings(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithBinding("foo", "bar") + { + value, err := parent.Bindings().Get("$foo") + assert.Error(t, err) + assert.Nil(t, value) + } + { + value, err := child.Bindings().Get("$foo") + assert.NoError(t, err) + assert.NotNil(t, value) + } +} + +func TestTestContext_Catch(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithCatch([]v1alpha1.CatchFinally{{}}...) + { + value := parent.Catch() + assert.Nil(t, value) + } + { + value := child.Catch() + assert.NotNil(t, value) + } +} + +func TestTestContext_Cluster(t *testing.T) { + config, err := config.DefaultConfiguration() + assert.NoError(t, err) + defaultCluster := &rest.Config{} + parent, err := InitContext(config.Spec, defaultCluster, nil) + assert.NoError(t, err) + child := parent. + WithCluster(clusters.DefaultClient, nil). + WithCluster("foo", clusters.NewClusterFromKubeconfig("foo", "bar")) + { + d := parent.Cluster(clusters.DefaultClient) + f := parent.Cluster("foo") + assert.NotNil(t, d) + assert.Nil(t, f) + } + { + d := child.Cluster(clusters.DefaultClient) + f := child.Cluster("foo") + assert.Nil(t, d) + assert.NotNil(t, f) + } +} + +func TestTestContext_Clusters(t *testing.T) { + config, err := config.DefaultConfiguration() + assert.NoError(t, err) + defaultCluster := &rest.Config{} + parent, err := InitContext(config.Spec, defaultCluster, nil) + assert.NoError(t, err) + child := parent.WithCluster("foo", clusters.NewClusterFromKubeconfig("foo", "bar")) + { + assert.NotNil(t, parent.Clusters()) + d := parent.Clusters().Lookup(clusters.DefaultClient) + f := parent.Clusters().Lookup("foo") + assert.NotNil(t, d) + assert.Nil(t, f) + } + { + assert.NotNil(t, child.Clusters()) + d := child.Clusters().Lookup(clusters.DefaultClient) + f := child.Clusters().Lookup("foo") + assert.NotNil(t, d) + assert.NotNil(t, f) + } +} + +func TestTestContext_Compilers(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithDefaultCompiler(expression.CompilerCEL) + { + value := parent.Compilers() + assert.Equal(t, value.Jp, value.Default) + } + { + value := child.Compilers() + assert.Equal(t, value.Cel, value.Default) + } +} + +func TestTestContext_CurrentCluster(t *testing.T) { + config, err := config.DefaultConfiguration() + assert.NoError(t, err) + defaultCluster := &rest.Config{} + parent, err := InitContext(config.Spec, defaultCluster, nil) + assert.NoError(t, err) + child := parent.WithCurrentCluster("foo") + { + assert.NotNil(t, parent.CurrentCluster()) + } + { + assert.Nil(t, child.CurrentCluster()) + } +} + +func TestTestContext_CurrentClusterClient(t *testing.T) { + config, err := config.DefaultConfiguration() + assert.NoError(t, err) + defaultCluster := &rest.Config{} + parent, err := InitContext(config.Spec, defaultCluster, nil) + assert.NoError(t, err) + child := parent.WithCurrentCluster("foo") + child2 := parent.WithDryRun(true) + { + config, client, err := parent.CurrentClusterClient() + assert.NoError(t, err) + assert.NotNil(t, config) + assert.NotNil(t, client) + } + { + config, client, err := child.CurrentClusterClient() + assert.NoError(t, err) + assert.Nil(t, config) + assert.Nil(t, client) + } + { + config, client, err := child2.CurrentClusterClient() + assert.NoError(t, err) + assert.NotNil(t, config) + assert.NotNil(t, client) + } +} + +func TestTestContext_DelayBeforeCleanup(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithDelayBeforeCleanup(ptr.To(30 * time.Second)) + { + value := parent.DelayBeforeCleanup() + assert.Nil(t, value) + } + { + value := child.DelayBeforeCleanup() + assert.Equal(t, value, ptr.To(30*time.Second)) + } +} + +func TestTestContext_DeletionPropagation(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithDeletionPropagation(metav1.DeletePropagationOrphan) + { + value := parent.DeletionPropagation() + assert.Equal(t, metav1.DeletePropagationBackground, value) + } + { + value := child.DeletionPropagation() + assert.Equal(t, metav1.DeletePropagationOrphan, value) + } +} + +func TestTestContext_DryRun(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithDryRun(true) + { + value := parent.DryRun() + assert.False(t, value) + } + { + value := child.DryRun() + assert.True(t, value) + } +} + +func TestTestContext_FailFast(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithFailFast(true) + { + value := parent.FailFast() + assert.False(t, value) + } + { + value := child.FailFast() + assert.True(t, value) + } +} + +func TestTestContext_FullName(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithFullName(true) + { + value := parent.FullName() + assert.False(t, value) + } + { + value := child.FullName() + assert.True(t, value) + } +} + +func TestTestContext_SkipDelete(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithSkipDelete(true) + { + value := parent.SkipDelete() + assert.False(t, value) + } + { + value := child.SkipDelete() + assert.True(t, value) + } +} + +func TestTestContext_Templating(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithTemplating(true) + { + value := parent.Templating() + assert.False(t, value) + } + { + value := child.Templating() + assert.True(t, value) + } +} + +func TestTestContext_TerminationGrace(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithTerminationGrace(ptr.To(30 * time.Second)) + { + value := parent.TerminationGrace() + assert.Nil(t, value) + } + { + value := child.TerminationGrace() + assert.Equal(t, ptr.To(30*time.Second), value) + } +} + +func TestTestContext_Timeouts(t *testing.T) { + parent := EmptyContext(clock.RealClock{}) + child := parent.WithTimeouts(v1alpha1.Timeouts{ + Apply: &metav1.Duration{Duration: 10 * time.Second}, + Assert: &metav1.Duration{Duration: 20 * time.Second}, + Cleanup: &metav1.Duration{Duration: 30 * time.Second}, + Delete: &metav1.Duration{Duration: 40 * time.Second}, + Error: &metav1.Duration{Duration: 50 * time.Second}, + Exec: &metav1.Duration{Duration: 60 * time.Second}, + }) + child2 := parent.WithTimeouts(v1alpha1.Timeouts{}) + { + value := parent.Timeouts() + assert.Equal(t, v1alpha1.DefaultTimeouts{}, value) + } + { + value := child.Timeouts() + assert.Equal(t, v1alpha1.DefaultTimeouts{ + Apply: metav1.Duration{Duration: 10 * time.Second}, + Assert: metav1.Duration{Duration: 20 * time.Second}, + Cleanup: metav1.Duration{Duration: 30 * time.Second}, + Delete: metav1.Duration{Duration: 40 * time.Second}, + Error: metav1.Duration{Duration: 50 * time.Second}, + Exec: metav1.Duration{Duration: 60 * time.Second}, + }, value) + } + { + value := child2.Timeouts() + assert.Equal(t, v1alpha1.DefaultTimeouts{}, value) + } +} diff --git a/pkg/runner/context/init.go b/pkg/runner/context/init.go index 55f298ed6..5b8155c22 100644 --- a/pkg/runner/context/init.go +++ b/pkg/runner/context/init.go @@ -5,10 +5,11 @@ import ( "github.com/kyverno/chainsaw/pkg/engine/clusters" "github.com/kyverno/chainsaw/pkg/model" "k8s.io/client-go/rest" + "k8s.io/utils/clock" ) func InitContext(config model.Configuration, defaultCluster *rest.Config, values any) (TestContext, error) { - tc := EmptyContext() + tc := EmptyContext(clock.RealClock{}) // cleanup options tc = tc.WithSkipDelete(config.Cleanup.SkipDelete) if config.Cleanup.DelayBeforeCleanup != nil { @@ -44,12 +45,7 @@ func InitContext(config model.Configuration, defaultCluster *rest.Config, values // clusters tc = WithClusters(tc, "", config.Clusters) if defaultCluster != nil { - cluster, err := clusters.NewClusterFromConfig(defaultCluster) - if err != nil { - return tc, err - } - tc = tc.WithCluster(clusters.DefaultClient, cluster) - return WithCurrentCluster(tc, clusters.DefaultClient) + return WithCurrentCluster(tc.WithCluster(clusters.DefaultClient, clusters.NewClusterFromConfig(defaultCluster)), clusters.DefaultClient) } return tc, nil } diff --git a/pkg/runner/context/init_test.go b/pkg/runner/context/init_test.go index d8829bada..a47873f3a 100644 --- a/pkg/runner/context/init_test.go +++ b/pkg/runner/context/init_test.go @@ -47,6 +47,7 @@ func TestInitContext(t *testing.T) { assert.False(t, tc.SkipDelete()) assert.True(t, tc.Templating()) assert.Nil(t, tc.TerminationGrace()) + assert.Equal(t, config.Spec.Timeouts, tc.Timeouts()) }, }, { name: "with delay before cleanup", @@ -159,6 +160,40 @@ func TestInitContext(t *testing.T) { assert.True(t, tc.Templating()) assert.Nil(t, tc.TerminationGrace()) }, + }, { + name: "with clusters", + config: func(config model.Configuration) model.Configuration { + config.Clusters = v1alpha1.Clusters{ + "foo": v1alpha1.Cluster{ + Kubeconfig: "foo", + Context: "bar", + }, + } + return config + }(config.Spec), + defaultCluster: nil, + values: nil, + wantErr: false, + want: func(t *testing.T, tc TestContext) { + t.Helper() + assert.Equal(t, int32(0), tc.Passed()) + assert.Equal(t, int32(0), tc.Failed()) + assert.Equal(t, int32(0), tc.Skipped()) + assert.NotNil(t, tc.Bindings()) + assert.Nil(t, tc.Catch()) + assert.NotNil(t, tc.Compilers()) + assert.NotNil(t, tc.Clusters()) + assert.NotNil(t, tc.Clusters().Lookup("foo")) + assert.Nil(t, tc.CurrentCluster()) + assert.Nil(t, tc.DelayBeforeCleanup()) + assert.Equal(t, metav1.DeletePropagationBackground, tc.DeletionPropagation()) + assert.False(t, tc.DryRun()) + assert.False(t, tc.FailFast()) + assert.False(t, tc.FullName()) + assert.False(t, tc.SkipDelete()) + assert.True(t, tc.Templating()) + assert.Nil(t, tc.TerminationGrace()) + }, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/runner/operation_test.go b/pkg/runner/operation_test.go index b15e642e4..bbfb4b2ed 100644 --- a/pkg/runner/operation_test.go +++ b/pkg/runner/operation_test.go @@ -13,6 +13,7 @@ import ( enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" "github.com/kyverno/chainsaw/pkg/testing" "github.com/stretchr/testify/assert" + "k8s.io/utils/clock" ) func TestOperation_Execute(t *testing.T) { @@ -57,7 +58,7 @@ func TestOperation_Execute(t *testing.T) { return localTC.operation, &localTC.timeout, tc, nil }, ) - tcontext := enginecontext.EmptyContext() + tcontext := enginecontext.EmptyContext(clock.RealClock{}) ctx := context.Background() _, err := op.execute(ctx, tcontext, &model.StepReport{}) if localTC.expectedFail { diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index d194b4a17..0c8639c6c 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -122,7 +122,7 @@ func Test_runner_Run(t *testing.T) { registry := mocks.Registry{ Client: client, } - return enginecontext.MakeContext(apis.NewBindings(), registry).WithTimeouts(v1alpha1.Timeouts{ + return enginecontext.MakeContext(clock.RealClock{}, apis.NewBindings(), registry).WithTimeouts(v1alpha1.Timeouts{ Apply: &config.Spec.Timeouts.Apply, Assert: &config.Spec.Timeouts.Assert, Cleanup: &config.Spec.Timeouts.Cleanup, @@ -408,7 +408,7 @@ func Test_runner_run(t *testing.T) { r := &runner{ clock: clock.RealClock{}, } - err := r.run(context.TODO(), tt.m, v1alpha2.NamespaceOptions{}, enginecontext.EmptyContext(), tt.tests...) + err := r.run(context.TODO(), tt.m, v1alpha2.NamespaceOptions{}, enginecontext.EmptyContext(clock.RealClock{}), tt.tests...) if tt.wantErr { assert.Error(t, err) } else { diff --git a/pkg/runner/step_test.go b/pkg/runner/step_test.go index fe267d59f..802043282 100644 --- a/pkg/runner/step_test.go +++ b/pkg/runner/step_test.go @@ -21,6 +21,7 @@ import ( kerror "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/utils/clock" "k8s.io/utils/ptr" ) @@ -786,7 +787,7 @@ func TestStepProcessor_Run(t *testing.T) { nt := &testing.MockT{} ctx := context.Background() ctx = logging.WithLogger(ctx, &fakeLogger.Logger{}) - tcontext := enginecontext.MakeContext(apis.NewBindings(), registry).WithTimeouts(v1alpha1.Timeouts{ + tcontext := enginecontext.MakeContext(clock.RealClock{}, apis.NewBindings(), registry).WithTimeouts(v1alpha1.Timeouts{ Apply: &config.Spec.Timeouts.Apply, Assert: &config.Spec.Timeouts.Assert, Cleanup: &config.Spec.Timeouts.Cleanup,