/* * * 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 xdsrouting import ( "context" "testing" "google.golang.org/grpc/balancer" "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/metadata" ) func TestAndMatcherMatch(t *testing.T) { tests := []struct { name string pm pathMatcherInterface hm headerMatcherInterface info balancer.PickInfo want bool }{ { name: "both match", pm: newPathExactMatcher("/a/b"), hm: newHeaderExactMatcher("th", "tv"), info: balancer.PickInfo{ FullMethodName: "/a/b", Ctx: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), }, want: true, }, { name: "only one match", pm: newPathExactMatcher("/a/b"), hm: newHeaderExactMatcher("th", "tv"), info: balancer.PickInfo{ FullMethodName: "/z/y", Ctx: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), }, want: false, }, { name: "both not match", pm: newPathExactMatcher("/z/y"), hm: newHeaderExactMatcher("th", "abc"), info: balancer.PickInfo{ FullMethodName: "/a/b", Ctx: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), }, want: false, }, { name: "fake header", pm: newPathPrefixMatcher("/"), hm: newHeaderExactMatcher("content-type", "fake"), info: balancer.PickInfo{ FullMethodName: "/a/b", Ctx: grpcutil.WithExtraMetadata(context.Background(), metadata.Pairs( "content-type", "fake", )), }, want: true, }, { name: "binary header", pm: newPathPrefixMatcher("/"), hm: newHeaderPresentMatcher("t-bin", true), info: balancer.PickInfo{ FullMethodName: "/a/b", Ctx: grpcutil.WithExtraMetadata( metadata.NewOutgoingContext(context.Background(), metadata.Pairs("t-bin", "123")), metadata.Pairs( "content-type", "fake", )), }, // Shouldn't match binary header, even though it's in metadata. want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := newCompositeMatcher(tt.pm, []headerMatcherInterface{tt.hm}, nil) if got := a.match(tt.info); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } }) } } func TestFractionMatcherMatch(t *testing.T) { const fraction = 500000 fm := newFractionMatcher(fraction) defer func() { grpcrandInt63n = grpcrand.Int63n }() // rand > fraction, should return false. grpcrandInt63n = func(n int64) int64 { return fraction + 1 } if matched := fm.match(); matched { t.Errorf("match() = %v, want not match", matched) } // rand == fraction, should return true. grpcrandInt63n = func(n int64) int64 { return fraction } if matched := fm.match(); !matched { t.Errorf("match() = %v, want match", matched) } // rand < fraction, should return true. grpcrandInt63n = func(n int64) int64 { return fraction - 1 } if matched := fm.match(); !matched { t.Errorf("match() = %v, want match", matched) } } func TestCompositeMatcherEqual(t *testing.T) { tests := []struct { name string pm pathMatcherInterface hms []headerMatcherInterface fm *fractionMatcher mm *compositeMatcher want bool }{ { name: "equal", pm: newPathExactMatcher("/a/b"), mm: newCompositeMatcher(newPathExactMatcher("/a/b"), nil, nil), want: true, }, { name: "no path matcher", pm: nil, mm: newCompositeMatcher(nil, nil, nil), want: true, }, { name: "not equal", pm: newPathExactMatcher("/a/b"), fm: newFractionMatcher(123), mm: newCompositeMatcher(newPathExactMatcher("/a/b"), nil, nil), want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := newCompositeMatcher(tt.pm, tt.hms, tt.fm) if got := a.equal(tt.mm); got != tt.want { t.Errorf("equal() = %v, want %v", got, tt.want) } }) } }