/* * * Copyright 2020 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" "google.golang.org/grpc/metadata" ) func TestHeaderExactMatcherMatch(t *testing.T) { tests := []struct { name string key, exact string md metadata.MD want bool invert bool }{ { name: "one value one match", key: "th", exact: "tv", md: metadata.Pairs("th", "tv"), want: true, }, { name: "two value one match", key: "th", exact: "tv", md: metadata.Pairs("th", "abc", "th", "tv"), // Doesn't match comma-concatenated string. want: false, }, { name: "two value match concatenated", key: "th", exact: "abc,tv", md: metadata.Pairs("th", "abc", "th", "tv"), want: true, }, { name: "not match", key: "th", exact: "tv", md: metadata.Pairs("th", "abc"), want: false, }, { name: "invert header not present", key: "th", exact: "tv", md: metadata.Pairs(":method", "GET"), want: false, invert: true, }, { name: "invert header match", key: "th", exact: "tv", md: metadata.Pairs("th", "tv"), want: false, invert: true, }, { name: "invert header not match", key: "th", exact: "tv", md: metadata.Pairs("th", "tvv"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hem := NewHeaderExactMatcher(tt.key, tt.exact, tt.invert) if got := hem.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestHeaderRegexMatcherMatch(t *testing.T) { tests := []struct { name string key, regexStr string md metadata.MD want bool invert bool }{ { name: "one value one match", key: "th", regexStr: "^t+v*$", md: metadata.Pairs("th", "tttvv"), want: true, }, { name: "two value one match", key: "th", regexStr: "^t+v*$", md: metadata.Pairs("th", "abc", "th", "tttvv"), want: false, }, { name: "two value match concatenated", key: "th", regexStr: "^[abc]*,t+v*$", md: metadata.Pairs("th", "abc", "th", "tttvv"), want: true, }, { name: "no match", key: "th", regexStr: "^t+v*$", md: metadata.Pairs("th", "abc"), want: false, }, { name: "no match because only part of value matches with regex", key: "header", regexStr: "^a+$", md: metadata.Pairs("header", "ab"), want: false, }, { name: "match because full value matches with regex", key: "header", regexStr: "^a+$", md: metadata.Pairs("header", "aa"), want: true, }, { name: "invert header not present", key: "th", regexStr: "^t+v*$", md: metadata.Pairs(":method", "GET"), want: false, invert: true, }, { name: "invert header match", key: "th", regexStr: "^t+v*$", md: metadata.Pairs("th", "tttvv"), want: false, invert: true, }, { name: "invert header not match", key: "th", regexStr: "^t+v*$", md: metadata.Pairs("th", "abc"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hrm := NewHeaderRegexMatcher(tt.key, regexp.MustCompile(tt.regexStr), tt.invert) if got := hrm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestHeaderRangeMatcherMatch(t *testing.T) { tests := []struct { name string key string start, end int64 md metadata.MD want bool invert bool }{ { name: "match", key: "th", start: 1, end: 10, md: metadata.Pairs("th", "5"), want: true, }, { name: "equal to start", key: "th", start: 1, end: 10, md: metadata.Pairs("th", "1"), want: true, }, { name: "equal to end", key: "th", start: 1, end: 10, md: metadata.Pairs("th", "10"), want: false, }, { name: "negative", key: "th", start: -10, end: 10, md: metadata.Pairs("th", "-5"), want: true, }, { name: "invert header not present", key: "th", start: 1, end: 10, md: metadata.Pairs(":method", "GET"), want: false, invert: true, }, { name: "invert header match", key: "th", start: 1, end: 10, md: metadata.Pairs("th", "5"), want: false, invert: true, }, { name: "invert header not match", key: "th", start: 1, end: 9, md: metadata.Pairs("th", "10"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hrm := NewHeaderRangeMatcher(tt.key, tt.start, tt.end, tt.invert) if got := hrm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestHeaderPresentMatcherMatch(t *testing.T) { tests := []struct { name string key string present bool md metadata.MD want bool invert bool }{ { name: "want present is present", key: "th", present: true, md: metadata.Pairs("th", "tv"), want: true, }, { name: "want present not present", key: "th", present: true, md: metadata.Pairs("abc", "tv"), want: false, }, { name: "want not present is present", key: "th", present: false, md: metadata.Pairs("th", "tv"), want: false, }, { name: "want not present is not present", key: "th", present: false, md: metadata.Pairs("abc", "tv"), want: true, }, { name: "invert header not present", key: "th", present: true, md: metadata.Pairs(":method", "GET"), want: true, invert: true, }, { name: "invert header match", key: "th", present: true, md: metadata.Pairs("th", "tv"), want: false, invert: true, }, { name: "invert header not match", key: "th", present: true, md: metadata.Pairs(":method", "GET"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hpm := NewHeaderPresentMatcher(tt.key, tt.present, tt.invert) if got := hpm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestHeaderPrefixMatcherMatch(t *testing.T) { tests := []struct { name string key, prefix string md metadata.MD want bool invert bool }{ { name: "one value one match", key: "th", prefix: "tv", md: metadata.Pairs("th", "tv123"), want: true, }, { name: "two value one match", key: "th", prefix: "tv", md: metadata.Pairs("th", "abc", "th", "tv123"), want: false, }, { name: "two value match concatenated", key: "th", prefix: "tv", md: metadata.Pairs("th", "tv123", "th", "abc"), want: true, }, { name: "not match", key: "th", prefix: "tv", md: metadata.Pairs("th", "abc"), want: false, }, { name: "invert header not present", key: "th", prefix: "tv", md: metadata.Pairs(":method", "GET"), want: false, invert: true, }, { name: "invert header match", key: "th", prefix: "tv", md: metadata.Pairs("th", "tv123"), want: false, invert: true, }, { name: "invert header not match", key: "th", prefix: "tv", md: metadata.Pairs("th", "abc"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hpm := NewHeaderPrefixMatcher(tt.key, tt.prefix, tt.invert) if got := hpm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestHeaderSuffixMatcherMatch(t *testing.T) { tests := []struct { name string key, suffix string md metadata.MD want bool invert bool }{ { name: "one value one match", key: "th", suffix: "tv", md: metadata.Pairs("th", "123tv"), want: true, }, { name: "two value one match", key: "th", suffix: "tv", md: metadata.Pairs("th", "123tv", "th", "abc"), want: false, }, { name: "two value match concatenated", key: "th", suffix: "tv", md: metadata.Pairs("th", "abc", "th", "123tv"), want: true, }, { name: "not match", key: "th", suffix: "tv", md: metadata.Pairs("th", "abc"), want: false, }, { name: "invert header not present", key: "th", suffix: "tv", md: metadata.Pairs(":method", "GET"), want: false, invert: true, }, { name: "invert header match", key: "th", suffix: "tv", md: metadata.Pairs("th", "123tv"), want: false, invert: true, }, { name: "invert header not match", key: "th", suffix: "tv", md: metadata.Pairs("th", "abc"), want: true, invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hsm := NewHeaderSuffixMatcher(tt.key, tt.suffix, tt.invert) if got := hsm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } }