knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/reconciler/filter_test.go (about) 1 /* 2 Copyright 2020 The Knative 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 reconciler 18 19 import ( 20 "testing" 21 22 v1 "k8s.io/api/core/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 ) 25 26 const ( 27 keyToFilter = "testKey" 28 valueToFilter = "testVal" 29 nameToFilter = "testName" 30 namespaceToFilter = "testSpace" 31 ) 32 33 func pod(namespace, name string, annos, labels map[string]string) *v1.Pod { 34 return &v1.Pod{ 35 ObjectMeta: metav1.ObjectMeta{ 36 Namespace: namespace, 37 Name: name, 38 Annotations: annos, 39 Labels: labels, 40 }, 41 } 42 } 43 44 func podWithLabels(labels map[string]string) *v1.Pod { 45 return pod(namespaceToFilter, nameToFilter, nil, labels) 46 } 47 48 func podWithAnnotations(annos map[string]string) *v1.Pod { 49 return pod(namespaceToFilter, nameToFilter, annos, nil) 50 } 51 52 func podWithName(name string) *v1.Pod { 53 return pod(namespaceToFilter, name, nil, nil) 54 } 55 56 func podWithNamespace(namespace string) *v1.Pod { 57 return pod(namespace, nameToFilter, nil, nil) 58 } 59 60 type params struct { 61 name string 62 allowUnset bool 63 in interface{} 64 want bool 65 } 66 67 func TestAnnotationFilter(t *testing.T) { 68 tests := []params{{ 69 name: "non kubernetes object", 70 in: struct{}{}, 71 want: false, 72 }, { 73 name: "empty annotations", 74 in: podWithAnnotations(nil), 75 want: false, 76 }, { 77 name: "empty annotations, allow unset", 78 allowUnset: true, 79 in: podWithAnnotations(nil), 80 want: true, 81 }, { 82 name: "other annotations", 83 in: podWithAnnotations(map[string]string{"anotherKey": "anotherValue"}), 84 want: false, 85 }, { 86 name: "other annotations, allow unset", 87 allowUnset: true, 88 in: podWithAnnotations(map[string]string{"anotherKey": "anotherValue"}), 89 want: true, 90 }, { 91 name: "matching key, value mismatch", 92 in: podWithAnnotations(map[string]string{keyToFilter: "testVal2"}), 93 want: false, 94 }, { 95 name: "matching key, value mismatch, allow unset", 96 allowUnset: true, 97 in: podWithAnnotations(map[string]string{keyToFilter: "testVal2"}), 98 want: false, 99 }, { 100 name: "match", 101 in: podWithAnnotations(map[string]string{keyToFilter: valueToFilter}), 102 want: true, 103 }} 104 105 for _, test := range tests { 106 t.Run(test.name, func(t *testing.T) { 107 filter := AnnotationFilterFunc(keyToFilter, valueToFilter, test.allowUnset) 108 got := filter(test.in) 109 if got != test.want { 110 t.Errorf("AnnotationFilterFunc() = %v, want %v", got, test.want) 111 } 112 }) 113 } 114 } 115 116 func TestLabelExistsFilterFunc(t *testing.T) { 117 ti := []params{{ 118 name: "label exists", 119 in: podWithLabels(map[string]string{keyToFilter: valueToFilter}), 120 want: true, 121 }, { 122 name: "empty labels", 123 in: podWithLabels(map[string]string{}), 124 want: false, 125 }, { 126 name: "non-empty map, the required label doesn't exist", 127 in: podWithLabels(map[string]string{"randomLabel": ""}), 128 want: false, 129 }, { 130 name: "non kubernetes object", 131 in: struct{}{}, 132 want: false, 133 }} 134 135 for _, test := range ti { 136 t.Run(test.name, func(t *testing.T) { 137 filter := LabelExistsFilterFunc(keyToFilter) 138 got := filter(test.in) 139 if got != test.want { 140 t.Errorf("LabelExistsFilterFunc() = %v, want %v", got, test.want) 141 } 142 }) 143 } 144 } 145 146 func TestLabelFilterFunc(t *testing.T) { 147 ti := []params{{ 148 name: "label matches no unset", 149 in: podWithLabels(map[string]string{keyToFilter: valueToFilter}), 150 allowUnset: false, 151 want: true, 152 }, { 153 name: "label matches with unset", 154 in: podWithLabels(map[string]string{keyToFilter: valueToFilter}), 155 allowUnset: true, 156 want: true, 157 }, { 158 name: "label mismatch no unset", 159 in: podWithLabels(map[string]string{keyToFilter: "otherval"}), 160 allowUnset: false, 161 want: false, 162 }, { 163 name: "label mismatch with unset", 164 in: podWithLabels(map[string]string{keyToFilter: "otherval"}), 165 allowUnset: true, 166 want: false, 167 }, { 168 name: "label missing no unset", 169 in: podWithLabels(map[string]string{}), 170 allowUnset: false, 171 want: false, 172 }, { 173 name: "label missing with unset", 174 in: podWithLabels(map[string]string{}), 175 allowUnset: true, 176 want: true, 177 }, { 178 name: "nil labels no unset", 179 in: podWithLabels(nil), 180 allowUnset: false, 181 want: false, 182 }, { 183 name: "nil labels with unset", 184 in: podWithLabels(nil), 185 allowUnset: true, 186 want: true, 187 }, { 188 name: "non kubernetes object", 189 in: struct{}{}, 190 want: false, 191 }} 192 193 for _, test := range ti { 194 t.Run(test.name, func(t *testing.T) { 195 filter := LabelFilterFunc(keyToFilter, valueToFilter, test.allowUnset) 196 got := filter(test.in) 197 if got != test.want { 198 t.Errorf("LabelFilterFunc() = %v, want %v", got, test.want) 199 } 200 }) 201 } 202 } 203 204 func TestNameFilterFunc(t *testing.T) { 205 ti := []params{{ 206 name: "name match", 207 in: podWithName(nameToFilter), 208 want: true, 209 }, { 210 name: "name mismatch", 211 in: podWithName("bogus"), 212 want: false, 213 }, { 214 name: "non kubernetes object", 215 in: struct{}{}, 216 want: false, 217 }} 218 219 for _, test := range ti { 220 t.Run(test.name, func(t *testing.T) { 221 filter := NameFilterFunc(nameToFilter) 222 got := filter(test.in) 223 if got != test.want { 224 t.Errorf("NameFilterFunc() = %v, want %v", got, test.want) 225 } 226 }) 227 } 228 } 229 230 func TestNamespaceFilterFunc(t *testing.T) { 231 ti := []params{{ 232 name: "namespace match", 233 in: podWithNamespace(namespaceToFilter), 234 want: true, 235 }, { 236 name: "namespace mismatch", 237 in: podWithNamespace("bogus"), 238 want: false, 239 }, { 240 name: "non kubernetes object", 241 in: struct{}{}, 242 want: false, 243 }} 244 245 for _, test := range ti { 246 t.Run(test.name, func(t *testing.T) { 247 filter := NamespaceFilterFunc(namespaceToFilter) 248 got := filter(test.in) 249 if got != test.want { 250 t.Errorf("NamespaceFilterFunc() = %v, want %v", got, test.want) 251 } 252 }) 253 } 254 } 255 256 func TestChainFilterFuncs(t *testing.T) { 257 tc := []struct { 258 name string 259 chain []bool 260 want bool 261 }{{ 262 name: "single true", 263 chain: []bool{true}, 264 want: true, 265 }, { 266 name: "single false", 267 chain: []bool{false}, 268 want: false, 269 }, { 270 name: "second false", 271 chain: []bool{true, false}, 272 want: false, 273 }, { 274 name: "multi true", 275 chain: []bool{true, true}, 276 want: true, 277 }} 278 279 for _, test := range tc { 280 t.Run(test.name, func(t *testing.T) { 281 filters := make([]func(interface{}) bool, len(test.chain)) 282 for i, chainVal := range test.chain { 283 filters[i] = func(interface{}) bool { 284 return chainVal 285 } 286 } 287 filter := ChainFilterFuncs(filters...) 288 got := filter(nil) 289 if got != test.want { 290 t.Errorf("ChainFilterFuncs() = %v, want %v", got, test.want) 291 } 292 }) 293 } 294 } 295 296 func TestNotFilter(t *testing.T) { 297 odd := func(o interface{}) bool { 298 // Return true if odd. 299 return (o.(int))&1 == 1 300 } 301 if got, want := Not(odd)(1), false; got != want { 302 t.Errorf("Odd input = %v, want: %v", got, want) 303 } 304 if got, want := Not(odd)(2), true; got != want { 305 t.Errorf("Odd input = %v, want: %v", got, want) 306 } 307 }