github.com/redhat-appstudio/release-service@v0.0.0-20240507143925-083712697924/controllers/utils/predicates/predicates_test.go (about)

     1  /*
     2  Copyright 2023 Red Hat Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package predicates
    18  
    19  import (
    20  	"time"
    21  
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  
    25  	"github.com/redhat-appstudio/release-service/api/v1alpha1"
    26  	"github.com/redhat-appstudio/release-service/metadata"
    27  	tektonutils "github.com/redhat-appstudio/release-service/tekton/utils"
    28  	corev1 "k8s.io/api/core/v1"
    29  	"sigs.k8s.io/controller-runtime/pkg/event"
    30  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    31  
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  )
    34  
    35  var _ = Describe("Predicates", Ordered, func() {
    36  
    37  	const (
    38  		namespace       = "default"
    39  		namespace2      = "other"
    40  		applicationName = "test-application"
    41  	)
    42  
    43  	Context("Working with ReleasePlans and ReleasePlanAdmissions", func() {
    44  		var releasePlan, releasePlanDiffApp, releasePlanDiffLabel,
    45  			releasePlanDiffTarget, releasePlanDiffStatus *v1alpha1.ReleasePlan
    46  		var releasePlanAdmission, releasePlanAdmissionDiffApps, releasePlanAdmissionDiffOrigin,
    47  			releasePlanAdmissionDiffStatus *v1alpha1.ReleasePlanAdmission
    48  		var instance predicate.Predicate
    49  
    50  		BeforeAll(func() {
    51  			releasePlan = &v1alpha1.ReleasePlan{
    52  				ObjectMeta: metav1.ObjectMeta{
    53  					Name:      "releaseplan",
    54  					Namespace: namespace,
    55  					Labels: map[string]string{
    56  						metadata.AutoReleaseLabel: "true",
    57  					},
    58  				},
    59  				Spec: v1alpha1.ReleasePlanSpec{
    60  					Application: applicationName,
    61  					Target:      namespace2,
    62  				},
    63  			}
    64  			releasePlanDiffApp = &v1alpha1.ReleasePlan{
    65  				ObjectMeta: metav1.ObjectMeta{
    66  					Name:      "releaseplan-app",
    67  					Namespace: namespace,
    68  					Labels: map[string]string{
    69  						metadata.AutoReleaseLabel: "true",
    70  					},
    71  				},
    72  				Spec: v1alpha1.ReleasePlanSpec{
    73  					Application: "diff",
    74  					Target:      namespace2,
    75  				},
    76  			}
    77  			releasePlanDiffLabel = &v1alpha1.ReleasePlan{
    78  				ObjectMeta: metav1.ObjectMeta{
    79  					Name:      "releaseplan-label",
    80  					Namespace: namespace,
    81  					Labels: map[string]string{
    82  						metadata.AutoReleaseLabel: "false",
    83  					},
    84  				},
    85  				Spec: v1alpha1.ReleasePlanSpec{
    86  					Application: applicationName,
    87  					Target:      namespace2,
    88  				},
    89  			}
    90  			releasePlanDiffTarget = &v1alpha1.ReleasePlan{
    91  				ObjectMeta: metav1.ObjectMeta{
    92  					Name:      "releaseplan-target",
    93  					Namespace: namespace,
    94  					Labels: map[string]string{
    95  						metadata.AutoReleaseLabel: "true",
    96  					},
    97  				},
    98  				Spec: v1alpha1.ReleasePlanSpec{
    99  					Application: applicationName,
   100  					Target:      "diff",
   101  				},
   102  			}
   103  			releasePlanDiffStatus = &v1alpha1.ReleasePlan{
   104  				ObjectMeta: metav1.ObjectMeta{
   105  					Name:      "releaseplan-status",
   106  					Namespace: namespace,
   107  					Labels: map[string]string{
   108  						metadata.AutoReleaseLabel: "true",
   109  					},
   110  				},
   111  				Spec: v1alpha1.ReleasePlanSpec{
   112  					Application: applicationName,
   113  					Target:      namespace2,
   114  				},
   115  			}
   116  			releasePlanDiffStatus.MarkMatched(&v1alpha1.ReleasePlanAdmission{})
   117  
   118  			releasePlanAdmission = &v1alpha1.ReleasePlanAdmission{
   119  				ObjectMeta: metav1.ObjectMeta{
   120  					Name:      "releaseplanadmission",
   121  					Namespace: namespace,
   122  					Labels: map[string]string{
   123  						metadata.AutoReleaseLabel: "true",
   124  					},
   125  				},
   126  				Spec: v1alpha1.ReleasePlanAdmissionSpec{
   127  					Applications: []string{
   128  						applicationName,
   129  					},
   130  					Origin: namespace2,
   131  					Pipeline: &tektonutils.Pipeline{
   132  						PipelineRef: tektonutils.PipelineRef{
   133  							Resolver: "bundles",
   134  							Params: []tektonutils.Param{
   135  								{Name: "bundle", Value: "quay.io/some/bundle"},
   136  								{Name: "name", Value: "release-pipeline"},
   137  								{Name: "kind", Value: "pipeline"},
   138  							},
   139  						},
   140  					},
   141  					Policy: "policy",
   142  				},
   143  			}
   144  			releasePlanAdmissionDiffApps = &v1alpha1.ReleasePlanAdmission{
   145  				ObjectMeta: metav1.ObjectMeta{
   146  					Name:      "releaseplanadmission-app",
   147  					Namespace: namespace,
   148  					Labels: map[string]string{
   149  						metadata.AutoReleaseLabel: "true",
   150  					},
   151  				},
   152  				Spec: v1alpha1.ReleasePlanAdmissionSpec{
   153  					Applications: []string{
   154  						"diff",
   155  					},
   156  					Origin: namespace2,
   157  					Pipeline: &tektonutils.Pipeline{
   158  						PipelineRef: tektonutils.PipelineRef{
   159  							Resolver: "bundles",
   160  							Params: []tektonutils.Param{
   161  								{Name: "bundle", Value: "quay.io/some/bundle"},
   162  								{Name: "name", Value: "release-pipeline"},
   163  								{Name: "kind", Value: "pipeline"},
   164  							},
   165  						},
   166  					},
   167  					Policy: "policy",
   168  				},
   169  			}
   170  			releasePlanAdmissionDiffOrigin = &v1alpha1.ReleasePlanAdmission{
   171  				ObjectMeta: metav1.ObjectMeta{
   172  					Name:      "releaseplanadmission-origin",
   173  					Namespace: namespace,
   174  					Labels: map[string]string{
   175  						metadata.AutoReleaseLabel: "true",
   176  					},
   177  				},
   178  				Spec: v1alpha1.ReleasePlanAdmissionSpec{
   179  					Applications: []string{
   180  						applicationName,
   181  					},
   182  					Origin: "diff",
   183  					Pipeline: &tektonutils.Pipeline{
   184  						PipelineRef: tektonutils.PipelineRef{
   185  							Resolver: "bundles",
   186  							Params: []tektonutils.Param{
   187  								{Name: "bundle", Value: "quay.io/some/bundle"},
   188  								{Name: "name", Value: "release-pipeline"},
   189  								{Name: "kind", Value: "pipeline"},
   190  							},
   191  						},
   192  					},
   193  					Policy: "policy",
   194  				},
   195  			}
   196  			releasePlanAdmissionDiffStatus = &v1alpha1.ReleasePlanAdmission{
   197  				ObjectMeta: metav1.ObjectMeta{
   198  					Name:      "releaseplanadmission-status",
   199  					Namespace: namespace,
   200  					Labels: map[string]string{
   201  						metadata.AutoReleaseLabel: "true",
   202  					},
   203  				},
   204  				Spec: v1alpha1.ReleasePlanAdmissionSpec{
   205  					Applications: []string{
   206  						applicationName,
   207  					},
   208  					Origin: namespace2,
   209  					Pipeline: &tektonutils.Pipeline{
   210  						PipelineRef: tektonutils.PipelineRef{
   211  							Resolver: "bundles",
   212  							Params: []tektonutils.Param{
   213  								{Name: "bundle", Value: "quay.io/some/bundle"},
   214  								{Name: "name", Value: "release-pipeline"},
   215  								{Name: "kind", Value: "pipeline"},
   216  							},
   217  						},
   218  					},
   219  					Policy: "policy",
   220  				},
   221  			}
   222  			releasePlanAdmissionDiffStatus.MarkMatched(&v1alpha1.ReleasePlan{})
   223  
   224  			instance = MatchPredicate()
   225  		})
   226  
   227  		When("calling MatchPredicate", func() {
   228  			It("returns true for creating events", func() {
   229  				contextEvent := event.CreateEvent{
   230  					Object: releasePlan,
   231  				}
   232  				Expect(instance.Create(contextEvent)).To(BeTrue())
   233  			})
   234  
   235  			It("returns true for deleting events", func() {
   236  				contextEvent := event.DeleteEvent{
   237  					Object: releasePlan,
   238  				}
   239  				Expect(instance.Delete(contextEvent)).To(BeTrue())
   240  			})
   241  
   242  			It("should ignore generic events", func() {
   243  				contextEvent := event.GenericEvent{
   244  					Object: releasePlan,
   245  				}
   246  				Expect(instance.Generic(contextEvent)).To(BeFalse())
   247  			})
   248  
   249  			It("returns true when the application changes between ReleasePlans", func() {
   250  				contextEvent := event.UpdateEvent{
   251  					ObjectOld: releasePlan,
   252  					ObjectNew: releasePlanDiffApp,
   253  				}
   254  				Expect(instance.Update(contextEvent)).To(BeTrue())
   255  			})
   256  
   257  			It("returns true when the applications change between ReleasePlanAdmissions", func() {
   258  				contextEvent := event.UpdateEvent{
   259  					ObjectOld: releasePlanAdmission,
   260  					ObjectNew: releasePlanAdmissionDiffApps,
   261  				}
   262  				Expect(instance.Update(contextEvent)).To(BeTrue())
   263  			})
   264  
   265  			It("returns true when the auto-release label changes", func() {
   266  				contextEvent := event.UpdateEvent{
   267  					ObjectOld: releasePlan,
   268  					ObjectNew: releasePlanDiffLabel,
   269  				}
   270  				Expect(instance.Update(contextEvent)).To(BeTrue())
   271  			})
   272  
   273  			It("returns true when the target changes between ReleasePlans", func() {
   274  				contextEvent := event.UpdateEvent{
   275  					ObjectOld: releasePlan,
   276  					ObjectNew: releasePlanDiffTarget,
   277  				}
   278  				Expect(instance.Update(contextEvent)).To(BeTrue())
   279  			})
   280  
   281  			It("returns true when the origin changes between ReleasePlanAdmissions", func() {
   282  				contextEvent := event.UpdateEvent{
   283  					ObjectOld: releasePlanAdmission,
   284  					ObjectNew: releasePlanAdmissionDiffOrigin,
   285  				}
   286  				Expect(instance.Update(contextEvent)).To(BeTrue())
   287  			})
   288  
   289  			It("returns true when the matched condition in the status changes", func() {
   290  				contextEvent := event.UpdateEvent{
   291  					ObjectOld: releasePlan,
   292  					ObjectNew: releasePlanDiffStatus,
   293  				}
   294  				Expect(instance.Update(contextEvent)).To(BeTrue())
   295  			})
   296  		})
   297  
   298  		When("calling haveApplicationsChanged", func() {
   299  			It("returns true when the application has changed between ReleasePlans", func() {
   300  				Expect(haveApplicationsChanged(releasePlan, releasePlanDiffApp)).To(BeTrue())
   301  			})
   302  
   303  			It("returns true when the applications have changed between ReleasePlanAdmissions", func() {
   304  				Expect(haveApplicationsChanged(releasePlanAdmission, releasePlanAdmissionDiffApps)).To(BeTrue())
   305  			})
   306  
   307  			It("returns false when the application has not changed between ReleasePlans", func() {
   308  				Expect(haveApplicationsChanged(releasePlan, releasePlanDiffTarget)).To(BeFalse())
   309  			})
   310  
   311  			It("returns false when the applications have not changed between ReleasePlanAdmissions", func() {
   312  				Expect(haveApplicationsChanged(releasePlanAdmission, releasePlanAdmissionDiffOrigin)).To(BeFalse())
   313  			})
   314  		})
   315  
   316  		When("calling hasSourceChanged", func() {
   317  			It("returns true when the target has changed between ReleasePlans", func() {
   318  				Expect(hasSourceChanged(releasePlan, releasePlanDiffTarget)).To(BeTrue())
   319  			})
   320  
   321  			It("returns true when the origin has changed between ReleasePlanAdmissions", func() {
   322  				Expect(hasSourceChanged(releasePlanAdmission, releasePlanAdmissionDiffOrigin)).To(BeTrue())
   323  			})
   324  
   325  			It("returns false when the target has not changed between ReleasePlans", func() {
   326  				Expect(hasSourceChanged(releasePlan, releasePlanDiffApp)).To(BeFalse())
   327  			})
   328  
   329  			It("returns false when the target has not changed between ReleasePlanAdmissions", func() {
   330  				Expect(hasSourceChanged(releasePlanAdmission, releasePlanAdmissionDiffApps)).To(BeFalse())
   331  			})
   332  		})
   333  
   334  		When("calling hasMatchConditionChanged", func() {
   335  			It("returns true when the ReleasePlans with differing lastTransitionTimes are passed", func() {
   336  				Expect(hasMatchConditionChanged(releasePlan, releasePlanDiffStatus)).To(BeTrue())
   337  			})
   338  
   339  			It("returns true when the ReleasePlanAdmissions with differing lastTransitionTimes are passed", func() {
   340  				Expect(hasMatchConditionChanged(releasePlanAdmission, releasePlanAdmissionDiffStatus)).To(BeTrue())
   341  			})
   342  
   343  			It("returns false when the ReleasePlans with the same lastTransitionTimes are passed", func() {
   344  				Expect(hasMatchConditionChanged(releasePlanDiffStatus, releasePlanDiffStatus)).To(BeFalse())
   345  			})
   346  
   347  			It("returns false when the ReleasePlanAdmissions with the same lastTransitionTimes are passed", func() {
   348  				Expect(hasMatchConditionChanged(releasePlanAdmissionDiffStatus, releasePlanAdmissionDiffStatus)).To(BeFalse())
   349  			})
   350  
   351  			It("returns false when objects of different types are passed", func() {
   352  				Expect(hasMatchConditionChanged(releasePlanDiffStatus, releasePlanAdmissionDiffStatus)).To(BeFalse())
   353  			})
   354  		})
   355  	})
   356  
   357  	When("calling hasConditionChanged", func() {
   358  		It("returns false when both conditions are nil", func() {
   359  			Expect(hasConditionChanged(nil, nil)).To(BeFalse())
   360  		})
   361  
   362  		It("returns true when just the first condition is nil", func() {
   363  			condition := &metav1.Condition{}
   364  			Expect(hasConditionChanged(condition, nil)).To(BeTrue())
   365  		})
   366  
   367  		It("returns true when just the second condition is nil", func() {
   368  			condition := &metav1.Condition{}
   369  			Expect(hasConditionChanged(nil, condition)).To(BeTrue())
   370  		})
   371  
   372  		It("returns false when the conditions have the same lastTransitionTime", func() {
   373  			transitionTime := metav1.Time{Time: time.Now()}
   374  			condition1 := &metav1.Condition{LastTransitionTime: transitionTime}
   375  			condition2 := &metav1.Condition{LastTransitionTime: transitionTime}
   376  			Expect(hasConditionChanged(condition1, condition2)).To(BeFalse())
   377  		})
   378  
   379  		It("returns true when the conditions have different lastTransitionTimes", func() {
   380  			transitionTime := metav1.Time{Time: time.Now()}
   381  			condition1 := &metav1.Condition{LastTransitionTime: transitionTime}
   382  			condition2 := &metav1.Condition{LastTransitionTime: metav1.Time{Time: transitionTime.Add(time.Minute)}}
   383  			Expect(hasConditionChanged(condition1, condition2)).To(BeTrue())
   384  		})
   385  	})
   386  
   387  	When("calling hasAutoReleaseLabelChanged", func() {
   388  		var podTrue, podFalse, podMissing *corev1.Pod
   389  
   390  		BeforeAll(func() {
   391  			podTrue = &corev1.Pod{
   392  				ObjectMeta: metav1.ObjectMeta{
   393  					Annotations: nil,
   394  					Labels: map[string]string{
   395  						metadata.AutoReleaseLabel: "true",
   396  					},
   397  				},
   398  			}
   399  			podFalse = &corev1.Pod{
   400  				ObjectMeta: metav1.ObjectMeta{
   401  					Annotations: nil,
   402  					Labels: map[string]string{
   403  						metadata.AutoReleaseLabel: "false",
   404  					},
   405  				},
   406  			}
   407  			podMissing = &corev1.Pod{
   408  				ObjectMeta: metav1.ObjectMeta{
   409  					Annotations: nil,
   410  					Labels:      nil,
   411  				},
   412  			}
   413  		})
   414  
   415  		It("returns true when the first object has no labels", func() {
   416  			Expect(hasAutoReleaseLabelChanged(podMissing, podTrue)).To(BeTrue())
   417  		})
   418  
   419  		It("returns true when the second object has no labels", func() {
   420  			Expect(hasAutoReleaseLabelChanged(podTrue, podFalse)).To(BeTrue())
   421  		})
   422  
   423  		It("returns true when the first object has a true label and second has a false label", func() {
   424  			Expect(hasAutoReleaseLabelChanged(podTrue, podFalse)).To(BeTrue())
   425  		})
   426  
   427  		It("returns true when the first object has a false label and second has a true label", func() {
   428  			Expect(hasAutoReleaseLabelChanged(podFalse, podTrue)).To(BeTrue())
   429  		})
   430  
   431  		It("returns false when the both objects have the label set to false", func() {
   432  			Expect(hasAutoReleaseLabelChanged(podFalse, podFalse)).To(BeFalse())
   433  		})
   434  
   435  		It("returns false when the both objects have the label set to true", func() {
   436  			Expect(hasAutoReleaseLabelChanged(podTrue, podTrue)).To(BeFalse())
   437  		})
   438  
   439  		It("returns false when the both objects are missing the label", func() {
   440  			Expect(hasAutoReleaseLabelChanged(podMissing, podMissing)).To(BeFalse())
   441  		})
   442  	})
   443  })