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  }