1 package conditions
2
3 import (
4 "testing"
5
6 fuzz "github.com/AdaLogics/go-fuzz-headers"
7 . "github.com/onsi/gomega"
8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9
10 "edge-infra.dev/pkg/k8s/meta/status"
11 "edge-infra.dev/pkg/k8s/object/fobject"
12 )
13
14 var (
15 nil1 *metav1.Condition
16 true1 = TrueCondition("true1", "reason true1", "message true1")
17 unknown1 = UnknownCondition("unknown1", "reason unknown1", "message unknown1")
18 false1 = FalseCondition("false1", "reason false1", "message false1")
19 readyTrue = TrueCondition(status.ReadyCondition, "reason readyTrue", "message readyTrue")
20 readyFalse = FalseCondition(status.ReadyCondition, "reason readyFalse", "message readyFalse")
21 stalledTrue = TrueCondition(status.StalledCondition, "reason stalledTrue", "message stalledTrue")
22 stalledFalse = FalseCondition(status.StalledCondition, "reason stalledFalse", "message stalledFalse")
23 reconcilingTrue = TrueCondition(status.ReconcilingCondition, "reason reconcilingTrue", "message reconcilingTrue")
24 )
25
26 func TestGetAndHas(t *testing.T) {
27 g := NewWithT(t)
28
29 obj := &fobject.Fake{}
30
31 g.Expect(Has(obj, "conditionBaz")).To(BeFalse())
32 g.Expect(Get(obj, "conditionBaz")).To(BeNil())
33
34 obj.SetConditions(conditionList(TrueCondition("conditionBaz", "", "")))
35
36 g.Expect(Has(obj, "conditionBaz")).To(BeTrue())
37 g.Expect(Get(obj, "conditionBaz")).To(HaveSameStateOf(TrueCondition("conditionBaz", "", "")))
38
39 Set(obj, FalseCondition("conditionFoo", "", ""))
40 g.Expect(HasAny(obj, []string{"conditionFoo", "conditionX"})).To(BeTrue())
41 g.Expect(HasAny(obj, []string{"conditionY", "conditionBaz"})).To(BeTrue())
42 g.Expect(HasAny(obj, []string{"conditionX", "conditionY"})).To(BeFalse())
43 }
44
45 func TestIsMethods(t *testing.T) {
46 g := NewWithT(t)
47
48 false2 := false1.DeepCopy()
49 false2.Type = "false2"
50 false2.ObservedGeneration = 1
51
52 obj := getterWithConditions(nil1, true1, unknown1, false1, false2)
53
54
55 g.Expect(IsTrue(obj, "nil1")).To(BeFalse())
56 g.Expect(IsTrue(obj, "true1")).To(BeTrue())
57 g.Expect(IsTrue(obj, "false1")).To(BeFalse())
58 g.Expect(IsTrue(obj, "unknown1")).To(BeFalse())
59
60
61 g.Expect(IsFalse(obj, "nil1")).To(BeFalse())
62 g.Expect(IsFalse(obj, "true1")).To(BeFalse())
63 g.Expect(IsFalse(obj, "false1")).To(BeTrue())
64 g.Expect(IsFalse(obj, "unknown1")).To(BeFalse())
65
66
67 g.Expect(IsUnknown(obj, "nil1")).To(BeTrue())
68 g.Expect(IsUnknown(obj, "true1")).To(BeFalse())
69 g.Expect(IsUnknown(obj, "false1")).To(BeFalse())
70 g.Expect(IsUnknown(obj, "unknown1")).To(BeTrue())
71
72
73 g.Expect(GetReason(obj, "nil1")).To(Equal(""))
74 g.Expect(GetReason(obj, "false1")).To(Equal("reason false1"))
75
76
77 g.Expect(GetMessage(obj, "nil1")).To(Equal(""))
78 g.Expect(GetMessage(obj, "false1")).To(Equal("message false1"))
79
80
81 g.Expect(GetLastTransitionTime(obj, "nil1")).To(BeNil())
82 g.Expect(GetLastTransitionTime(obj, "false1")).ToNot(BeNil())
83
84
85 g.Expect(GetObservedGeneration(obj, "nil1")).To(BeZero())
86 g.Expect(GetObservedGeneration(obj, "false2")).ToNot(BeZero())
87 }
88
89 func TestIsReadyStalledReconciling(t *testing.T) {
90 g := NewWithT(t)
91
92 readyObj := getterWithConditions(readyTrue, stalledFalse)
93 stalledObj := getterWithConditions(stalledTrue, readyFalse)
94 reconcilingObj := getterWithConditions(reconcilingTrue, stalledFalse)
95
96
97 g.Expect(IsReady(readyObj)).To(BeTrue())
98 g.Expect(IsReady(stalledObj)).To(BeFalse())
99 g.Expect(IsReady(reconcilingObj)).To(BeFalse())
100
101
102 g.Expect(IsStalled(stalledObj)).To(BeTrue())
103 g.Expect(IsStalled(readyObj)).To(BeFalse())
104 g.Expect(IsStalled(reconcilingObj)).To(BeFalse())
105
106
107 g.Expect(IsReconciling(reconcilingObj)).To(BeTrue())
108 g.Expect(IsReconciling(stalledObj)).To(BeFalse())
109 g.Expect(IsReconciling(readyObj)).To(BeFalse())
110 }
111
112 func TestMirror(t *testing.T) {
113 foo := FalseCondition("foo", "reason foo", "message foo")
114 ready := TrueCondition(status.ReadyCondition, "reason ready", "message ready")
115 readyBar := ready.DeepCopy()
116 readyBar.Type = "bar"
117
118 tests := []struct {
119 name string
120 from Getter
121 t string
122 want *metav1.Condition
123 }{
124 {
125 name: "Returns nil when the ready condition does not exists",
126 from: getterWithConditions(foo),
127 want: nil,
128 },
129 {
130 name: "Returns ready condition from source",
131 from: getterWithConditions(ready, foo),
132 t: "bar",
133 want: readyBar,
134 },
135 }
136
137 for _, tt := range tests {
138 t.Run(tt.name, func(t *testing.T) {
139 g := NewWithT(t)
140
141 got := mirror(tt.from, tt.t)
142 if tt.want == nil {
143 g.Expect(got).To(BeNil())
144 return
145 }
146 g.Expect(got).To(HaveSameStateOf(tt.want))
147 })
148 }
149 }
150
151 func TestSummary(t *testing.T) {
152 foo := TrueCondition("foo", "reason trueFoo", "message trueFoo")
153 bar := FalseCondition("bar", "reason falseBar", "message falseBar")
154 baz := FalseCondition("baz", "reason falseBaz", "message falseBaz")
155 existingReady := FalseCondition(status.ReadyCondition, "reason falseReady", "message falseReady")
156
157 tests := []struct {
158 name string
159 from Getter
160 options []MergeOption
161 want *metav1.Condition
162 }{
163 {
164 name: "Returns nil when there are no conditions to summarize",
165 from: getterWithConditions(),
166 want: nil,
167 },
168 {
169 name: "Returns ready condition with the summary of existing conditions (with default options)",
170 from: getterWithConditions(foo, bar),
171 want: FalseCondition(status.ReadyCondition, "reason falseBar", "message falseBar"),
172 },
173 {
174 name: "Returns ready condition with the summary of existing conditions (using WithStepCounter options)",
175 from: getterWithConditions(foo, bar),
176 options: []MergeOption{WithStepCounter()},
177 want: FalseCondition(status.ReadyCondition, "reason falseBar", "1 of 2 completed"),
178 },
179 {
180 name: "Returns ready condition with the summary of existing conditions (using WithStepCounterIf options)",
181 from: getterWithConditions(foo, bar),
182 options: []MergeOption{WithStepCounterIf(false)},
183 want: FalseCondition(status.ReadyCondition, "reason falseBar", "message falseBar"),
184 },
185 {
186 name: "Returns ready condition with the summary of existing conditions (using WithStepCounterIf options)",
187 from: getterWithConditions(foo, bar),
188 options: []MergeOption{WithStepCounterIf(true)},
189 want: FalseCondition(status.ReadyCondition, "reason falseBar", "1 of 2 completed"),
190 },
191 {
192 name: "Returns ready condition with the summary of existing conditions (using WithStepCounterIf and WithStepCounterIfOnly options)",
193 from: getterWithConditions(bar),
194 options: []MergeOption{WithStepCounter(), WithStepCounterIfOnly("bar")},
195 want: FalseCondition(status.ReadyCondition, "reason falseBar", "0 of 1 completed"),
196 },
197 {
198 name: "Returns ready condition with the summary of existing conditions (using WithStepCounterIf and WithStepCounterIfOnly options)",
199 from: getterWithConditions(foo, bar),
200 options: []MergeOption{WithStepCounter(), WithStepCounterIfOnly("foo")},
201 want: FalseCondition(status.ReadyCondition, "reason falseBar", "message falseBar"),
202 },
203 {
204 name: "Returns ready condition with the summary of selected conditions (using WithConditions options)",
205 from: getterWithConditions(foo, bar),
206 options: []MergeOption{WithConditions("foo")},
207 want: TrueCondition(status.ReadyCondition, "reason trueFoo", "message trueFoo"),
208 },
209 {
210 name: "Returns ready condition with the summary of selected conditions (using WithConditions and WithStepCounter options)",
211 from: getterWithConditions(foo, bar, baz),
212 options: []MergeOption{WithConditions("foo", "bar"), WithStepCounter()},
213 want: FalseCondition(status.ReadyCondition, "reason falseBar", "1 of 2 completed"),
214 },
215 {
216 name: "Returns ready condition with the summary of selected conditions (using WithConditions and WithStepCounterIfOnly options)",
217 from: getterWithConditions(bar),
218 options: []MergeOption{WithConditions("bar", "baz"), WithStepCounter(), WithStepCounterIfOnly("bar")},
219 want: FalseCondition(status.ReadyCondition, "reason falseBar", "0 of 1 completed"),
220 },
221 {
222 name: "Returns ready condition with the summary of selected conditions (using WithConditions and WithStepCounterIfOnly options - with inconsistent order between the two)",
223 from: getterWithConditions(bar),
224 options: []MergeOption{WithConditions("baz", "bar"), WithStepCounter(), WithStepCounterIfOnly("bar", "baz")},
225 want: FalseCondition(status.ReadyCondition, "reason falseBar", "0 of 2 completed"),
226 },
227 {
228 name: "Returns ready condition with the summary of selected conditions (using WithConditions and WithStepCounterIfOnly options)",
229 from: getterWithConditions(bar, baz),
230 options: []MergeOption{WithConditions("bar", "baz"), WithStepCounter(), WithStepCounterIfOnly("bar")},
231 want: FalseCondition(status.ReadyCondition, "reason falseBar", "message falseBar"),
232 },
233 {
234 name: "Ready condition respects merge order",
235 from: getterWithConditions(bar, baz),
236 options: []MergeOption{WithConditions("baz", "bar")},
237 want: FalseCondition(status.ReadyCondition, "reason falseBaz", "message falseBaz"),
238 },
239 {
240 name: "Ignores existing Ready condition when computing the summary",
241 from: getterWithConditions(existingReady, foo, bar),
242 want: FalseCondition(status.ReadyCondition, "reason falseBar", "message falseBar"),
243 },
244 }
245
246 for _, tt := range tests {
247 t.Run(tt.name, func(t *testing.T) {
248 g := NewWithT(t)
249
250 got := summary(tt.from, status.ReadyCondition, tt.options...)
251 if tt.want == nil {
252 g.Expect(got).To(BeNil())
253 return
254 }
255 g.Expect(got).To(HaveSameStateOf(tt.want))
256 })
257 }
258 }
259
260 func TestAggregate(t *testing.T) {
261 ready1 := TrueCondition(status.ReadyCondition, "reason true1", "message true1")
262 ready2 := FalseCondition(status.ReadyCondition, "reason false1", "message false1")
263 bar := FalseCondition("bar", "reason falseBar1", "message falseBar1")
264
265 tests := []struct {
266 name string
267 from []Getter
268 t string
269 opts []MergeOption
270 want *metav1.Condition
271 }{
272 {
273 name: "Returns nil when there are no conditions to aggregate",
274 from: []Getter{},
275 want: nil,
276 },
277 {
278 name: "Returns foo condition with an aggregation of the object's top group conditions",
279 from: []Getter{
280 getterWithConditions(ready1),
281 getterWithConditions(ready1),
282 getterWithConditions(ready2, bar),
283 getterWithConditions(),
284 getterWithConditions(bar),
285 },
286 t: "foo",
287 want: FalseCondition("foo", "reason false1", "message false1"),
288 },
289 {
290 name: "Returns foo condition with the aggregation of object's subset conditions",
291 from: []Getter{
292 getterWithConditions(ready1),
293 getterWithConditions(ready1),
294 getterWithConditions(ready2, bar),
295 getterWithConditions(),
296 getterWithConditions(bar),
297 },
298 opts: []MergeOption{
299 WithConditions("bar"),
300 },
301 t: "foo",
302 want: FalseCondition("foo", "reason falseBar1", "message falseBar1"),
303 },
304 {
305 name: "Returns foo condition with the aggregation of object's subset priority conditions",
306 from: []Getter{
307 getterWithConditions(ready1),
308 getterWithConditions(ready1),
309 getterWithConditions(ready2, bar),
310 getterWithConditions(),
311 getterWithConditions(bar),
312 },
313 opts: []MergeOption{
314 WithConditions("bar", status.ReadyCondition),
315 },
316 t: "foo",
317 want: FalseCondition("foo", "reason falseBar1", "message falseBar1"),
318 },
319 {
320 name: "Returns foo condition with the aggregation of object's subset priority conditions (inverse)",
321 from: []Getter{
322 getterWithConditions(ready1),
323 getterWithConditions(ready1),
324 getterWithConditions(ready2, bar),
325 getterWithConditions(),
326 getterWithConditions(bar),
327 },
328 opts: []MergeOption{
329 WithConditions(status.ReadyCondition, "bar"),
330 },
331 t: "foo",
332 want: FalseCondition("foo", "reason false1", "message false1"),
333 },
334 {
335 name: "Returns foo condition with source ref",
336 from: []Getter{
337 getterWithConditions(ready1),
338 getterWithConditions(ready1),
339 getterWithConditions(ready2, bar),
340 getterWithConditions(),
341 getterWithConditions(bar),
342 },
343 opts: []MergeOption{
344 WithSourceRefIf(status.ReadyCondition),
345 },
346 t: "foo",
347 want: FalseCondition("foo", "reason false1 @ /", "message false1"),
348 },
349 }
350
351 for _, tt := range tests {
352 t.Run(tt.name, func(t *testing.T) {
353 g := NewWithT(t)
354
355 got := aggregate(tt.from, tt.t, tt.opts...)
356 if tt.want == nil {
357 g.Expect(got).To(BeNil())
358 return
359 }
360 g.Expect(got).To(HaveSameStateOf(tt.want))
361 })
362 }
363 }
364
365 func Fuzz_Getter(f *testing.F) {
366 f.Fuzz(func(_ *testing.T,
367 data []byte) {
368 fc := fuzz.NewConsumer(data)
369
370
371 noOfConditions, err := fc.GetInt()
372 if err != nil {
373 return
374 }
375 maxNoOfConditions := 30
376 conditions := make([]metav1.Condition, 0)
377
378
379
380 for i := 0; i < noOfConditions%maxNoOfConditions; i++ {
381 c := metav1.Condition{}
382 err = fc.GenerateStruct(&c)
383 if err != nil {
384 return
385 }
386 conditions = append(conditions, c)
387 }
388 obj := &fobject.Fake{}
389 obj.SetConditions(conditions)
390
391 targetCondition, err := fc.GetString()
392 if err != nil {
393 return
394 }
395
396 SetSummary(obj, targetCondition)
397 })
398 }
399
400 func getterWithConditions(conditions ...*metav1.Condition) Getter {
401 obj := &fobject.Fake{}
402 obj.SetConditions(conditionList(conditions...))
403 return obj
404 }
405
406 func conditionList(conditions ...*metav1.Condition) []metav1.Condition {
407 cs := []metav1.Condition{}
408 for _, x := range conditions {
409 if x != nil {
410 cs = append(cs, *x)
411 }
412 }
413 return cs
414 }
415
View as plain text