sigs.k8s.io/cluster-api@v1.7.1/util/conditions/setter_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 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 conditions 18 19 import ( 20 "testing" 21 "time" 22 23 . "github.com/onsi/gomega" 24 "github.com/onsi/gomega/format" 25 "github.com/onsi/gomega/types" 26 "github.com/pkg/errors" 27 corev1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 30 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 31 ) 32 33 func TestHasSameState(t *testing.T) { 34 g := NewWithT(t) 35 36 // same condition 37 falseInfo2 := falseInfo1.DeepCopy() 38 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeTrue()) 39 40 // different LastTransitionTime does not impact state 41 falseInfo2 = falseInfo1.DeepCopy() 42 falseInfo2.LastTransitionTime = metav1.NewTime(time.Date(1900, time.November, 10, 23, 0, 0, 0, time.UTC)) 43 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeTrue()) 44 45 // different Type, Status, Reason, Severity and Message determine different state 46 falseInfo2 = falseInfo1.DeepCopy() 47 falseInfo2.Type = "another type" 48 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse()) 49 50 falseInfo2 = falseInfo1.DeepCopy() 51 falseInfo2.Status = corev1.ConditionTrue 52 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse()) 53 54 falseInfo2 = falseInfo1.DeepCopy() 55 falseInfo2.Severity = clusterv1.ConditionSeverityWarning 56 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse()) 57 58 falseInfo2 = falseInfo1.DeepCopy() 59 falseInfo2.Reason = "another severity" 60 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse()) 61 62 falseInfo2 = falseInfo1.DeepCopy() 63 falseInfo2.Message = "another message" 64 g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse()) 65 } 66 67 func TestLexicographicLess(t *testing.T) { 68 g := NewWithT(t) 69 70 // alphabetical order of Type is respected 71 a := TrueCondition("A") 72 b := TrueCondition("B") 73 g.Expect(lexicographicLess(a, b)).To(BeTrue()) 74 75 a = TrueCondition("B") 76 b = TrueCondition("A") 77 g.Expect(lexicographicLess(a, b)).To(BeFalse()) 78 79 // Ready condition is treated as an exception and always goes first 80 a = TrueCondition(clusterv1.ReadyCondition) 81 b = TrueCondition("A") 82 g.Expect(lexicographicLess(a, b)).To(BeTrue()) 83 84 a = TrueCondition("A") 85 b = TrueCondition(clusterv1.ReadyCondition) 86 g.Expect(lexicographicLess(a, b)).To(BeFalse()) 87 } 88 89 func TestSet(t *testing.T) { 90 a := TrueCondition("a") 91 b := TrueCondition("b") 92 ready := TrueCondition(clusterv1.ReadyCondition) 93 94 tests := []struct { 95 name string 96 to Setter 97 condition *clusterv1.Condition 98 want clusterv1.Conditions 99 }{ 100 { 101 name: "Set adds a condition", 102 to: setterWithConditions(), 103 condition: a, 104 want: conditionList(a), 105 }, 106 { 107 name: "Set adds more conditions", 108 to: setterWithConditions(a), 109 condition: b, 110 want: conditionList(a, b), 111 }, 112 { 113 name: "Set does not duplicate existing conditions", 114 to: setterWithConditions(a, b), 115 condition: a, 116 want: conditionList(a, b), 117 }, 118 { 119 name: "Set sorts conditions in lexicographic order", 120 to: setterWithConditions(b, a), 121 condition: ready, 122 want: conditionList(ready, a, b), 123 }, 124 } 125 126 for _, tt := range tests { 127 t.Run(tt.name, func(t *testing.T) { 128 g := NewWithT(t) 129 130 Set(tt.to, tt.condition) 131 132 g.Expect(tt.to.GetConditions()).To(haveSameConditionsOf(tt.want)) 133 }) 134 } 135 } 136 137 func TestSetLastTransitionTime(t *testing.T) { 138 x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC) 139 140 foo := FalseCondition("foo", "reason foo", clusterv1.ConditionSeverityInfo, "message foo") 141 fooWithLastTransitionTime := FalseCondition("foo", "reason foo", clusterv1.ConditionSeverityInfo, "message foo") 142 fooWithLastTransitionTime.LastTransitionTime = x 143 fooWithAnotherState := TrueCondition("foo") 144 145 tests := []struct { 146 name string 147 to Setter 148 new *clusterv1.Condition 149 LastTransitionTimeCheck func(*WithT, metav1.Time) 150 }{ 151 { 152 name: "Set a condition that does not exists should set the last transition time if not defined", 153 to: setterWithConditions(), 154 new: foo, 155 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) { 156 g.Expect(lastTransitionTime).ToNot(BeZero()) 157 }, 158 }, 159 { 160 name: "Set a condition that does not exists should preserve the last transition time if defined", 161 to: setterWithConditions(), 162 new: fooWithLastTransitionTime, 163 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) { 164 g.Expect(lastTransitionTime).To(Equal(x)) 165 }, 166 }, 167 { 168 name: "Set a condition that already exists with the same state should preserves the last transition time", 169 to: setterWithConditions(fooWithLastTransitionTime), 170 new: foo, 171 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) { 172 g.Expect(lastTransitionTime).To(Equal(x)) 173 }, 174 }, 175 { 176 name: "Set a condition that already exists but with different state should changes the last transition time", 177 to: setterWithConditions(fooWithLastTransitionTime), 178 new: fooWithAnotherState, 179 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) { 180 g.Expect(lastTransitionTime).ToNot(Equal(x)) 181 }, 182 }, 183 } 184 185 for _, tt := range tests { 186 t.Run(tt.name, func(t *testing.T) { 187 g := NewWithT(t) 188 189 Set(tt.to, tt.new) 190 191 tt.LastTransitionTimeCheck(g, Get(tt.to, "foo").LastTransitionTime) 192 }) 193 } 194 } 195 196 func TestMarkMethods(t *testing.T) { 197 g := NewWithT(t) 198 199 cluster := &clusterv1.Cluster{} 200 201 // test MarkTrue 202 MarkTrue(cluster, "conditionFoo") 203 g.Expect(Get(cluster, "conditionFoo")).To(HaveSameStateOf(&clusterv1.Condition{ 204 Type: "conditionFoo", 205 Status: corev1.ConditionTrue, 206 })) 207 208 // test MarkFalse 209 MarkFalse(cluster, "conditionBar", "reasonBar", clusterv1.ConditionSeverityError, "messageBar") 210 g.Expect(Get(cluster, "conditionBar")).To(HaveSameStateOf(&clusterv1.Condition{ 211 Type: "conditionBar", 212 Status: corev1.ConditionFalse, 213 Severity: clusterv1.ConditionSeverityError, 214 Reason: "reasonBar", 215 Message: "messageBar", 216 })) 217 218 // test MarkUnknown 219 MarkUnknown(cluster, "conditionBaz", "reasonBaz", "messageBaz") 220 g.Expect(Get(cluster, "conditionBaz")).To(HaveSameStateOf(&clusterv1.Condition{ 221 Type: "conditionBaz", 222 Status: corev1.ConditionUnknown, 223 Reason: "reasonBaz", 224 Message: "messageBaz", 225 })) 226 } 227 228 func TestSetSummary(t *testing.T) { 229 g := NewWithT(t) 230 target := setterWithConditions(TrueCondition("foo")) 231 232 SetSummary(target) 233 234 g.Expect(Has(target, clusterv1.ReadyCondition)).To(BeTrue()) 235 } 236 237 func TestSetMirror(t *testing.T) { 238 g := NewWithT(t) 239 source := getterWithConditions(TrueCondition(clusterv1.ReadyCondition)) 240 target := setterWithConditions() 241 242 SetMirror(target, "foo", source) 243 244 g.Expect(Has(target, "foo")).To(BeTrue()) 245 } 246 247 func TestSetAggregate(t *testing.T) { 248 g := NewWithT(t) 249 source1 := getterWithConditions(TrueCondition(clusterv1.ReadyCondition)) 250 source2 := getterWithConditions(TrueCondition(clusterv1.ReadyCondition)) 251 target := setterWithConditions() 252 253 SetAggregate(target, "foo", []Getter{source1, source2}) 254 255 g.Expect(Has(target, "foo")).To(BeTrue()) 256 } 257 258 func setterWithConditions(conditions ...*clusterv1.Condition) Setter { 259 obj := &clusterv1.Cluster{} 260 obj.SetConditions(conditionList(conditions...)) 261 return obj 262 } 263 264 func nilSetter() Setter { 265 var obj *clusterv1.Cluster 266 return obj 267 } 268 269 func haveSameConditionsOf(expected clusterv1.Conditions) types.GomegaMatcher { 270 return &ConditionsMatcher{ 271 Expected: expected, 272 } 273 } 274 275 type ConditionsMatcher struct { 276 Expected clusterv1.Conditions 277 } 278 279 func (matcher *ConditionsMatcher) Match(actual interface{}) (success bool, err error) { 280 actualConditions, ok := actual.(clusterv1.Conditions) 281 if !ok { 282 return false, errors.New("Value should be a conditions list") 283 } 284 285 if len(actualConditions) != len(matcher.Expected) { 286 return false, nil 287 } 288 289 for i := range actualConditions { 290 if !hasSameState(&actualConditions[i], &matcher.Expected[i]) { 291 return false, nil 292 } 293 } 294 return true, nil 295 } 296 297 func (matcher *ConditionsMatcher) FailureMessage(actual interface{}) (message string) { 298 return format.Message(actual, "to have the same conditions of", matcher.Expected) 299 } 300 func (matcher *ConditionsMatcher) NegatedFailureMessage(actual interface{}) (message string) { 301 return format.Message(actual, "not to have the same conditions of", matcher.Expected) 302 }