1 package match
2
3 import (
4 "testing"
5
6 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
7 "k8s.io/apimachinery/pkg/runtime/schema"
8 "sigs.k8s.io/controller-runtime/pkg/client"
9 )
10
11 func TestObject(t *testing.T) {
12 tcs := map[string]struct {
13 obj client.Object
14 matcher Matcher
15 exp bool
16 }{
17 "nil object is false": {
18 nil,
19 AnyInMetadata{"key": "value"},
20 false,
21 },
22 "nil matcher is false": {
23 &unstructured.Unstructured{},
24 nil,
25 false,
26 },
27 "actually matches": {
28 func() client.Object {
29 o := &unstructured.Unstructured{}
30 o.SetGroupVersionKind(schema.GroupVersionKind{
31 Group: "apps",
32 Version: "v1",
33 Kind: "Deployment",
34 })
35 return o
36 }(),
37 Kind{"Deployment"},
38 true,
39 },
40 }
41
42 for name, tc := range tcs {
43 t.Run(name, func(t *testing.T) {
44 ok := Object(tc.obj, tc.matcher)
45 if ok != tc.exp {
46 t.Errorf("unexpected Match() result: wanted %t, got %t", tc.exp, ok)
47 }
48 })
49 }
50 }
51
52 func TestMatchers(t *testing.T) {
53 empty := &unstructured.Unstructured{}
54
55 type testCase struct {
56 obj client.Object
57 matcher Matcher
58 exp bool
59 }
60
61 tcs := map[string]map[string]testCase{
62 "Namespace": {
63 "Matches": {
64 &unstructured.Unstructured{Object: map[string]any{
65 "metadata": map[string]any{
66 "namespace": "kube-system",
67 },
68 }},
69 Namespace{"kube-system"},
70 true,
71 },
72 "Doesn't Match": {
73 &unstructured.Unstructured{Object: map[string]any{
74 "metadata": map[string]any{
75 "namespace": "kube-system",
76 },
77 }},
78 Namespace{"test"},
79 false,
80 },
81 "Empty Doesn't Match": {
82 empty,
83 Namespace{"test"},
84 false,
85 },
86 },
87 "Name": {
88 "Matches": {
89 &unstructured.Unstructured{Object: map[string]any{
90 "metadata": map[string]any{
91 "name": "bundle-of-joy",
92 },
93 }},
94 Name{"bundle-of-joy"},
95 true,
96 },
97 "Doesn't Match": {
98 &unstructured.Unstructured{Object: map[string]any{
99 "metadata": map[string]any{
100 "name": "unexpected-encounter",
101 },
102 }},
103 Name{"bundle-of-joy"},
104 false,
105 },
106 "Empty Doesn't Match": {
107 empty,
108 Name{"bundle-of-joy"},
109 false,
110 },
111 },
112 "Kind": {
113 "Matches": {
114 &unstructured.Unstructured{Object: map[string]any{
115 "kind": "Deployment",
116 }},
117 Kind{"Deployment"},
118 true,
119 },
120 "Doesn't Match": {
121 &unstructured.Unstructured{Object: map[string]any{
122 "kind": "Deployment",
123 }},
124 Kind{"Pod"},
125 false,
126 },
127 "Empty Doesn't Match": {
128 empty,
129 Kind{"Pod"},
130 false,
131 },
132 },
133 "GroupVersion": {
134 "Matches": {
135 &unstructured.Unstructured{Object: map[string]any{
136 "apiVersion": "apps/v1",
137 }},
138 GroupVersion{Group: "apps", Version: "v1"},
139 true,
140 },
141 "Doesn't Match": {
142 &unstructured.Unstructured{Object: map[string]any{
143 "apiVersion": "apps/v1",
144 }},
145 GroupVersion{Group: "", Version: "v1beta1"},
146 false,
147 },
148 "Doesn't Match Version": {
149 &unstructured.Unstructured{Object: map[string]any{
150 "apiVersion": "apps/v1",
151 }},
152 GroupVersion{Group: "apps", Version: "v1beta1"},
153 false,
154 },
155 "Empty Doesn't Match": {
156 empty,
157 GroupVersion{Group: "apps", Version: "v1"},
158 false,
159 },
160 },
161 "AnyInMetadata": {
162 "Matches": {
163 objWithMeta(map[string]any{
164 "labels": map[string]any{
165 "key": "value",
166 },
167 }),
168 AnyInMetadata{"key": "value"},
169 true,
170 },
171 "Partial Match Annotations": {
172 objWithMeta(map[string]any{
173 "annotations": map[string]any{
174 "key": "value",
175 },
176 }),
177 AnyInMetadata{"key": "value", "key2": "value2"},
178 true,
179 },
180 "Partial Match Labels": {
181 objWithMeta(map[string]any{
182 "labels": map[string]any{
183 "key": "value",
184 },
185 }),
186 AnyInMetadata{"key": "value", "key2": "value2"},
187 true,
188 },
189 "Matches Case Insensitive": {
190 objWithMeta(map[string]any{
191 "labels": map[string]any{
192 "key": "valUe",
193 },
194 }),
195 AnyInMetadata{"key": "value"},
196 true,
197 },
198 "Doesn't Match": {
199 objWithMeta(map[string]any{
200 "labels": map[string]any{
201 "key2": "value",
202 },
203 "annotations": map[string]any{
204 "key": "value2",
205 },
206 }),
207 AnyInMetadata{"key": "value", "key2": "value2"},
208 false,
209 },
210 "Empty Doesn't Match": {
211 empty,
212 AnyInMetadata{"key": "value"},
213 false,
214 },
215 },
216 "Labels": {
217 "Matches": {
218 objWithMeta(map[string]any{
219 "labels": map[string]any{
220 "label": "labelValue",
221 },
222 }),
223 Labels{"label": "labelValue"},
224 true,
225 },
226 "Matches Multiple": {
227 objWithMeta(map[string]any{
228 "labels": map[string]any{
229 "key": "value",
230 "key2": "value2",
231 },
232 }),
233 Labels{
234 "key": "value",
235 "key2": "value2",
236 },
237 true,
238 },
239 "Matches Case Insensitive": {
240 objWithMeta(map[string]any{
241 "labels": map[string]any{
242 "key": "valUe",
243 },
244 }),
245 Labels{"key": "valUe"},
246 true,
247 },
248 "Doesn't Match Partially": {
249 objWithMeta(map[string]any{
250 "labels": map[string]any{
251 "key": "value",
252 "key2": "value3",
253 },
254 }),
255 Labels{"key": "value", "key2": "value2"},
256 false,
257 },
258 "Empty Doesn't Match": {
259 empty,
260 Labels{"key": "value"},
261 false,
262 },
263 },
264 "Annotations": {
265 "Matches": {
266 objWithMeta(map[string]any{
267 "annotations": map[string]any{
268 "key": "value",
269 },
270 }),
271 Annotations{"key": "value"},
272 true,
273 },
274 "Matches Multiple": {
275 objWithMeta(map[string]any{
276 "annotations": map[string]any{
277 "key": "value",
278 "key2": "value2",
279 },
280 }),
281 Annotations{"key": "value", "key2": "value2"},
282 true,
283 },
284 "Matches Case Insensitive": {
285 objWithMeta(map[string]any{
286 "annotations": map[string]any{
287 "key": "valUe",
288 },
289 }),
290 Annotations{"key": "valUe"},
291 true,
292 },
293 "Doesn't Match Partially": {
294 objWithMeta(map[string]any{
295 "annotations": map[string]any{
296 "key": "value",
297 "key2": "value3",
298 },
299 }),
300 Annotations{"key": "value", "key2": "value2"},
301 false,
302 },
303 "Empty Doesn't Match": {
304 empty,
305 Annotations{"key": "value"},
306 false,
307 },
308 },
309 "Any": {
310 "Matches": {
311 &unstructured.Unstructured{Object: map[string]any{
312 "kind": "Deployment",
313 }},
314 Any{Kind{"Deployment"}},
315 true,
316 },
317 "Partial Matches": {
318 &unstructured.Unstructured{Object: map[string]any{
319 "kind": "Deployment",
320 }},
321 Any{Kind{"Deployment"}, Name{"bundle-of-joy"}},
322 true,
323 },
324 "Ignores nil Matchers": {
325 &unstructured.Unstructured{Object: map[string]any{
326 "kind": "Deployment",
327 "metadata": map[string]any{
328 "name": "bundle-of-joy",
329 },
330 }},
331 Any{Kind{"Deployment"}, nil, Name{"bundle-of-joy"}},
332 true,
333 },
334 "Doesn't Match": {
335 empty,
336 Any{Kind{"Deployment"}, Name{"bundle-of-joy"}},
337 false,
338 },
339 "Empty Doesn't Match": {
340 &unstructured.Unstructured{Object: map[string]any{
341 "kind": "Deployment",
342 "metadata": map[string]any{
343 "name": "bundle-of-joy",
344 },
345 }},
346 Any{},
347 false,
348 },
349 },
350 "All": {
351 "Matches": {
352 &unstructured.Unstructured{Object: map[string]any{
353 "kind": "Deployment",
354 }},
355 All{Kind{"Deployment"}},
356 true,
357 },
358 "Partial Doesn't Match": {
359 &unstructured.Unstructured{Object: map[string]any{
360 "kind": "Deployment",
361 }},
362 All{Kind{"Deployment"}, Name{"bundle-of-joy"}},
363 false,
364 },
365 "Ignores nil Matchers": {
366 &unstructured.Unstructured{Object: map[string]any{
367 "kind": "Deployment",
368 "metadata": map[string]any{
369 "name": "bundle-of-joy",
370 },
371 }},
372 All{Kind{"Deployment"}, nil, Name{"bundle-of-joy"}},
373 true,
374 },
375 "Doesn't Match": {
376 empty,
377 All{Kind{"Deployment"}, Name{"bundle-of-joy"}},
378 false,
379 },
380 "Empty Doesn't Match": {
381 &unstructured.Unstructured{Object: map[string]any{
382 "kind": "Deployment",
383 "metadata": map[string]any{
384 "name": "bundle-of-joy",
385 },
386 }},
387 All{},
388 false,
389 },
390 },
391 }
392
393 for fn, fnTestCases := range tcs {
394 t.Run(fn, func(t *testing.T) {
395 for name, tc := range fnTestCases {
396 t.Run(name, func(t *testing.T) {
397 ok := tc.matcher.Match(tc.obj)
398 if ok != tc.exp {
399 t.Errorf("unexpected Match() result: wanted %t, got %t", tc.exp, ok)
400 }
401 })
402 }
403 })
404 }
405 }
406
407 func objWithMeta(meta map[string]any) *unstructured.Unstructured {
408 return &unstructured.Unstructured{Object: map[string]any{
409 "metadata": meta,
410 }}
411 }
412
View as plain text