/* * * 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 resolver import ( "context" "regexp" "testing" xxhash "github.com/cespare/xxhash/v2" "github.com/google/go-cmp/cmp" iresolver "google.golang.org/grpc/internal/resolver" "google.golang.org/grpc/metadata" _ "google.golang.org/grpc/xds/internal/balancer/cdsbalancer" // To parse LB config "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" ) func (s) TestPruneActiveClusters(t *testing.T) { r := &xdsResolver{activeClusters: map[string]*clusterInfo{ "zero": {refCount: 0}, "one": {refCount: 1}, "two": {refCount: 2}, "anotherzero": {refCount: 0}, }} want := map[string]*clusterInfo{ "one": {refCount: 1}, "two": {refCount: 2}, } r.pruneActiveClusters() if d := cmp.Diff(r.activeClusters, want, cmp.AllowUnexported(clusterInfo{})); d != "" { t.Fatalf("r.activeClusters = %v; want %v\nDiffs: %v", r.activeClusters, want, d) } } func (s) TestGenerateRequestHash(t *testing.T) { const channelID = 12378921 cs := &configSelector{ r: &xdsResolver{ cc: &testClientConn{}, channelID: channelID, }, } tests := []struct { name string hashPolicies []*xdsresource.HashPolicy requestHashWant uint64 rpcInfo iresolver.RPCInfo }{ // TestGenerateRequestHashHeaders tests generating request hashes for // hash policies that specify to hash headers. { name: "test-generate-request-hash-headers", hashPolicies: []*xdsresource.HashPolicy{{ HashPolicyType: xdsresource.HashPolicyTypeHeader, HeaderName: ":path", Regex: func() *regexp.Regexp { return regexp.MustCompile("/products") }(), // Will replace /products with /new-products, to test find and replace functionality. RegexSubstitution: "/new-products", }}, requestHashWant: xxhash.Sum64String("/new-products"), rpcInfo: iresolver.RPCInfo{ Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs(":path", "/products")), Method: "/some-method", }, }, // TestGenerateHashChannelID tests generating request hashes for hash // policies that specify to hash something that uniquely identifies the // ClientConn (the pointer). { name: "test-generate-request-hash-channel-id", hashPolicies: []*xdsresource.HashPolicy{{ HashPolicyType: xdsresource.HashPolicyTypeChannelID, }}, requestHashWant: channelID, rpcInfo: iresolver.RPCInfo{}, }, // TestGenerateRequestHashEmptyString tests generating request hashes // for hash policies that specify to hash headers and replace empty // strings in the headers. { name: "test-generate-request-hash-empty-string", hashPolicies: []*xdsresource.HashPolicy{{ HashPolicyType: xdsresource.HashPolicyTypeHeader, HeaderName: ":path", Regex: func() *regexp.Regexp { return regexp.MustCompile("") }(), RegexSubstitution: "e", }}, requestHashWant: xxhash.Sum64String("eaebece"), rpcInfo: iresolver.RPCInfo{ Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs(":path", "abc")), Method: "/some-method", }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { requestHashGot := cs.generateHash(test.rpcInfo, test.hashPolicies) if requestHashGot != test.requestHashWant { t.Fatalf("requestHashGot = %v, requestHashWant = %v", requestHashGot, test.requestHashWant) } }) } }