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