1
18
19 package grpc
20
21 import (
22 "encoding/json"
23 "fmt"
24 "reflect"
25 "testing"
26 "time"
27
28 "google.golang.org/grpc/balancer"
29 "google.golang.org/grpc/internal/balancer/gracefulswitch"
30 "google.golang.org/grpc/serviceconfig"
31 )
32
33 type parseTestCase struct {
34 scjs string
35 wantSC *ServiceConfig
36 wantErr bool
37 }
38
39 func lbConfigFor(t *testing.T, name string, cfg serviceconfig.LoadBalancingConfig) serviceconfig.LoadBalancingConfig {
40 if name == "" {
41 name = "pick_first"
42 cfg = struct {
43 serviceconfig.LoadBalancingConfig
44 }{}
45 }
46 d := []map[string]any{{name: cfg}}
47 strCfg, err := json.Marshal(d)
48 t.Logf("strCfg = %v", string(strCfg))
49 if err != nil {
50 t.Fatalf("Error parsing config: %v", err)
51 }
52 parsedCfg, err := gracefulswitch.ParseConfig(strCfg)
53 if err != nil {
54 t.Fatalf("Error parsing config: %v", err)
55 }
56 return parsedCfg
57 }
58
59 func runParseTests(t *testing.T, testCases []parseTestCase) {
60 t.Helper()
61 for i, c := range testCases {
62 t.Run(fmt.Sprint(i), func(t *testing.T) {
63 scpr := parseServiceConfig(c.scjs)
64 var sc *ServiceConfig
65 sc, _ = scpr.Config.(*ServiceConfig)
66 if !c.wantErr {
67 c.wantSC.rawJSONString = c.scjs
68 }
69 if c.wantErr != (scpr.Err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
70 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, scpr.Err, c.wantSC, c.wantErr)
71 }
72 })
73 }
74 }
75
76 type pbbData struct {
77 serviceconfig.LoadBalancingConfig
78 Foo string
79 Bar int
80 }
81
82 type parseBalancerBuilder struct{}
83
84 func (parseBalancerBuilder) Name() string {
85 return "pbb"
86 }
87
88 func (parseBalancerBuilder) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
89 d := pbbData{}
90 if err := json.Unmarshal(c, &d); err != nil {
91 return nil, err
92 }
93 return d, nil
94 }
95
96 func (parseBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
97 panic("unimplemented")
98 }
99
100 func init() {
101 balancer.Register(parseBalancerBuilder{})
102 }
103
104 func (s) TestParseLBConfig(t *testing.T) {
105 testcases := []parseTestCase{
106 {
107 `{
108 "loadBalancingConfig": [{"pbb": { "foo": "hi" } }]
109 }`,
110 &ServiceConfig{
111 Methods: make(map[string]MethodConfig),
112 lbConfig: lbConfigFor(t, "pbb", pbbData{Foo: "hi"}),
113 },
114 false,
115 },
116 }
117 runParseTests(t, testcases)
118 }
119
120 func (s) TestParseNoLBConfigSupported(t *testing.T) {
121
122
123 testcases := []parseTestCase{
124 {
125 scjs: `{
126 "loadBalancingConfig": [{"not_a_balancer1": {} }, {"not_a_balancer2": {}}]
127 }`,
128 wantErr: true,
129 }, {
130 scjs: `{"loadBalancingConfig": []}`,
131 wantErr: true,
132 },
133 }
134 runParseTests(t, testcases)
135 }
136
137 func (s) TestParseLoadBalancer(t *testing.T) {
138 testcases := []parseTestCase{
139 {
140 `{
141 "loadBalancingPolicy": "round_robin",
142 "methodConfig": [
143 {
144 "name": [
145 {
146 "service": "foo",
147 "method": "Bar"
148 }
149 ],
150 "waitForReady": true
151 }
152 ]
153 }`,
154 &ServiceConfig{
155 Methods: map[string]MethodConfig{
156 "/foo/Bar": {
157 WaitForReady: newBool(true),
158 },
159 },
160 lbConfig: lbConfigFor(t, "round_robin", nil),
161 },
162 false,
163 },
164 {
165 `{
166 "loadBalancingPolicy": 1,
167 "methodConfig": [
168 {
169 "name": [
170 {
171 "service": "foo",
172 "method": "Bar"
173 }
174 ],
175 "waitForReady": false
176 }
177 ]
178 }`,
179 nil,
180 true,
181 },
182 }
183 runParseTests(t, testcases)
184 }
185
186 func (s) TestParseWaitForReady(t *testing.T) {
187 testcases := []parseTestCase{
188 {
189 `{
190 "methodConfig": [
191 {
192 "name": [
193 {
194 "service": "foo",
195 "method": "Bar"
196 }
197 ],
198 "waitForReady": true
199 }
200 ]
201 }`,
202 &ServiceConfig{
203 Methods: map[string]MethodConfig{
204 "/foo/Bar": {
205 WaitForReady: newBool(true),
206 },
207 },
208 lbConfig: lbConfigFor(t, "", nil),
209 },
210 false,
211 },
212 {
213 `{
214 "methodConfig": [
215 {
216 "name": [
217 {
218 "service": "foo",
219 "method": "Bar"
220 }
221 ],
222 "waitForReady": false
223 }
224 ]
225 }`,
226 &ServiceConfig{
227 Methods: map[string]MethodConfig{
228 "/foo/Bar": {
229 WaitForReady: newBool(false),
230 },
231 },
232 lbConfig: lbConfigFor(t, "", nil),
233 },
234 false,
235 },
236 {
237 `{
238 "methodConfig": [
239 {
240 "name": [
241 {
242 "service": "foo",
243 "method": "Bar"
244 }
245 ],
246 "waitForReady": fall
247 },
248 {
249 "name": [
250 {
251 "service": "foo",
252 "method": "Bar"
253 }
254 ],
255 "waitForReady": true
256 }
257 ]
258 }`,
259 nil,
260 true,
261 },
262 }
263
264 runParseTests(t, testcases)
265 }
266
267 func (s) TestParseTimeOut(t *testing.T) {
268 testcases := []parseTestCase{
269 {
270 `{
271 "methodConfig": [
272 {
273 "name": [
274 {
275 "service": "foo",
276 "method": "Bar"
277 }
278 ],
279 "timeout": "1s"
280 }
281 ]
282 }`,
283 &ServiceConfig{
284 Methods: map[string]MethodConfig{
285 "/foo/Bar": {
286 Timeout: newDuration(time.Second),
287 },
288 },
289 lbConfig: lbConfigFor(t, "", nil),
290 },
291 false,
292 },
293 {
294 `{
295 "methodConfig": [
296 {
297 "name": [
298 {
299 "service": "foo",
300 "method": "Bar"
301 }
302 ],
303 "timeout": "3c"
304 }
305 ]
306 }`,
307 nil,
308 true,
309 },
310 {
311 `{
312 "methodConfig": [
313 {
314 "name": [
315 {
316 "service": "foo",
317 "method": "Bar"
318 }
319 ],
320 "timeout": "3c"
321 },
322 {
323 "name": [
324 {
325 "service": "foo",
326 "method": "Bar"
327 }
328 ],
329 "timeout": "1s"
330 }
331 ]
332 }`,
333 nil,
334 true,
335 },
336 }
337
338 runParseTests(t, testcases)
339 }
340
341 func (s) TestParseMsgSize(t *testing.T) {
342 testcases := []parseTestCase{
343 {
344 `{
345 "methodConfig": [
346 {
347 "name": [
348 {
349 "service": "foo",
350 "method": "Bar"
351 }
352 ],
353 "maxRequestMessageBytes": 1024,
354 "maxResponseMessageBytes": 2048
355 }
356 ]
357 }`,
358 &ServiceConfig{
359 Methods: map[string]MethodConfig{
360 "/foo/Bar": {
361 MaxReqSize: newInt(1024),
362 MaxRespSize: newInt(2048),
363 },
364 },
365 lbConfig: lbConfigFor(t, "", nil),
366 },
367 false,
368 },
369 {
370 `{
371 "methodConfig": [
372 {
373 "name": [
374 {
375 "service": "foo",
376 "method": "Bar"
377 }
378 ],
379 "maxRequestMessageBytes": "1024",
380 "maxResponseMessageBytes": "2048"
381 },
382 {
383 "name": [
384 {
385 "service": "foo",
386 "method": "Bar"
387 }
388 ],
389 "maxRequestMessageBytes": 1024,
390 "maxResponseMessageBytes": 2048
391 }
392 ]
393 }`,
394 nil,
395 true,
396 },
397 }
398
399 runParseTests(t, testcases)
400 }
401 func (s) TestParseDefaultMethodConfig(t *testing.T) {
402 dc := &ServiceConfig{
403 Methods: map[string]MethodConfig{
404 "": {WaitForReady: newBool(true)},
405 },
406 lbConfig: lbConfigFor(t, "", nil),
407 }
408
409 runParseTests(t, []parseTestCase{
410 {
411 `{
412 "methodConfig": [{
413 "name": [{}],
414 "waitForReady": true
415 }]
416 }`,
417 dc,
418 false,
419 },
420 {
421 `{
422 "methodConfig": [{
423 "name": [{"service": null}],
424 "waitForReady": true
425 }]
426 }`,
427 dc,
428 false,
429 },
430 {
431 `{
432 "methodConfig": [{
433 "name": [{"service": ""}],
434 "waitForReady": true
435 }]
436 }`,
437 dc,
438 false,
439 },
440 {
441 `{
442 "methodConfig": [{
443 "name": [{"method": "Bar"}],
444 "waitForReady": true
445 }]
446 }`,
447 nil,
448 true,
449 },
450 {
451 `{
452 "methodConfig": [{
453 "name": [{"service": "", "method": "Bar"}],
454 "waitForReady": true
455 }]
456 }`,
457 nil,
458 true,
459 },
460 })
461 }
462
463 func (s) TestParseMethodConfigDuplicatedName(t *testing.T) {
464 runParseTests(t, []parseTestCase{
465 {
466 `{
467 "methodConfig": [{
468 "name": [
469 {"service": "foo"},
470 {"service": "foo"}
471 ],
472 "waitForReady": true
473 }]
474 }`, nil, true,
475 },
476 })
477 }
478
479 func newBool(b bool) *bool {
480 return &b
481 }
482
483 func newDuration(b time.Duration) *time.Duration {
484 return &b
485 }
486
View as plain text