From b5b6134fd0935e1ebc2d49cdf8e890dbba26b0f5 Mon Sep 17 00:00:00 2001 From: Avi Biton Date: Thu, 18 Jul 2024 18:19:38 +0300 Subject: [PATCH] add unit tests to pipelinerun_helper Add unit tests to pipelinerun helper Signed-off-by: Avi Biton --- go.mod | 5 +- go.sum | 7 - .../controller/pipelinerun_helper_test.go | 496 ++++++++++++++++++ internal/controller/suite_test.go | 26 +- 4 files changed, 521 insertions(+), 13 deletions(-) create mode 100644 internal/controller/pipelinerun_helper_test.go diff --git a/go.mod b/go.mod index 9a5e852..61780fd 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/konflux-ci/notification-service go 1.21.9 require ( + github.com/aws/aws-sdk-go-v2 v1.30.3 github.com/go-logr/logr v1.4.2 github.com/konflux-ci/operator-toolkit v0.0.0-20240402130556-ef6dcbeca69d github.com/onsi/ginkgo/v2 v2.19.0 @@ -15,7 +16,6 @@ require ( ) require ( - github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.26 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect @@ -27,17 +27,14 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/aws/smithy-go v1.20.3 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect ) require ( contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/aws/aws-sdk-go v1.54.19 github.com/aws/aws-sdk-go-v2/config v1.27.26 github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 - github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 github.com/beorn7/perks v1.0.1 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect diff --git a/go.sum b/go.sum index 46b2adc..1d5bf3d 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,6 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8V github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= -github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/config v1.27.26 h1:T1kAefbKuNum/AbShMsZEro6eRkeOT8YILfE9wyjAYQ= @@ -68,8 +66,6 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrx github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 h1:eSTEdxkfle2G98FE+Xl3db/XAXXVTJPNQo9K/Ar8oAI= github.com/aws/aws-sdk-go-v2/service/sns v1.31.3/go.mod h1:1dn0delSO3J69THuty5iwP0US2Glt0mx2qBBlI13pvw= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz7y/WXbN3RgOoVrc= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI= github.com/aws/aws-sdk-go-v2/service/sso v1.22.3 h1:Fv1vD2L65Jnp5QRsdiM64JvUM4Xe+E0JyVsRQKv6IeA= github.com/aws/aws-sdk-go-v2/service/sso v1.22.3/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= @@ -233,9 +229,6 @@ github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= diff --git a/internal/controller/pipelinerun_helper_test.go b/internal/controller/pipelinerun_helper_test.go new file mode 100644 index 0000000..131ae15 --- /dev/null +++ b/internal/controller/pipelinerun_helper_test.go @@ -0,0 +1,496 @@ +package controller + +import ( + "encoding/json" + "time" + + "github.com/konflux-ci/operator-toolkit/metadata" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "knative.dev/pkg/apis" + v1 "knative.dev/pkg/apis/duck/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +var _ = Describe("Unit testing for pipelinerun_helper", func() { + var ( + testTruePipelineRun, + testFalsePipelineRun, + testErrMmarshelPipelineRun, + testResourcesPipelineRun *tektonv1.PipelineRun + notPipelineRun *tektonv1.TaskRun + ) + const ( + timeout = time.Second * 10 + interval = time.Millisecond * 250 + pushPipelineRunName = "push-pipelinerun-sample-unit-test" + testResourcesPipelineRunName = "test-resources-pipelinerun-unit-test" + namespace = "default" + ) + + testPipelineLookupKey := types.NamespacedName{Name: testResourcesPipelineRunName, Namespace: namespace} + testPipelineRun := &tektonv1.PipelineRun{} + + Describe("Push Pipelinerun; Ended; with finalizer, annotation and results", func() { + testTruePipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: pushPipelineRunName, + Namespace: namespace, + Labels: map[string]string{ + PipelineRunTypeLabel: PushPipelineRunTypeValue, + }, + Finalizers: []string{ + NotificationPipelineRunFinalizer, + }, + Annotations: map[string]string{ + NotificationPipelineRunAnnotation: NotificationPipelineRunAnnotationValue, + }, + }, + Spec: tektonv1.PipelineRunSpec{ + PipelineRef: &tektonv1.PipelineRef{}, + }, + Status: tektonv1.PipelineRunStatus{ + PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Now()}, + CompletionTime: &metav1.Time{Time: time.Now().Add(5 * time.Minute)}, + Results: []tektonv1.PipelineRunResult{ + { + Name: "IMAGE_DIGEST", + Value: *tektonv1.NewStructuredValues("image_digest_value"), + }, + { + Name: "IMAGE_URL", + Value: *tektonv1.NewStructuredValues("image"), + }, + { + Name: "CHAINS-GIT_URL", + Value: *tektonv1.NewStructuredValues("git_url_value"), + }, + { + Name: "CHAINS-GIT_COMMIT", + Value: *tektonv1.NewStructuredValues("git_commit_value"), + }, + }, + }, + Status: v1.Status{ + Conditions: v1.Conditions{ + apis.Condition{ + Message: "Tasks Completed: 12 (Failed: 0, Cancelled 0), Skipped: 2", + Reason: "Completed", + Status: "True", + Type: apis.ConditionSucceeded, + }, + }, + }, + }, + } + Context("when a push pipelinerun ends successfully and has results", func() { + It("IsPushPipelineRun should return true", func() { + got := IsPushPipelineRun(testTruePipelineRun) + Expect(got).To(BeTrue()) + }) + It("IsAnnotationExistInPipelineRun should return true", func() { + got := IsAnnotationExistInPipelineRun(testTruePipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(got).To(BeTrue()) + }) + It("IsFinalizerExistInPipelineRun should return true", func() { + got := IsFinalizerExistInPipelineRun(testTruePipelineRun, NotificationPipelineRunFinalizer) + Expect(got).To(BeTrue()) + }) + It("IsPipelineRunEnded should return true", func() { + got := IsPipelineRunEnded(testTruePipelineRun) + Expect(got).To(BeTrue()) + }) + It("IsPipelineRunEndedSuccessfully should return true", func() { + got := IsPipelineRunEndedSuccessfully(testTruePipelineRun) + Expect(got).To(BeTrue()) + }) + It("GetResultsFromPipelineRun should extract the result and add it the pipelinerunName", func() { + got, err := GetResultsFromPipelineRun(testTruePipelineRun) + Expect(err).ToNot(HaveOccurred()) + result := []tektonv1.PipelineRunResult{ + { + Name: "PIPELINERUN_NAME", + Value: *tektonv1.NewStructuredValues(testTruePipelineRun.Name), + }, + { + Name: "IMAGE_DIGEST", + Value: *tektonv1.NewStructuredValues("image_digest_value"), + }, + { + Name: "IMAGE_URL", + Value: *tektonv1.NewStructuredValues("image"), + }, + { + Name: "CHAINS-GIT_URL", + Value: *tektonv1.NewStructuredValues("git_url_value"), + }, + { + Name: "CHAINS-GIT_COMMIT", + Value: *tektonv1.NewStructuredValues("git_commit_value"), + }, + } + expectedResult, err := json.Marshal(result) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(expectedResult)) + }) + }) + }) + Describe("Pull_request Pipelinerun; Not Ended; without finalizer, annotation or results", func() { + testFalsePipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPullRequestPipelineRun", + Namespace: namespace, + Labels: map[string]string{ + PipelineRunTypeLabel: "pull_request", + }, + Finalizers: []string{ + "test.appstudio.openshift.io/pipelinerun", + }, + Annotations: map[string]string{ + "dummy_annotation": "false", + }, + }, + Spec: tektonv1.PipelineRunSpec{ + PipelineRef: &tektonv1.PipelineRef{}, + }, + Status: tektonv1.PipelineRunStatus{ + PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Now()}, + CompletionTime: &metav1.Time{Time: time.Now().Add(5 * time.Minute)}, + }, + Status: v1.Status{ + Conditions: v1.Conditions{ + apis.Condition{ + Message: "Tasks Completed: 3 (Failed: 0, Cancelled 0), Incomplete: 10, Skipped:1", + Reason: "Running", + Status: "Unknown", + Type: apis.ConditionSucceeded, + }, + }, + }, + }, + } + Context("when a pull_request pipelinerun is created, Still running and has no results", func() { + It("IsPushPipelineRun should return false", func() { + got := IsPushPipelineRun(testFalsePipelineRun) + Expect(got).To(BeFalse()) + }) + It("IsAnnotationExistInPipelineRun shouldreturn false", func() { + got := IsAnnotationExistInPipelineRun(testFalsePipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(got).To(BeFalse()) + }) + It("IsFinalizerExistInPipelineRun should return false", func() { + got := IsFinalizerExistInPipelineRun(testFalsePipelineRun, NotificationPipelineRunFinalizer) + Expect(got).To(BeFalse()) + }) + It("IsPipelineRunEnded should return false", func() { + got := IsPipelineRunEnded(testFalsePipelineRun) + Expect(got).To(BeFalse()) + }) + It("IsPipelineRunEndedSuccessfully should return false", func() { + got := IsPipelineRunEndedSuccessfully(testFalsePipelineRun) + Expect(got).To(BeFalse()) + }) + It("GetResultsFromPipelineRun should add pipelinerun name to empty results", func() { + got, err := GetResultsFromPipelineRun(testFalsePipelineRun) + Expect(err).ToNot(HaveOccurred()) + result := []tektonv1.PipelineRunResult{ + { + Name: "PIPELINERUN_NAME", + Value: *tektonv1.NewStructuredValues(testFalsePipelineRun.Name), + }, + } + expectedResult, err := json.Marshal(result) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(expectedResult)) + }) + }) + }) + Describe("Not a pipelinerun object", func() { + notPipelineRun = &tektonv1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-taskrun-pass", + Namespace: namespace, + }, + Spec: tektonv1.TaskRunSpec{ + TaskRef: &tektonv1.TaskRef{ + Name: "test-taskrun-pass", + ResolverRef: tektonv1.ResolverRef{ + Resolver: "bundle", + Params: tektonv1.Params{ + {Name: "bundle", + Value: tektonv1.ParamValue{Type: "string", StringVal: "test/example:test"}, + }, + {Name: "name", + Value: tektonv1.ParamValue{Type: "string", StringVal: "test-task"}, + }, + }, + }, + }, + }, + } + Context("When non pipelinerun object created", func() { + It("IsPushPipelineRun should return false", func() { + got := IsPushPipelineRun(notPipelineRun) + Expect(got).To(BeFalse()) + }) + It("IsAnnotationExistInPipelineRun should return false", func() { + got := IsAnnotationExistInPipelineRun(notPipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(got).To(BeFalse()) + }) + It("IsFinalizerExistInPipelineRun should return false", func() { + got := IsFinalizerExistInPipelineRun(notPipelineRun, NotificationPipelineRunFinalizer) + Expect(got).To(BeFalse()) + }) + It("IsPipelineRunEnded should return false", func() { + got := IsPipelineRunEnded(notPipelineRun) + Expect(got).To(BeFalse()) + }) + It("IsPipelineRunEndedSuccessfully should return false", func() { + got := IsPipelineRunEndedSuccessfully(notPipelineRun) + Expect(got).To(BeFalse()) + }) + }) + }) + Describe("When results fails to marshal", func() { + testErrMmarshelPipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: pushPipelineRunName, + Namespace: notPipelineRun.Namespace, + Labels: map[string]string{ + PipelineRunTypeLabel: PushPipelineRunTypeValue, + }, + Finalizers: []string{ + NotificationPipelineRunFinalizer, + }, + Annotations: map[string]string{ + NotificationPipelineRunAnnotation: NotificationPipelineRunAnnotationValue, + }, + }, + Spec: tektonv1.PipelineRunSpec{ + PipelineRef: &tektonv1.PipelineRef{}, + }, + Status: tektonv1.PipelineRunStatus{ + PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Now()}, + CompletionTime: &metav1.Time{Time: time.Now().Add(5 * time.Minute)}, + Results: []tektonv1.PipelineRunResult{ + {}, + { + Name: "wrong key and value", + Value: tektonv1.ParamValue{Type: "test"}, + }, + }, + }, + Status: v1.Status{ + Conditions: v1.Conditions{ + apis.Condition{ + Message: "Tasks Completed: 12 (Failed: 0, Cancelled 0), Skipped: 2", + Reason: "Completed", + Status: "True", + Type: apis.ConditionSucceeded, + }, + }, + }, + }, + } + Context("when a push pipelinerun is created, and results cannot be marshaled", func() { + It("GetResultsFromPipelineRun should return an error", func() { + got, err := GetResultsFromPipelineRun(testErrMmarshelPipelineRun) + Expect(got).To(BeNil()) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("failed to marshel results from pipelinerun %s", testErrMmarshelPipelineRun.Name)) + }) + }) + }) + + Describe("Successfully adding/removing resources when not exist", func() { + BeforeEach(func() { + // Create a pull_request pipelinerun + testResourcesPipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: testResourcesPipelineRunName, + Namespace: namespace, + Labels: map[string]string{ + "pipelines.openshift.io/used-by": "build-cloud", + "pipelines.openshift.io/runtime": "nodejs", + "pipelines.openshift.io/strategy": "s2i", + "appstudio.openshift.io/component": "component-sample", + "appstudio.openshift.io/application": "aaa", + }, + }, + Spec: tektonv1.PipelineRunSpec{ + PipelineRef: &tektonv1.PipelineRef{}, + }, + Status: tektonv1.PipelineRunStatus{ + PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Now()}, + CompletionTime: &metav1.Time{Time: time.Now().Add(5 * time.Minute)}, + }, + Status: v1.Status{ + Conditions: v1.Conditions{ + apis.Condition{ + Message: "Tasks Completed: 3 (Failed: 0, Cancelled 0), Incomplete: 10, Skipped:1", + Reason: "Running", + Status: "Unknown", + Type: apis.ConditionSucceeded, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, testResourcesPipelineRun) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err = k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // Finalizer and annotation should not exists + Expect(controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer)).To(BeFalse()) + Expect(metadata.HasAnnotationWithValue(testPipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue)).To(BeFalse()) + }) + Context("Pipelinerun without finalizer and annotation", func() { + It("AddFinalizerToPipelineRun should succeed", func() { + err := AddFinalizerToPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunFinalizer) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer) + }, timeout, interval).Should(BeTrue()) + }) + It("AddAnnotationToPipelineRun should succeed", func() { + err := AddAnnotationToPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return metadata.HasAnnotationWithValue(testPipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + }, timeout, interval).Should(BeTrue()) + }) + It("RemoveFinalizerFromPipelineRun should succeed if finalizer does not exist", func() { + err := RemoveFinalizerFromPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunFinalizer) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return !controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer) + }, timeout, interval).Should(BeTrue()) + }) + }) + AfterEach(func() { + err := k8sClient.Delete(ctx, testPipelineRun) + Expect(err == nil || errors.IsNotFound(err)).To(BeTrue()) + }) + }) + Describe("Successfully adding/removing resources when exist", func() { + BeforeEach(func() { + // Create a pull_request pipelinerun + testResourcesPipelineRun = &tektonv1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: testResourcesPipelineRunName, + Namespace: namespace, + Labels: map[string]string{ + "pipelines.openshift.io/used-by": "build-cloud", + "pipelines.openshift.io/runtime": "nodejs", + "pipelines.openshift.io/strategy": "s2i", + "appstudio.openshift.io/component": "component-sample", + "appstudio.openshift.io/application": "aaa", + }, + Finalizers: []string{ + NotificationPipelineRunFinalizer, + }, + Annotations: map[string]string{ + NotificationPipelineRunAnnotation: NotificationPipelineRunAnnotationValue, + }, + }, + Spec: tektonv1.PipelineRunSpec{ + PipelineRef: &tektonv1.PipelineRef{}, + }, + Status: tektonv1.PipelineRunStatus{ + PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Now()}, + CompletionTime: &metav1.Time{Time: time.Now().Add(5 * time.Minute)}, + }, + Status: v1.Status{ + Conditions: v1.Conditions{ + apis.Condition{ + Message: "Tasks Completed: 3 (Failed: 0, Cancelled 0), Incomplete: 10, Skipped:1", + Reason: "Running", + Status: "Unknown", + Type: apis.ConditionSucceeded, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, testResourcesPipelineRun) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err = k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + return err == nil + }, timeout, interval).Should(BeTrue()) + + // Finalizer and annotation should exist + Expect(controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer)).To(BeTrue()) + Expect(metadata.HasAnnotationWithValue(testPipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue)).To(BeTrue()) + }) + Context("Pipelinerun with finalizer and annotation", func() { + It("AddFinalizerToPipelineRun should succeed if finalizer exists", func() { + err := AddFinalizerToPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunFinalizer) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer) + }, timeout, interval).Should(BeTrue()) + }) + It("AddAnnotationToPipelineRun should succeed if annotation exists", func() { + err := AddAnnotationToPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return metadata.HasAnnotationWithValue(testPipelineRun, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + }, timeout, interval).Should(BeTrue()) + }) + It("RemoveFinalizerFromPipelineRun should succeed", func() { + err := RemoveFinalizerFromPipelineRun(ctx, testPipelineRun, nsr, NotificationPipelineRunFinalizer) + Expect(err).ToNot(HaveOccurred()) + Eventually(func() bool { + err := k8sClient.Get(ctx, testPipelineLookupKey, testPipelineRun) + Expect(err).ToNot(HaveOccurred()) + return !controllerutil.ContainsFinalizer(testPipelineRun, NotificationPipelineRunFinalizer) + }, timeout, interval).Should(BeTrue()) + }) + }) + AfterEach(func() { + err := k8sClient.Delete(ctx, testPipelineRun) + Expect(err == nil || errors.IsNotFound(err)).To(BeTrue()) + }) + }) + Describe("Test errors", func() { + Context("When Patch action returns an error", func() { + It("AddFinalizerToPipelineRun should return an error", func() { + err := AddFinalizerToPipelineRun(ctx, testPipelineRun, fakeErrorNsr, NotificationPipelineRunFinalizer) + Expect(err).To(HaveOccurred()) + }) + It("AddAnnotationToPipelineRun should return an error", func() { + err := AddAnnotationToPipelineRun(ctx, testPipelineRun, fakeErrorNsr, NotificationPipelineRunAnnotation, NotificationPipelineRunAnnotationValue) + Expect(err).To(HaveOccurred()) + }) + It("RemoveFinalizerFromPipelineRun should return an error", func() { + err := RemoveFinalizerFromPipelineRun(ctx, testPipelineRun, fakeErrorNsr, NotificationPipelineRunFinalizer) + Expect(err).To(HaveOccurred()) + }) + }) + }) +}) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 5da86c8..e1ec044 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -18,6 +18,7 @@ package controller import ( "context" + "errors" "fmt" "go/build" "path/filepath" @@ -49,6 +50,8 @@ var testEnv *envtest.Environment var ctx context.Context var cancel context.CancelFunc var mn *MockNotifier +var nsr *NotificationServiceReconciler +var fakeErrorNsr *NotificationServiceReconciler func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -65,6 +68,18 @@ func (mn *MockNotifier) Notify(ctx context.Context, message string) error { return nil } +type clientMock struct { + client.Client + shouldThroughError bool +} + +func (p *clientMock) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + if p.shouldThroughError { + return errors.New("Failed to patch") + } + return nil +} + var _ = BeforeEach(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) ctx, cancel = context.WithCancel(context.TODO()) @@ -134,16 +149,23 @@ var _ = BeforeEach(func() { // Create a mock of notifier mn = &MockNotifier{} - nsr := &NotificationServiceReconciler{ + nsr = &NotificationServiceReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), Log: k8sManager.GetLogger(), Notifier: mn, } - err = nsr.SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + fakeErrorPatchClient := &clientMock{shouldThroughError: true} + fakeErrorNsr = &NotificationServiceReconciler{ + Client: fakeErrorPatchClient, + Scheme: k8sManager.GetScheme(), + Log: k8sManager.GetLogger(), + Notifier: mn, + } + go func() { defer GinkgoRecover() err = k8sManager.Start(ctx)