github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/label/selector_test.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package label
    15  
    16  import (
    17  	"strconv"
    18  	"sync"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestSelectorMatches(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	cases := []struct {
    28  		selector    *Selector
    29  		labels      Set
    30  		shouldMatch bool
    31  	}{
    32  		{
    33  			selector: &Selector{
    34  				Key:    "tenant",
    35  				Target: "1",
    36  				Op:     OpEq,
    37  			},
    38  			labels:      map[Key]Value{"tenant": "1", "node_type": "2"},
    39  			shouldMatch: true,
    40  		},
    41  		{
    42  			selector: &Selector{
    43  				Key:    "tenant",
    44  				Target: "1",
    45  				Op:     OpEq,
    46  			},
    47  			labels:      map[Key]Value{"tenant": "2", "node_type": "2"},
    48  			shouldMatch: false,
    49  		},
    50  		{
    51  			selector: &Selector{
    52  				Key:    "tenant",
    53  				Target: "1",
    54  				Op:     OpEq,
    55  			},
    56  			labels:      map[Key]Value{"node_type": "2"},
    57  			shouldMatch: false,
    58  		},
    59  		{
    60  			selector: &Selector{
    61  				Key:    "tenant",
    62  				Target: "1",
    63  				Op:     OpNeq,
    64  			},
    65  			labels:      map[Key]Value{"tenant": "2", "node_type": "2"},
    66  			shouldMatch: true,
    67  		},
    68  		{
    69  			selector: &Selector{
    70  				Key:    "tenant",
    71  				Target: "1",
    72  				Op:     OpNeq,
    73  			},
    74  			labels:      map[Key]Value{"tenant": "1", "node_type": "2"},
    75  			shouldMatch: false,
    76  		},
    77  		{
    78  			selector: &Selector{
    79  				Key:    "tenant",
    80  				Target: "1",
    81  				Op:     OpNeq,
    82  			},
    83  			labels:      map[Key]Value{"node_type": "2"},
    84  			shouldMatch: true,
    85  		},
    86  		{
    87  			selector: &Selector{
    88  				Key:    "tenant",
    89  				Target: ".*abc.*",
    90  				Op:     OpRegex,
    91  			},
    92  			labels:      map[Key]Value{"tenant": "1abc2", "node_type": "2"},
    93  			shouldMatch: true,
    94  		},
    95  		{
    96  			selector: &Selector{
    97  				Key:    "tenant",
    98  				Target: ".*abc.*",
    99  				Op:     OpRegex,
   100  			},
   101  			labels:      map[Key]Value{"tenant": "asdf", "node_type": "2"},
   102  			shouldMatch: false,
   103  		},
   104  		{
   105  			selector: &Selector{
   106  				Key:    "tenant",
   107  				Target: "^(abc|def)$",
   108  				Op:     OpRegex,
   109  			},
   110  			labels:      map[Key]Value{"tenant": "def", "node_type": "2"},
   111  			shouldMatch: true,
   112  		},
   113  		{
   114  			selector: &Selector{
   115  				Key:    "tenant",
   116  				Target: "^(abc|def)$",
   117  				Op:     OpRegex,
   118  			},
   119  			labels:      map[Key]Value{"tenant": "abc", "node_type": "2"},
   120  			shouldMatch: true,
   121  		},
   122  		{
   123  			selector: &Selector{
   124  				Key:    "tenant",
   125  				Target: "^(abc|def)$",
   126  				Op:     OpRegex,
   127  			},
   128  			labels:      map[Key]Value{"tenant": "abcdef", "node_type": "2"},
   129  			shouldMatch: false,
   130  		},
   131  		{
   132  			selector: &Selector{
   133  				Key:    "tenant",
   134  				Target: "^(abc|def)$",
   135  				Op:     OpRegex,
   136  			},
   137  			labels:      map[Key]Value{"node_type": "2"},
   138  			shouldMatch: false,
   139  		},
   140  	}
   141  
   142  	for idx, tc := range cases {
   143  		tc := tc
   144  		t.Run(strconv.Itoa(idx), func(t *testing.T) {
   145  			t.Parallel()
   146  			require.Equal(t, tc.shouldMatch, tc.selector.Matches(tc.labels))
   147  		})
   148  	}
   149  }
   150  
   151  func TestSelectorRegexLazyCompile(t *testing.T) {
   152  	t.Parallel()
   153  
   154  	selector := Selector{
   155  		Key:    "tenant",
   156  		Target: "^(abc|def)$",
   157  		Op:     OpRegex,
   158  	}
   159  
   160  	labelSetMatch := map[Key]Value{"tenant": "abc", "node_type": "2"}
   161  	labelSetNotMatch := map[Key]Value{"tenant": "abcdef", "node_type": "2"}
   162  
   163  	var wg sync.WaitGroup
   164  	for i := 0; i < 10; i++ {
   165  		wg.Add(1)
   166  		go func() {
   167  			defer wg.Done()
   168  			require.True(t, selector.Matches(labelSetMatch))
   169  			require.False(t, selector.Matches(labelSetNotMatch))
   170  		}()
   171  	}
   172  
   173  	wg.Wait()
   174  }
   175  
   176  func TestSelectorRegexIllegal(t *testing.T) {
   177  	t.Parallel()
   178  
   179  	selector := Selector{
   180  		Key:    "tenant",
   181  		Target: "(((", // illegal regular expression
   182  		Op:     OpRegex,
   183  	}
   184  	require.False(t, selector.Matches(map[Key]Value{"tenant": "abc", "node_type": "2"}))
   185  }
   186  
   187  func TestSelectorValidate(t *testing.T) {
   188  	t.Parallel()
   189  
   190  	cases := []struct {
   191  		selector *Selector
   192  		checkErr func(err error)
   193  	}{
   194  		{
   195  			selector: &Selector{
   196  				Key:    "#@$!@#", // Illegal Key
   197  				Target: "1234",
   198  				Op:     OpEq,
   199  			},
   200  			checkErr: func(err error) {
   201  				require.EqualError(t, err,
   202  					"validate selector key: label string has wrong format: #@$!@#")
   203  			},
   204  		},
   205  		{
   206  			selector: &Selector{
   207  				Key:    "tenant",
   208  				Target: "1234",
   209  				Op:     Op("invalid"), // invalid op code
   210  			},
   211  			checkErr: func(err error) {
   212  				require.EqualError(t, err, "invalid selector op: key: tenant, op: invalid")
   213  			},
   214  		},
   215  		{
   216  			selector: &Selector{
   217  				Key:    "tenant",
   218  				Target: ")))", // invalid regex
   219  				Op:     OpRegex,
   220  			},
   221  			checkErr: func(err error) {
   222  				require.ErrorContains(t, err, ")))")
   223  			},
   224  		},
   225  		{
   226  			selector: &Selector{
   227  				Key:    "tenant",
   228  				Target: ".*abc.*",
   229  				Op:     OpRegex,
   230  			},
   231  			checkErr: func(err error) {
   232  				require.NoError(t, err)
   233  			},
   234  		},
   235  	}
   236  
   237  	for idx, tc := range cases {
   238  		tc := tc
   239  		t.Run(strconv.Itoa(idx), func(t *testing.T) {
   240  			t.Parallel()
   241  			tc.checkErr(tc.selector.Validate())
   242  		})
   243  	}
   244  }