snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
650
balancer/rls/internal/keys/builder_test.go
Normal file
650
balancer/rls/internal/keys/builder_test.go
Normal file
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
*
|
||||
* 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 keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
rlspb "google.golang.org/grpc/internal/proto/grpc_lookup_v1"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
goodKeyBuilder1 = &rlspb.GrpcKeyBuilder{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gFoo"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{
|
||||
{Key: "k1", Names: []string{"n1"}},
|
||||
{Key: "k2", Names: []string{"n1"}},
|
||||
},
|
||||
ExtraKeys: &rlspb.GrpcKeyBuilder_ExtraKeys{
|
||||
Host: "host",
|
||||
Service: "service",
|
||||
Method: "method",
|
||||
},
|
||||
ConstantKeys: map[string]string{
|
||||
"const-key-1": "const-val-1",
|
||||
"const-key-2": "const-val-2",
|
||||
},
|
||||
}
|
||||
goodKeyBuilder2 = &rlspb.GrpcKeyBuilder{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gBar", Method: "method1"},
|
||||
{Service: "gFoobar"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{
|
||||
{Key: "k1", Names: []string{"n1", "n2"}},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestMakeBuilderMap(t *testing.T) {
|
||||
gFooBuilder := builder{
|
||||
headerKeys: []matcher{{key: "k1", names: []string{"n1"}}, {key: "k2", names: []string{"n1"}}},
|
||||
constantKeys: map[string]string{
|
||||
"const-key-1": "const-val-1",
|
||||
"const-key-2": "const-val-2",
|
||||
},
|
||||
hostKey: "host",
|
||||
serviceKey: "service",
|
||||
methodKey: "method",
|
||||
}
|
||||
wantBuilderMap1 := map[string]builder{"/gFoo/": gFooBuilder}
|
||||
wantBuilderMap2 := map[string]builder{
|
||||
"/gFoo/": gFooBuilder,
|
||||
"/gBar/method1": {headerKeys: []matcher{{key: "k1", names: []string{"n1", "n2"}}}},
|
||||
"/gFoobar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1", "n2"}}}},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
cfg *rlspb.RouteLookupConfig
|
||||
wantBuilderMap BuilderMap
|
||||
}{
|
||||
{
|
||||
desc: "One good GrpcKeyBuilder",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{goodKeyBuilder1},
|
||||
},
|
||||
wantBuilderMap: wantBuilderMap1,
|
||||
},
|
||||
{
|
||||
desc: "Two good GrpcKeyBuilders",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{goodKeyBuilder1, goodKeyBuilder2},
|
||||
},
|
||||
wantBuilderMap: wantBuilderMap2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
builderMap, err := MakeBuilderMap(test.cfg)
|
||||
if err != nil || !builderMap.Equal(test.wantBuilderMap) {
|
||||
t.Errorf("MakeBuilderMap(%+v) returned {%v, %v}, want: {%v, nil}", test.cfg, builderMap, err, test.wantBuilderMap)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeBuilderMapErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
cfg *rlspb.RouteLookupConfig
|
||||
wantErrPrefix string
|
||||
}{
|
||||
{
|
||||
desc: "No GrpcKeyBuilder",
|
||||
cfg: &rlspb.RouteLookupConfig{},
|
||||
wantErrPrefix: "rls: RouteLookupConfig does not contain any GrpcKeyBuilder",
|
||||
},
|
||||
{
|
||||
desc: "Two GrpcKeyBuilders with same Name",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{goodKeyBuilder1, goodKeyBuilder1},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains repeated Name field",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder with empty Service field",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "bFoo", Method: "method1"},
|
||||
{Service: "bBar"},
|
||||
{Method: "method1"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}}},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains a Name field with no Service",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder with no Name",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{{}, goodKeyBuilder1},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig does not contain any Name",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder with requiredMatch field set",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{{Service: "bFoo", Method: "method1"}},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}, RequiredMatch: true}},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder two headers with same key",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gBar", Method: "method1"},
|
||||
{Service: "gFoobar"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{
|
||||
{Key: "k1", Names: []string{"n1", "n2"}},
|
||||
{Key: "k1", Names: []string{"n1", "n2"}},
|
||||
},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key \"k1\" across headers, constant_keys and extra_keys",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder repeated keys across headers and constant_keys",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gBar", Method: "method1"},
|
||||
{Service: "gFoobar"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}}},
|
||||
ConstantKeys: map[string]string{"k1": "v1"},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key \"k1\" across headers, constant_keys and extra_keys",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder repeated keys across headers and extra_keys",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gBar", Method: "method1"},
|
||||
{Service: "gFoobar"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}}},
|
||||
ExtraKeys: &rlspb.GrpcKeyBuilder_ExtraKeys{Method: "k1"},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key \"k1\" in extra_keys from constant_keys or headers",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder repeated keys across constant_keys and extra_keys",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{
|
||||
{Service: "gBar", Method: "method1"},
|
||||
{Service: "gFoobar"},
|
||||
},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}}},
|
||||
ConstantKeys: map[string]string{"host": "v1"},
|
||||
ExtraKeys: &rlspb.GrpcKeyBuilder_ExtraKeys{Host: "host"},
|
||||
},
|
||||
goodKeyBuilder1,
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key \"host\" in extra_keys from constant_keys or headers",
|
||||
},
|
||||
{
|
||||
desc: "GrpcKeyBuilder with slash in method name",
|
||||
cfg: &rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{
|
||||
{
|
||||
Names: []*rlspb.GrpcKeyBuilder_Name{{Service: "gBar", Method: "method1/foo"}},
|
||||
Headers: []*rlspb.NameMatcher{{Key: "k1", Names: []string{"n1", "n2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErrPrefix: "rls: GrpcKeyBuilder in RouteLookupConfig contains a method with a slash",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
builderMap, err := MakeBuilderMap(test.cfg)
|
||||
if builderMap != nil || !strings.HasPrefix(fmt.Sprint(err), test.wantErrPrefix) {
|
||||
t.Errorf("MakeBuilderMap(%+v) returned {%v, %v}, want: {nil, %v}", test.cfg, builderMap, err, test.wantErrPrefix)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLSKey(t *testing.T) {
|
||||
bm, err := MakeBuilderMap(&rlspb.RouteLookupConfig{
|
||||
GrpcKeybuilders: []*rlspb.GrpcKeyBuilder{goodKeyBuilder1, goodKeyBuilder2},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("MakeBuilderMap() failed: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
path string
|
||||
md metadata.MD
|
||||
wantKM KeyMap
|
||||
}{
|
||||
{
|
||||
// No keyBuilder is found for the provided service.
|
||||
desc: "service not found in key builder map",
|
||||
path: "/notFoundService/method",
|
||||
md: metadata.Pairs("n1", "v1", "n2", "v2"),
|
||||
wantKM: KeyMap{},
|
||||
},
|
||||
{
|
||||
// No keyBuilder is found for the provided method.
|
||||
desc: "method not found in key builder map",
|
||||
path: "/gBar/notFoundMethod",
|
||||
md: metadata.Pairs("n1", "v1", "n2", "v2"),
|
||||
wantKM: KeyMap{},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found, but none of the headers match.
|
||||
desc: "directPathMatch-NoMatchingKey",
|
||||
path: "/gBar/method1",
|
||||
md: metadata.Pairs("notMatchingKey", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{}, Str: ""},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found, and a single headers matches.
|
||||
desc: "directPathMatch-SingleKey",
|
||||
path: "/gBar/method1",
|
||||
md: metadata.Pairs("n1", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{"k1": "v1"}, Str: "k1=v1"},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found, and multiple headers match, but the first
|
||||
// match is chosen.
|
||||
desc: "directPathMatch-FirstMatchingKey",
|
||||
path: "/gBar/method1",
|
||||
md: metadata.Pairs("n2", "v2", "n1", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{"k1": "v1"}, Str: "k1=v1"},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found as a wildcard match, but none of the
|
||||
// headers match.
|
||||
desc: "wildcardPathMatch-NoMatchingKey",
|
||||
path: "/gFoobar/method1",
|
||||
md: metadata.Pairs("notMatchingKey", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{}, Str: ""},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found as a wildcard match, and a single headers
|
||||
// matches.
|
||||
desc: "wildcardPathMatch-SingleKey",
|
||||
path: "/gFoobar/method1",
|
||||
md: metadata.Pairs("n1", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{"k1": "v1"}, Str: "k1=v1"},
|
||||
},
|
||||
{
|
||||
// A keyBuilder is found as a wildcard match, and multiple headers
|
||||
// match, but the first match is chosen.
|
||||
desc: "wildcardPathMatch-FirstMatchingKey",
|
||||
path: "/gFoobar/method1",
|
||||
md: metadata.Pairs("n2", "v2", "n1", "v1"),
|
||||
wantKM: KeyMap{Map: map[string]string{"k1": "v1"}, Str: "k1=v1"},
|
||||
},
|
||||
{
|
||||
// Multiple headerKeys find hits in the provided request headers.
|
||||
desc: "multipleMatchers",
|
||||
path: "/gFoo/method1",
|
||||
md: metadata.Pairs("n2", "v2", "n1", "v1"),
|
||||
wantKM: KeyMap{
|
||||
Map: map[string]string{
|
||||
"const-key-1": "const-val-1",
|
||||
"const-key-2": "const-val-2",
|
||||
"host": "dummy-host",
|
||||
"service": "gFoo",
|
||||
"method": "method1",
|
||||
"k1": "v1",
|
||||
"k2": "v1",
|
||||
},
|
||||
Str: "const-key-1=const-val-1,const-key-2=const-val-2,host=dummy-host,k1=v1,k2=v1,method=method1,service=gFoo",
|
||||
},
|
||||
},
|
||||
{
|
||||
// A match is found for a header which is specified multiple times.
|
||||
// So, the values are joined with commas separating them.
|
||||
desc: "commaSeparated",
|
||||
path: "/gBar/method1",
|
||||
md: metadata.Pairs("n1", "v1", "n1", "v2", "n1", "v3"),
|
||||
wantKM: KeyMap{Map: map[string]string{"k1": "v1,v2,v3"}, Str: "k1=v1,v2,v3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if gotKM := bm.RLSKey(test.md, "dummy-host", test.path); !cmp.Equal(gotKM, test.wantKM) {
|
||||
t.Errorf("RLSKey(%+v, %s) = %+v, want %+v", test.md, test.path, gotKM, test.wantKM)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapToString(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
input map[string]string
|
||||
wantStr string
|
||||
}{
|
||||
{
|
||||
desc: "empty map",
|
||||
input: nil,
|
||||
wantStr: "",
|
||||
},
|
||||
{
|
||||
desc: "one key",
|
||||
input: map[string]string{
|
||||
"k1": "v1",
|
||||
},
|
||||
wantStr: "k1=v1",
|
||||
},
|
||||
{
|
||||
desc: "sorted keys",
|
||||
input: map[string]string{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
},
|
||||
wantStr: "k1=v1,k2=v2,k3=v3",
|
||||
},
|
||||
{
|
||||
desc: "unsorted keys",
|
||||
input: map[string]string{
|
||||
"k3": "v3",
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
wantStr: "k1=v1,k2=v2,k3=v3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if gotStr := mapToString(test.input); gotStr != test.wantStr {
|
||||
t.Errorf("mapToString(%v) = %s, want %s", test.input, gotStr, test.wantStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderMapEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
a BuilderMap
|
||||
b BuilderMap
|
||||
wantEqual bool
|
||||
}{
|
||||
{
|
||||
desc: "nil builder maps",
|
||||
a: nil,
|
||||
b: nil,
|
||||
wantEqual: true,
|
||||
},
|
||||
{
|
||||
desc: "empty builder maps",
|
||||
a: make(map[string]builder),
|
||||
b: make(map[string]builder),
|
||||
wantEqual: true,
|
||||
},
|
||||
{
|
||||
desc: "nil and non-nil builder maps",
|
||||
a: nil,
|
||||
b: map[string]builder{"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "empty and non-empty builder maps",
|
||||
a: make(map[string]builder),
|
||||
b: map[string]builder{"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different number of map keys",
|
||||
a: map[string]builder{
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
b: map[string]builder{
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different map keys",
|
||||
a: map[string]builder{
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
b: map[string]builder{
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "equal keys different values",
|
||||
a: map[string]builder{
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1", "n2"}}}},
|
||||
},
|
||||
b: map[string]builder{
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "good match",
|
||||
a: map[string]builder{
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
b: map[string]builder{
|
||||
"/gBar/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
"/gFoo/": {headerKeys: []matcher{{key: "k1", names: []string{"n1"}}}},
|
||||
},
|
||||
wantEqual: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if gotEqual := test.a.Equal(test.b); gotEqual != test.wantEqual {
|
||||
t.Errorf("BuilderMap.Equal(%v, %v) = %v, want %v", test.a, test.b, gotEqual, test.wantEqual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
a builder
|
||||
b builder
|
||||
wantEqual bool
|
||||
}{
|
||||
{
|
||||
desc: "nil builders",
|
||||
a: builder{headerKeys: nil},
|
||||
b: builder{headerKeys: nil},
|
||||
wantEqual: true,
|
||||
},
|
||||
{
|
||||
desc: "empty builders",
|
||||
a: builder{headerKeys: []matcher{}},
|
||||
b: builder{headerKeys: []matcher{}},
|
||||
wantEqual: true,
|
||||
},
|
||||
{
|
||||
desc: "empty and non-empty builders",
|
||||
a: builder{headerKeys: []matcher{}},
|
||||
b: builder{headerKeys: []matcher{{key: "foo"}}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different number of headerKeys",
|
||||
a: builder{headerKeys: []matcher{{key: "foo"}, {key: "bar"}}},
|
||||
b: builder{headerKeys: []matcher{{key: "foo"}}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "equal number but differing headerKeys",
|
||||
a: builder{headerKeys: []matcher{{key: "bar"}}},
|
||||
b: builder{headerKeys: []matcher{{key: "foo"}}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different number of constantKeys",
|
||||
a: builder{constantKeys: map[string]string{"k1": "v1"}},
|
||||
b: builder{constantKeys: map[string]string{"k1": "v1", "k2": "v2"}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "equal number but differing constantKeys",
|
||||
a: builder{constantKeys: map[string]string{"k1": "v1"}},
|
||||
b: builder{constantKeys: map[string]string{"k2": "v2"}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different hostKey",
|
||||
a: builder{hostKey: "host1"},
|
||||
b: builder{hostKey: "host2"},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different serviceKey",
|
||||
a: builder{hostKey: "service1"},
|
||||
b: builder{hostKey: "service2"},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different methodKey",
|
||||
a: builder{hostKey: "method1"},
|
||||
b: builder{hostKey: "method2"},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "equal",
|
||||
a: builder{
|
||||
headerKeys: []matcher{{key: "foo"}},
|
||||
constantKeys: map[string]string{"k1": "v1"},
|
||||
hostKey: "host",
|
||||
serviceKey: "/service/",
|
||||
methodKey: "method",
|
||||
},
|
||||
b: builder{
|
||||
headerKeys: []matcher{{key: "foo"}},
|
||||
constantKeys: map[string]string{"k1": "v1"},
|
||||
hostKey: "host",
|
||||
serviceKey: "/service/",
|
||||
methodKey: "method",
|
||||
},
|
||||
wantEqual: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if gotEqual := test.a.Equal(test.b); gotEqual != test.wantEqual {
|
||||
t.Errorf("builder.Equal(%v, %v) = %v, want %v", test.a, test.b, gotEqual, test.wantEqual)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// matcher helps extract a key from request headers based on a given name.
|
||||
func TestMatcherEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
a matcher
|
||||
b matcher
|
||||
wantEqual bool
|
||||
}{
|
||||
{
|
||||
desc: "different keys",
|
||||
a: matcher{key: "foo"},
|
||||
b: matcher{key: "bar"},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "different number of names",
|
||||
a: matcher{key: "foo", names: []string{"v1", "v2"}},
|
||||
b: matcher{key: "foo", names: []string{"v1"}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "equal number but differing names",
|
||||
a: matcher{key: "foo", names: []string{"v1", "v2"}},
|
||||
b: matcher{key: "foo", names: []string{"v1", "v22"}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "same names in different order",
|
||||
a: matcher{key: "foo", names: []string{"v2", "v1"}},
|
||||
b: matcher{key: "foo", names: []string{"v1", "v3"}},
|
||||
wantEqual: false,
|
||||
},
|
||||
{
|
||||
desc: "good match",
|
||||
a: matcher{key: "foo", names: []string{"v1", "v2"}},
|
||||
b: matcher{key: "foo", names: []string{"v1", "v2"}},
|
||||
wantEqual: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if gotEqual := test.a.Equal(test.b); gotEqual != test.wantEqual {
|
||||
t.Errorf("matcher.Equal(%v, %v) = %v, want %v", test.a, test.b, gotEqual, test.wantEqual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user