dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/matcher/string_matcher_test.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright 2020 gRPC authors.
    21   *
    22   */
    23  
    24  package matcher
    25  
    26  import (
    27  	"regexp"
    28  	"testing"
    29  )
    30  
    31  import (
    32  	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    33  
    34  	"github.com/google/go-cmp/cmp"
    35  )
    36  
    37  func TestStringMatcherFromProto(t *testing.T) {
    38  	tests := []struct {
    39  		desc        string
    40  		inputProto  *v3matcherpb.StringMatcher
    41  		wantMatcher StringMatcher
    42  		wantErr     bool
    43  	}{
    44  		{
    45  			desc:    "nil proto",
    46  			wantErr: true,
    47  		},
    48  		{
    49  			desc: "empty prefix",
    50  			inputProto: &v3matcherpb.StringMatcher{
    51  				MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""},
    52  			},
    53  			wantErr: true,
    54  		},
    55  		{
    56  			desc: "empty suffix",
    57  			inputProto: &v3matcherpb.StringMatcher{
    58  				MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""},
    59  			},
    60  			wantErr: true,
    61  		},
    62  		{
    63  			desc: "empty contains",
    64  			inputProto: &v3matcherpb.StringMatcher{
    65  				MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""},
    66  			},
    67  			wantErr: true,
    68  		},
    69  		{
    70  			desc: "invalid regex",
    71  			inputProto: &v3matcherpb.StringMatcher{
    72  				MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{
    73  					SafeRegex: &v3matcherpb.RegexMatcher{Regex: "??"},
    74  				},
    75  			},
    76  			wantErr: true,
    77  		},
    78  		{
    79  			desc: "happy case exact",
    80  			inputProto: &v3matcherpb.StringMatcher{
    81  				MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"},
    82  			},
    83  			wantMatcher: StringMatcher{exactMatch: newStringP("exact")},
    84  		},
    85  		{
    86  			desc: "happy case exact ignore case",
    87  			inputProto: &v3matcherpb.StringMatcher{
    88  				MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "EXACT"},
    89  				IgnoreCase:   true,
    90  			},
    91  			wantMatcher: StringMatcher{
    92  				exactMatch: newStringP("exact"),
    93  				ignoreCase: true,
    94  			},
    95  		},
    96  		{
    97  			desc: "happy case prefix",
    98  			inputProto: &v3matcherpb.StringMatcher{
    99  				MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"},
   100  			},
   101  			wantMatcher: StringMatcher{prefixMatch: newStringP("prefix")},
   102  		},
   103  		{
   104  			desc: "happy case prefix ignore case",
   105  			inputProto: &v3matcherpb.StringMatcher{
   106  				MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "PREFIX"},
   107  				IgnoreCase:   true,
   108  			},
   109  			wantMatcher: StringMatcher{
   110  				prefixMatch: newStringP("prefix"),
   111  				ignoreCase:  true,
   112  			},
   113  		},
   114  		{
   115  			desc: "happy case suffix",
   116  			inputProto: &v3matcherpb.StringMatcher{
   117  				MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"},
   118  			},
   119  			wantMatcher: StringMatcher{suffixMatch: newStringP("suffix")},
   120  		},
   121  		{
   122  			desc: "happy case suffix ignore case",
   123  			inputProto: &v3matcherpb.StringMatcher{
   124  				MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "SUFFIX"},
   125  				IgnoreCase:   true,
   126  			},
   127  			wantMatcher: StringMatcher{
   128  				suffixMatch: newStringP("suffix"),
   129  				ignoreCase:  true,
   130  			},
   131  		},
   132  		{
   133  			desc: "happy case regex",
   134  			inputProto: &v3matcherpb.StringMatcher{
   135  				MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{
   136  					SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"},
   137  				},
   138  			},
   139  			wantMatcher: StringMatcher{regexMatch: regexp.MustCompile("good?regex?")},
   140  		},
   141  		{
   142  			desc: "happy case contains",
   143  			inputProto: &v3matcherpb.StringMatcher{
   144  				MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"},
   145  			},
   146  			wantMatcher: StringMatcher{containsMatch: newStringP("contains")},
   147  		},
   148  		{
   149  			desc: "happy case contains ignore case",
   150  			inputProto: &v3matcherpb.StringMatcher{
   151  				MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "CONTAINS"},
   152  				IgnoreCase:   true,
   153  			},
   154  			wantMatcher: StringMatcher{
   155  				containsMatch: newStringP("contains"),
   156  				ignoreCase:    true,
   157  			},
   158  		},
   159  	}
   160  
   161  	for _, test := range tests {
   162  		t.Run(test.desc, func(t *testing.T) {
   163  			gotMatcher, err := StringMatcherFromProto(test.inputProto)
   164  			if (err != nil) != test.wantErr {
   165  				t.Fatalf("StringMatcherFromProto(%+v) returned err: %v, wantErr: %v", test.inputProto, err, test.wantErr)
   166  			}
   167  			if diff := cmp.Diff(gotMatcher, test.wantMatcher, cmp.AllowUnexported(regexp.Regexp{})); diff != "" {
   168  				t.Fatalf("StringMatcherFromProto(%+v) returned unexpected diff (-got, +want):\n%s", test.inputProto, diff)
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  func TestMatch(t *testing.T) {
   175  	var (
   176  		exactMatcher, _           = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"}})
   177  		prefixMatcher, _          = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"}})
   178  		suffixMatcher, _          = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"}})
   179  		regexMatcher, _           = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"}}})
   180  		containsMatcher, _        = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"}})
   181  		exactMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
   182  			MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"},
   183  			IgnoreCase:   true,
   184  		})
   185  		prefixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
   186  			MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"},
   187  			IgnoreCase:   true,
   188  		})
   189  		suffixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
   190  			MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"},
   191  			IgnoreCase:   true,
   192  		})
   193  		containsMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
   194  			MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"},
   195  			IgnoreCase:   true,
   196  		})
   197  	)
   198  
   199  	tests := []struct {
   200  		desc      string
   201  		matcher   StringMatcher
   202  		input     string
   203  		wantMatch bool
   204  	}{
   205  		{
   206  			desc:      "exact match success",
   207  			matcher:   exactMatcher,
   208  			input:     "exact",
   209  			wantMatch: true,
   210  		},
   211  		{
   212  			desc:    "exact match failure",
   213  			matcher: exactMatcher,
   214  			input:   "not-exact",
   215  		},
   216  		{
   217  			desc:      "exact match success with ignore case",
   218  			matcher:   exactMatcherIgnoreCase,
   219  			input:     "EXACT",
   220  			wantMatch: true,
   221  		},
   222  		{
   223  			desc:    "exact match failure with ignore case",
   224  			matcher: exactMatcherIgnoreCase,
   225  			input:   "not-exact",
   226  		},
   227  		{
   228  			desc:      "prefix match success",
   229  			matcher:   prefixMatcher,
   230  			input:     "prefixIsHere",
   231  			wantMatch: true,
   232  		},
   233  		{
   234  			desc:    "prefix match failure",
   235  			matcher: prefixMatcher,
   236  			input:   "not-prefix",
   237  		},
   238  		{
   239  			desc:      "prefix match success with ignore case",
   240  			matcher:   prefixMatcherIgnoreCase,
   241  			input:     "PREFIXisHere",
   242  			wantMatch: true,
   243  		},
   244  		{
   245  			desc:    "prefix match failure with ignore case",
   246  			matcher: prefixMatcherIgnoreCase,
   247  			input:   "not-PREFIX",
   248  		},
   249  		{
   250  			desc:      "suffix match success",
   251  			matcher:   suffixMatcher,
   252  			input:     "hereIsThesuffix",
   253  			wantMatch: true,
   254  		},
   255  		{
   256  			desc:    "suffix match failure",
   257  			matcher: suffixMatcher,
   258  			input:   "suffix-is-not-here",
   259  		},
   260  		{
   261  			desc:      "suffix match success with ignore case",
   262  			matcher:   suffixMatcherIgnoreCase,
   263  			input:     "hereIsTheSuFFix",
   264  			wantMatch: true,
   265  		},
   266  		{
   267  			desc:    "suffix match failure with ignore case",
   268  			matcher: suffixMatcherIgnoreCase,
   269  			input:   "SUFFIX-is-not-here",
   270  		},
   271  		{
   272  			desc:      "regex match success",
   273  			matcher:   regexMatcher,
   274  			input:     "goodregex",
   275  			wantMatch: true,
   276  		},
   277  		{
   278  			desc:      "regex match failure because only part match",
   279  			matcher:   regexMatcher,
   280  			input:     "goodregexa",
   281  			wantMatch: false,
   282  		},
   283  		{
   284  			desc:    "regex match failure",
   285  			matcher: regexMatcher,
   286  			input:   "regex-is-not-here",
   287  		},
   288  		{
   289  			desc:      "contains match success",
   290  			matcher:   containsMatcher,
   291  			input:     "IScontainsHERE",
   292  			wantMatch: true,
   293  		},
   294  		{
   295  			desc:    "contains match failure",
   296  			matcher: containsMatcher,
   297  			input:   "con-tains-is-not-here",
   298  		},
   299  		{
   300  			desc:      "contains match success with ignore case",
   301  			matcher:   containsMatcherIgnoreCase,
   302  			input:     "isCONTAINShere",
   303  			wantMatch: true,
   304  		},
   305  		{
   306  			desc:    "contains match failure with ignore case",
   307  			matcher: containsMatcherIgnoreCase,
   308  			input:   "CON-TAINS-is-not-here",
   309  		},
   310  	}
   311  
   312  	for _, test := range tests {
   313  		t.Run(test.desc, func(t *testing.T) {
   314  			if gotMatch := test.matcher.Match(test.input); gotMatch != test.wantMatch {
   315  				t.Errorf("StringMatcher.Match(%s) returned %v, want %v", test.input, gotMatch, test.wantMatch)
   316  			}
   317  		})
   318  	}
   319  }
   320  
   321  func newStringP(s string) *string {
   322  	return &s
   323  }