| /* |
| * |
| * Copyright 2021 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package matcher |
| |
| import ( |
| "regexp" |
| "testing" |
| |
| v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| func TestStringMatcherFromProto(t *testing.T) { |
| tests := []struct { |
| desc string |
| inputProto *v3matcherpb.StringMatcher |
| wantMatcher StringMatcher |
| wantErr bool |
| }{ |
| { |
| desc: "nil proto", |
| wantErr: true, |
| }, |
| { |
| desc: "empty prefix", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}, |
| }, |
| wantErr: true, |
| }, |
| { |
| desc: "empty suffix", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}, |
| }, |
| wantErr: true, |
| }, |
| { |
| desc: "empty contains", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}, |
| }, |
| wantErr: true, |
| }, |
| { |
| desc: "invalid regex", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{ |
| SafeRegex: &v3matcherpb.RegexMatcher{Regex: "??"}, |
| }, |
| }, |
| wantErr: true, |
| }, |
| { |
| desc: "happy case exact", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"}, |
| }, |
| wantMatcher: StringMatcher{exactMatch: newStringP("exact")}, |
| }, |
| { |
| desc: "happy case exact ignore case", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "EXACT"}, |
| IgnoreCase: true, |
| }, |
| wantMatcher: StringMatcher{ |
| exactMatch: newStringP("exact"), |
| ignoreCase: true, |
| }, |
| }, |
| { |
| desc: "happy case prefix", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"}, |
| }, |
| wantMatcher: StringMatcher{prefixMatch: newStringP("prefix")}, |
| }, |
| { |
| desc: "happy case prefix ignore case", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "PREFIX"}, |
| IgnoreCase: true, |
| }, |
| wantMatcher: StringMatcher{ |
| prefixMatch: newStringP("prefix"), |
| ignoreCase: true, |
| }, |
| }, |
| { |
| desc: "happy case suffix", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"}, |
| }, |
| wantMatcher: StringMatcher{suffixMatch: newStringP("suffix")}, |
| }, |
| { |
| desc: "happy case suffix ignore case", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "SUFFIX"}, |
| IgnoreCase: true, |
| }, |
| wantMatcher: StringMatcher{ |
| suffixMatch: newStringP("suffix"), |
| ignoreCase: true, |
| }, |
| }, |
| { |
| desc: "happy case regex", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{ |
| SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"}, |
| }, |
| }, |
| wantMatcher: StringMatcher{regexMatch: regexp.MustCompile("good?regex?")}, |
| }, |
| { |
| desc: "happy case contains", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"}, |
| }, |
| wantMatcher: StringMatcher{containsMatch: newStringP("contains")}, |
| }, |
| { |
| desc: "happy case contains ignore case", |
| inputProto: &v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "CONTAINS"}, |
| IgnoreCase: true, |
| }, |
| wantMatcher: StringMatcher{ |
| containsMatch: newStringP("contains"), |
| ignoreCase: true, |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.desc, func(t *testing.T) { |
| gotMatcher, err := StringMatcherFromProto(test.inputProto) |
| if (err != nil) != test.wantErr { |
| t.Fatalf("StringMatcherFromProto(%+v) returned err: %v, wantErr: %v", test.inputProto, err, test.wantErr) |
| } |
| if diff := cmp.Diff(gotMatcher, test.wantMatcher, cmp.AllowUnexported(regexp.Regexp{})); diff != "" { |
| t.Fatalf("StringMatcherFromProto(%+v) returned unexpected diff (-got, +want):\n%s", test.inputProto, diff) |
| } |
| }) |
| } |
| } |
| |
| func TestMatch(t *testing.T) { |
| var ( |
| exactMatcher, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"}}) |
| prefixMatcher, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"}}) |
| suffixMatcher, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"}}) |
| regexMatcher, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"}}}) |
| containsMatcher, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"}}) |
| exactMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"}, |
| IgnoreCase: true, |
| }) |
| prefixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"}, |
| IgnoreCase: true, |
| }) |
| suffixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"}, |
| IgnoreCase: true, |
| }) |
| containsMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{ |
| MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"}, |
| IgnoreCase: true, |
| }) |
| ) |
| |
| tests := []struct { |
| desc string |
| matcher StringMatcher |
| input string |
| wantMatch bool |
| }{ |
| { |
| desc: "exact match success", |
| matcher: exactMatcher, |
| input: "exact", |
| wantMatch: true, |
| }, |
| { |
| desc: "exact match failure", |
| matcher: exactMatcher, |
| input: "not-exact", |
| }, |
| { |
| desc: "exact match success with ignore case", |
| matcher: exactMatcherIgnoreCase, |
| input: "EXACT", |
| wantMatch: true, |
| }, |
| { |
| desc: "exact match failure with ignore case", |
| matcher: exactMatcherIgnoreCase, |
| input: "not-exact", |
| }, |
| { |
| desc: "prefix match success", |
| matcher: prefixMatcher, |
| input: "prefixIsHere", |
| wantMatch: true, |
| }, |
| { |
| desc: "prefix match failure", |
| matcher: prefixMatcher, |
| input: "not-prefix", |
| }, |
| { |
| desc: "prefix match success with ignore case", |
| matcher: prefixMatcherIgnoreCase, |
| input: "PREFIXisHere", |
| wantMatch: true, |
| }, |
| { |
| desc: "prefix match failure with ignore case", |
| matcher: prefixMatcherIgnoreCase, |
| input: "not-PREFIX", |
| }, |
| { |
| desc: "suffix match success", |
| matcher: suffixMatcher, |
| input: "hereIsThesuffix", |
| wantMatch: true, |
| }, |
| { |
| desc: "suffix match failure", |
| matcher: suffixMatcher, |
| input: "suffix-is-not-here", |
| }, |
| { |
| desc: "suffix match success with ignore case", |
| matcher: suffixMatcherIgnoreCase, |
| input: "hereIsTheSuFFix", |
| wantMatch: true, |
| }, |
| { |
| desc: "suffix match failure with ignore case", |
| matcher: suffixMatcherIgnoreCase, |
| input: "SUFFIX-is-not-here", |
| }, |
| { |
| desc: "regex match success", |
| matcher: regexMatcher, |
| input: "goodregex", |
| wantMatch: true, |
| }, |
| { |
| desc: "regex match failure because only part match", |
| matcher: regexMatcher, |
| input: "goodregexa", |
| wantMatch: false, |
| }, |
| { |
| desc: "regex match failure", |
| matcher: regexMatcher, |
| input: "regex-is-not-here", |
| }, |
| { |
| desc: "contains match success", |
| matcher: containsMatcher, |
| input: "IScontainsHERE", |
| wantMatch: true, |
| }, |
| { |
| desc: "contains match failure", |
| matcher: containsMatcher, |
| input: "con-tains-is-not-here", |
| }, |
| { |
| desc: "contains match success with ignore case", |
| matcher: containsMatcherIgnoreCase, |
| input: "isCONTAINShere", |
| wantMatch: true, |
| }, |
| { |
| desc: "contains match failure with ignore case", |
| matcher: containsMatcherIgnoreCase, |
| input: "CON-TAINS-is-not-here", |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.desc, func(t *testing.T) { |
| if gotMatch := test.matcher.Match(test.input); gotMatch != test.wantMatch { |
| t.Errorf("StringMatcher.Match(%s) returned %v, want %v", test.input, gotMatch, test.wantMatch) |
| } |
| }) |
| } |
| } |
| |
| func newStringP(s string) *string { |
| return &s |
| } |