1 package conditions
2
3 import (
4 "testing"
5 "time"
6
7 . "github.com/onsi/gomega"
8 "github.com/onsi/gomega/format"
9 "github.com/onsi/gomega/types"
10 "github.com/pkg/errors"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12
13 "edge-infra.dev/pkg/k8s/meta/status"
14 "edge-infra.dev/pkg/k8s/object/fobject"
15 )
16
17 func TestHasSameState(t *testing.T) {
18 g := NewWithT(t)
19
20
21 true2 := true1.DeepCopy()
22 g.Expect(hasSameState(true1, true2)).To(BeTrue())
23
24
25 true2 = true1.DeepCopy()
26 true2.LastTransitionTime = metav1.NewTime(time.Date(1900, time.November, 10, 23, 0, 0, 0, time.UTC))
27 g.Expect(hasSameState(true1, true2)).To(BeTrue())
28
29
30 true2 = true1.DeepCopy()
31 true2.ObservedGeneration = 1
32 g.Expect(hasSameState(true1, true2)).To(BeTrue())
33
34
35
36 true2 = true1.DeepCopy()
37 true2.Type = "another type"
38 g.Expect(hasSameState(true1, true2)).To(BeFalse())
39
40 true2 = true1.DeepCopy()
41 true2.Status = metav1.ConditionFalse
42 g.Expect(hasSameState(true1, true2)).To(BeFalse())
43
44 true2 = true1.DeepCopy()
45 true2.Message = "another message"
46 g.Expect(hasSameState(true1, true2)).To(BeFalse())
47 }
48
49 func TestLexicographicLess(t *testing.T) {
50 g := NewWithT(t)
51
52
53 a := TrueCondition("A", "", "")
54 b := TrueCondition("B", "", "")
55 g.Expect(lexicographicLess(a, b)).To(BeTrue())
56
57 a = TrueCondition("B", "", "")
58 b = TrueCondition("A", "", "")
59 g.Expect(lexicographicLess(a, b)).To(BeFalse())
60
61
62 a = TrueCondition("A", "", "")
63 a.ObservedGeneration = 2
64 b = TrueCondition("B", "", "")
65 b.ObservedGeneration = 2
66 g.Expect(lexicographicLess(a, b)).To(BeTrue())
67
68 a = TrueCondition("A", "", "")
69 a.ObservedGeneration = 1
70 b = TrueCondition("B", "", "")
71 b.ObservedGeneration = 2
72 g.Expect(lexicographicLess(a, b)).To(BeFalse())
73
74 a = TrueCondition("A", "", "")
75 a.ObservedGeneration = 1
76 b = TrueCondition("B", "", "")
77 b.ObservedGeneration = 0
78 g.Expect(lexicographicLess(a, b)).To(BeTrue())
79
80
81 c := TrueCondition("C", "", "")
82 c.ObservedGeneration = 1
83 b = TrueCondition("B", "", "")
84 b.ObservedGeneration = 0
85 g.Expect(lexicographicLess(c, b)).To(BeTrue())
86
87
88
89 stalled := TrueCondition(status.StalledCondition, "", "")
90 ready := FalseCondition(status.ReadyCondition, "", "")
91 reconciling := TrueCondition(status.ReconcilingCondition, "", "")
92
93 g.Expect(lexicographicLess(stalled, ready)).To(BeTrue())
94 g.Expect(lexicographicLess(ready, stalled)).To(BeFalse())
95
96 g.Expect(lexicographicLess(reconciling, ready)).To(BeTrue())
97 g.Expect(lexicographicLess(ready, reconciling)).To(BeFalse())
98
99 g.Expect(lexicographicLess(stalled, reconciling)).To(BeTrue())
100 g.Expect(lexicographicLess(reconciling, stalled)).To(BeFalse())
101
102 g.Expect(lexicographicLess(ready, b)).To(BeTrue())
103 g.Expect(lexicographicLess(b, ready)).To(BeFalse())
104
105 ready.ObservedGeneration = 1
106 b.ObservedGeneration = 2
107 g.Expect(lexicographicLess(ready, b)).To(BeTrue())
108 }
109
110 func TestSet(t *testing.T) {
111 a := TrueCondition("a", "", "")
112 b := TrueCondition("b", "", "")
113 ready := TrueCondition(status.ReadyCondition, "", "")
114
115 tests := []struct {
116 name string
117 to Setter
118 condition *metav1.Condition
119 want []metav1.Condition
120 }{
121 {
122 name: "Set adds a condition",
123 to: setterWithConditions(),
124 condition: a,
125 want: conditionList(a),
126 },
127 {
128 name: "Set adds more conditions",
129 to: setterWithConditions(a),
130 condition: b,
131 want: conditionList(a, b),
132 },
133 {
134 name: "Set does not duplicate existing conditions",
135 to: setterWithConditions(a, b),
136 condition: a,
137 want: conditionList(a, b),
138 },
139 {
140 name: "Set sorts conditions in lexicographic order",
141 to: setterWithConditions(b, a),
142 condition: ready,
143 want: conditionList(ready, a, b),
144 },
145 }
146
147 for _, tt := range tests {
148 t.Run(tt.name, func(t *testing.T) {
149 g := NewWithT(t)
150
151 Set(tt.to, tt.condition)
152
153 g.Expect(tt.to.GetConditions()).To(haveSameConditionsOf(tt.want))
154 })
155 }
156 }
157
158 func TestSetLastTransitionTime(t *testing.T) {
159 x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC)
160
161 foo := FalseCondition("foo", "reason foo", "message foo")
162 fooWithLastTransitionTime := FalseCondition("foo", "reason foo", "message foo")
163 fooWithLastTransitionTime.LastTransitionTime = x
164 fooWithAnotherState := TrueCondition("foo", "", "")
165
166 tests := []struct {
167 name string
168 to Setter
169 new *metav1.Condition
170 LastTransitionTimeCheck func(*WithT, metav1.Time)
171 }{
172 {
173 name: "Set a condition that does not exists should set the last transition time if not defined",
174 to: setterWithConditions(),
175 new: foo,
176 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
177 g.Expect(lastTransitionTime).ToNot(BeZero())
178 },
179 },
180 {
181 name: "Set a condition that does not exists should preserve the last transition time if defined",
182 to: setterWithConditions(),
183 new: fooWithLastTransitionTime,
184 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
185 g.Expect(lastTransitionTime).To(Equal(x))
186 },
187 },
188 {
189 name: "Set a condition that already exists with the same state should preserves the last transition time",
190 to: setterWithConditions(fooWithLastTransitionTime),
191 new: foo,
192 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
193 g.Expect(lastTransitionTime).To(Equal(x))
194 },
195 },
196 {
197 name: "Set a condition that already exists but with different state should changes the last transition time",
198 to: setterWithConditions(fooWithLastTransitionTime),
199 new: fooWithAnotherState,
200 LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
201 g.Expect(lastTransitionTime).ToNot(Equal(x))
202 },
203 },
204 }
205
206 for _, tt := range tests {
207 t.Run(tt.name, func(t *testing.T) {
208 g := NewWithT(t)
209
210 Set(tt.to, tt.new)
211
212 tt.LastTransitionTimeCheck(g, Get(tt.to, "foo").LastTransitionTime)
213 })
214 }
215 }
216
217 func TestSetObservedGeneration(t *testing.T) {
218 g := NewWithT(t)
219
220 obj := &fobject.Fake{}
221 x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC)
222
223
224 foo1 := FalseCondition("foo1", "reasonFoo1", "messageFoo1")
225 foo1.ObservedGeneration = 2
226 foo1.LastTransitionTime = x
227 foo2 := TrueCondition("foo2", "reasonFoo2", "messageFoo2")
228 foo2.ObservedGeneration = 3
229 foo2.LastTransitionTime = x
230
231
232 obj.Generation = 4
233 obj.SetConditions([]metav1.Condition{*foo1, *foo2})
234
235
236 g.Expect(Get(obj, "foo1").ObservedGeneration).To(BeEquivalentTo(2))
237 g.Expect(Get(obj, "foo2").ObservedGeneration).To(BeEquivalentTo(3))
238
239
240
241 obj.Generation = 5
242 Set(obj, foo1)
243 Set(obj, foo2)
244
245
246 g.Expect(Get(obj, "foo1").ObservedGeneration).To(BeEquivalentTo(5))
247 g.Expect(Get(obj, "foo1").LastTransitionTime).To(Equal(x))
248 g.Expect(Get(obj, "foo2").ObservedGeneration).To(BeEquivalentTo(5))
249 g.Expect(Get(obj, "foo2").LastTransitionTime).To(Equal(x))
250 }
251
252 func TestMarkMethods(t *testing.T) {
253 g := NewWithT(t)
254
255 obj := &fobject.Fake{}
256
257
258 MarkTrue(obj, "conditionFoo", "reasonFoo", "messageFoo")
259 g.Expect(Get(obj, "conditionFoo")).To(HaveSameStateOf(&metav1.Condition{
260 Type: "conditionFoo",
261 Status: metav1.ConditionTrue,
262 Reason: "reasonFoo",
263 Message: "messageFoo",
264 }))
265
266
267 MarkFalse(obj, "conditionBar", "reasonBar", "messageBar")
268 g.Expect(Get(obj, "conditionBar")).To(HaveSameStateOf(&metav1.Condition{
269 Type: "conditionBar",
270 Status: metav1.ConditionFalse,
271 Reason: "reasonBar",
272 Message: "messageBar",
273 }))
274
275
276 MarkUnknown(obj, "conditionBaz", "reasonBaz", "messageBaz")
277 g.Expect(Get(obj, "conditionBaz")).To(HaveSameStateOf(&metav1.Condition{
278 Type: "conditionBaz",
279 Status: metav1.ConditionUnknown,
280 Reason: "reasonBaz",
281 Message: "messageBaz",
282 }))
283
284
285 MarkTrue(obj, status.StalledCondition, "reasonStalled", "messageStalled")
286 MarkReconciling(obj, "reasonReconciling", "messageReconciling")
287 g.Expect(Get(obj, status.ReconcilingCondition)).To(HaveSameStateOf(&metav1.Condition{
288 Type: status.ReconcilingCondition,
289 Status: metav1.ConditionTrue,
290 Reason: "reasonReconciling",
291 Message: "messageReconciling",
292 }))
293 g.Expect(IsUnknown(obj, status.StalledCondition)).To(BeTrue())
294
295
296 MarkTrue(obj, status.ReconcilingCondition, "reasonReconciling", "messageReconciling")
297 MarkStalled(obj, "reasonStalled", "messageStalled")
298 g.Expect(Get(obj, status.StalledCondition)).To(HaveSameStateOf(&metav1.Condition{
299 Type: status.StalledCondition,
300 Status: metav1.ConditionTrue,
301 Reason: "reasonStalled",
302 Message: "messageStalled",
303 }))
304 g.Expect(IsUnknown(obj, status.ReconcilingCondition)).To(BeTrue())
305 }
306
307 func TestSetSummary(t *testing.T) {
308 g := NewWithT(t)
309 target := setterWithConditions(TrueCondition("foo", "", ""))
310
311 SetSummary(target, "test")
312
313 g.Expect(Has(target, "test")).To(BeTrue())
314 }
315
316 func TestSetMirror(t *testing.T) {
317 g := NewWithT(t)
318 source := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
319 target := setterWithConditions()
320
321 SetMirror(target, "foo", source)
322
323 g.Expect(Has(target, "foo")).To(BeTrue())
324 }
325
326 func TestSetAggregate(t *testing.T) {
327 g := NewWithT(t)
328 source1 := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
329 source2 := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
330 target := setterWithConditions()
331
332 SetAggregate(target, "foo", []Getter{source1, source2})
333
334 g.Expect(Has(target, "foo")).To(BeTrue())
335 }
336
337 func setterWithConditions(conditions ...*metav1.Condition) Setter {
338 obj := &fobject.Fake{}
339 obj.SetConditions(conditionList(conditions...))
340 return obj
341 }
342
343 func haveSameConditionsOf(expected []metav1.Condition) types.GomegaMatcher {
344 return &ConditionsMatcher{
345 Expected: expected,
346 }
347 }
348
349 type ConditionsMatcher struct {
350 Expected []metav1.Condition
351 }
352
353 func (matcher *ConditionsMatcher) Match(actual interface{}) (success bool, err error) {
354 actualConditions, ok := actual.([]metav1.Condition)
355 if !ok {
356 return false, errors.New("Value should be a conditions list")
357 }
358
359 if len(actualConditions) != len(matcher.Expected) {
360 return false, nil
361 }
362
363 for i := range actualConditions {
364 if !hasSameState(&actualConditions[i], &matcher.Expected[i]) {
365 return false, nil
366 }
367 }
368 return true, nil
369 }
370
371 func (matcher *ConditionsMatcher) FailureMessage(actual interface{}) (message string) {
372 return format.Message(actual, "to have the same conditions of", matcher.Expected)
373 }
374 func (matcher *ConditionsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
375 return format.Message(actual, "not to have the same conditions of", matcher.Expected)
376 }
377
View as plain text