1
17
18 package xdsresource
19
20 import (
21 "context"
22 "testing"
23
24 "github.com/google/go-cmp/cmp"
25 "google.golang.org/grpc/internal/grpcrand"
26 "google.golang.org/grpc/internal/grpcutil"
27 iresolver "google.golang.org/grpc/internal/resolver"
28 "google.golang.org/grpc/internal/xds/matcher"
29 "google.golang.org/grpc/metadata"
30 "google.golang.org/protobuf/proto"
31 )
32
33 func (s) TestAndMatcherMatch(t *testing.T) {
34 tests := []struct {
35 name string
36 pm pathMatcher
37 hm matcher.HeaderMatcher
38 info iresolver.RPCInfo
39 want bool
40 }{
41 {
42 name: "both match",
43 pm: newPathExactMatcher("/a/b", false),
44 hm: matcher.NewHeaderExactMatcher("th", "tv", false),
45 info: iresolver.RPCInfo{
46 Method: "/a/b",
47 Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")),
48 },
49 want: true,
50 },
51 {
52 name: "both match with path case insensitive",
53 pm: newPathExactMatcher("/A/B", true),
54 hm: matcher.NewHeaderExactMatcher("th", "tv", false),
55 info: iresolver.RPCInfo{
56 Method: "/a/b",
57 Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")),
58 },
59 want: true,
60 },
61 {
62 name: "only one match",
63 pm: newPathExactMatcher("/a/b", false),
64 hm: matcher.NewHeaderExactMatcher("th", "tv", false),
65 info: iresolver.RPCInfo{
66 Method: "/z/y",
67 Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")),
68 },
69 want: false,
70 },
71 {
72 name: "both not match",
73 pm: newPathExactMatcher("/z/y", false),
74 hm: matcher.NewHeaderExactMatcher("th", "abc", false),
75 info: iresolver.RPCInfo{
76 Method: "/a/b",
77 Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")),
78 },
79 want: false,
80 },
81 {
82 name: "fake header",
83 pm: newPathPrefixMatcher("/", false),
84 hm: matcher.NewHeaderExactMatcher("content-type", "fake", false),
85 info: iresolver.RPCInfo{
86 Method: "/a/b",
87 Context: grpcutil.WithExtraMetadata(context.Background(), metadata.Pairs(
88 "content-type", "fake",
89 )),
90 },
91 want: true,
92 },
93 {
94 name: "binary header",
95 pm: newPathPrefixMatcher("/", false),
96 hm: matcher.NewHeaderPresentMatcher("t-bin", true, false),
97 info: iresolver.RPCInfo{
98 Method: "/a/b",
99 Context: grpcutil.WithExtraMetadata(
100 metadata.NewOutgoingContext(context.Background(), metadata.Pairs("t-bin", "123")), metadata.Pairs(
101 "content-type", "fake",
102 )),
103 },
104
105 want: false,
106 },
107 }
108 for _, tt := range tests {
109 t.Run(tt.name, func(t *testing.T) {
110 a := newCompositeMatcher(tt.pm, []matcher.HeaderMatcher{tt.hm}, nil)
111 if got := a.Match(tt.info); got != tt.want {
112 t.Errorf("match() = %v, want %v", got, tt.want)
113 }
114 })
115 }
116 }
117
118 func (s) TestFractionMatcherMatch(t *testing.T) {
119 const fraction = 500000
120 fm := newFractionMatcher(fraction)
121 defer func() {
122 RandInt63n = grpcrand.Int63n
123 }()
124
125
126 RandInt63n = func(n int64) int64 {
127 return fraction + 1
128 }
129 if matched := fm.match(); matched {
130 t.Errorf("match() = %v, want not match", matched)
131 }
132
133
134 RandInt63n = func(n int64) int64 {
135 return fraction
136 }
137 if matched := fm.match(); !matched {
138 t.Errorf("match() = %v, want match", matched)
139 }
140
141
142 RandInt63n = func(n int64) int64 {
143 return fraction - 1
144 }
145 if matched := fm.match(); !matched {
146 t.Errorf("match() = %v, want match", matched)
147 }
148 }
149
150 func (s) TestMatchTypeForDomain(t *testing.T) {
151 tests := []struct {
152 d string
153 want domainMatchType
154 }{
155 {d: "", want: domainMatchTypeInvalid},
156 {d: "*", want: domainMatchTypeUniversal},
157 {d: "bar.*", want: domainMatchTypePrefix},
158 {d: "*.abc.com", want: domainMatchTypeSuffix},
159 {d: "foo.bar.com", want: domainMatchTypeExact},
160 {d: "foo.*.com", want: domainMatchTypeInvalid},
161 }
162 for _, tt := range tests {
163 if got := matchTypeForDomain(tt.d); got != tt.want {
164 t.Errorf("matchTypeForDomain(%q) = %v, want %v", tt.d, got, tt.want)
165 }
166 }
167 }
168
169 func (s) TestMatch(t *testing.T) {
170 tests := []struct {
171 name string
172 domain string
173 host string
174 wantTyp domainMatchType
175 wantMatched bool
176 }{
177 {name: "invalid-empty", domain: "", host: "", wantTyp: domainMatchTypeInvalid, wantMatched: false},
178 {name: "invalid", domain: "a.*.b", host: "", wantTyp: domainMatchTypeInvalid, wantMatched: false},
179 {name: "universal", domain: "*", host: "abc.com", wantTyp: domainMatchTypeUniversal, wantMatched: true},
180 {name: "prefix-match", domain: "abc.*", host: "abc.123", wantTyp: domainMatchTypePrefix, wantMatched: true},
181 {name: "prefix-no-match", domain: "abc.*", host: "abcd.123", wantTyp: domainMatchTypePrefix, wantMatched: false},
182 {name: "suffix-match", domain: "*.123", host: "abc.123", wantTyp: domainMatchTypeSuffix, wantMatched: true},
183 {name: "suffix-no-match", domain: "*.123", host: "abc.1234", wantTyp: domainMatchTypeSuffix, wantMatched: false},
184 {name: "exact-match", domain: "foo.bar", host: "foo.bar", wantTyp: domainMatchTypeExact, wantMatched: true},
185 {name: "exact-no-match", domain: "foo.bar.com", host: "foo.bar", wantTyp: domainMatchTypeExact, wantMatched: false},
186 }
187 for _, tt := range tests {
188 t.Run(tt.name, func(t *testing.T) {
189 if gotTyp, gotMatched := match(tt.domain, tt.host); gotTyp != tt.wantTyp || gotMatched != tt.wantMatched {
190 t.Errorf("match() = %v, %v, want %v, %v", gotTyp, gotMatched, tt.wantTyp, tt.wantMatched)
191 }
192 })
193 }
194 }
195
196 func (s) TestFindBestMatchingVirtualHost(t *testing.T) {
197 var (
198 oneExactMatch = &VirtualHost{Domains: []string{"foo.bar.com"}}
199 oneSuffixMatch = &VirtualHost{Domains: []string{"*.bar.com"}}
200 onePrefixMatch = &VirtualHost{Domains: []string{"foo.bar.*"}}
201 oneUniversalMatch = &VirtualHost{Domains: []string{"*"}}
202 longExactMatch = &VirtualHost{Domains: []string{"v2.foo.bar.com"}}
203 multipleMatch = &VirtualHost{Domains: []string{"pi.foo.bar.com", "314.*", "*.159"}}
204 vhs = []*VirtualHost{oneExactMatch, oneSuffixMatch, onePrefixMatch, oneUniversalMatch, longExactMatch, multipleMatch}
205 )
206
207 tests := []struct {
208 name string
209 host string
210 vHosts []*VirtualHost
211 want *VirtualHost
212 }{
213 {name: "exact-match", host: "foo.bar.com", vHosts: vhs, want: oneExactMatch},
214 {name: "suffix-match", host: "123.bar.com", vHosts: vhs, want: oneSuffixMatch},
215 {name: "prefix-match", host: "foo.bar.org", vHosts: vhs, want: onePrefixMatch},
216 {name: "universal-match", host: "abc.123", vHosts: vhs, want: oneUniversalMatch},
217 {name: "long-exact-match", host: "v2.foo.bar.com", vHosts: vhs, want: longExactMatch},
218
219 {name: "multiple-match-exact", host: "pi.foo.bar.com", vHosts: vhs, want: multipleMatch},
220
221 {name: "multiple-match-suffix", host: "foo.bar.159", vHosts: vhs, want: multipleMatch},
222
223 {name: "multiple-match-prefix", host: "314.bar.com", vHosts: vhs, want: oneSuffixMatch},
224 }
225 for _, tt := range tests {
226 t.Run(tt.name, func(t *testing.T) {
227 if got := FindBestMatchingVirtualHost(tt.host, tt.vHosts); !cmp.Equal(got, tt.want, cmp.Comparer(proto.Equal)) {
228 t.Errorf("FindBestMatchingxdsclient.VirtualHost() = %v, want %v", got, tt.want)
229 }
230 })
231 }
232 }
233
View as plain text