google.golang.org/grpc@v1.62.1/service_config_test.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package grpc 20 21 import ( 22 "encoding/json" 23 "reflect" 24 "testing" 25 "time" 26 27 "google.golang.org/grpc/balancer" 28 "google.golang.org/grpc/serviceconfig" 29 ) 30 31 type parseTestCase struct { 32 scjs string 33 wantSC *ServiceConfig 34 wantErr bool 35 } 36 37 func runParseTests(t *testing.T, testCases []parseTestCase) { 38 t.Helper() 39 for _, c := range testCases { 40 scpr := parseServiceConfig(c.scjs) 41 var sc *ServiceConfig 42 sc, _ = scpr.Config.(*ServiceConfig) 43 if !c.wantErr { 44 c.wantSC.rawJSONString = c.scjs 45 } 46 if c.wantErr != (scpr.Err != nil) || !reflect.DeepEqual(sc, c.wantSC) { 47 t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, scpr.Err, c.wantSC, c.wantErr) 48 } 49 } 50 } 51 52 type pbbData struct { 53 serviceconfig.LoadBalancingConfig 54 Foo string 55 Bar int 56 } 57 58 type parseBalancerBuilder struct{} 59 60 func (parseBalancerBuilder) Name() string { 61 return "pbb" 62 } 63 64 func (parseBalancerBuilder) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 65 d := pbbData{} 66 if err := json.Unmarshal(c, &d); err != nil { 67 return nil, err 68 } 69 return d, nil 70 } 71 72 func (parseBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 73 panic("unimplemented") 74 } 75 76 func init() { 77 balancer.Register(parseBalancerBuilder{}) 78 } 79 80 func (s) TestParseLBConfig(t *testing.T) { 81 testcases := []parseTestCase{ 82 { 83 `{ 84 "loadBalancingConfig": [{"pbb": { "foo": "hi" } }] 85 }`, 86 &ServiceConfig{ 87 Methods: make(map[string]MethodConfig), 88 lbConfig: &lbConfig{name: "pbb", cfg: pbbData{Foo: "hi"}}, 89 }, 90 false, 91 }, 92 } 93 runParseTests(t, testcases) 94 } 95 96 func (s) TestParseNoLBConfigSupported(t *testing.T) { 97 // We have a loadBalancingConfig field but will not encounter a supported 98 // policy. The config will be considered invalid in this case. 99 testcases := []parseTestCase{ 100 { 101 scjs: `{ 102 "loadBalancingConfig": [{"not_a_balancer1": {} }, {"not_a_balancer2": {}}] 103 }`, 104 wantErr: true, 105 }, { 106 scjs: `{"loadBalancingConfig": []}`, 107 wantErr: true, 108 }, 109 } 110 runParseTests(t, testcases) 111 } 112 113 func (s) TestParseLoadBalancer(t *testing.T) { 114 testcases := []parseTestCase{ 115 { 116 `{ 117 "loadBalancingPolicy": "round_robin", 118 "methodConfig": [ 119 { 120 "name": [ 121 { 122 "service": "foo", 123 "method": "Bar" 124 } 125 ], 126 "waitForReady": true 127 } 128 ] 129 }`, 130 &ServiceConfig{ 131 LB: newString("round_robin"), 132 Methods: map[string]MethodConfig{ 133 "/foo/Bar": { 134 WaitForReady: newBool(true), 135 }, 136 }, 137 }, 138 false, 139 }, 140 { 141 `{ 142 "loadBalancingPolicy": 1, 143 "methodConfig": [ 144 { 145 "name": [ 146 { 147 "service": "foo", 148 "method": "Bar" 149 } 150 ], 151 "waitForReady": false 152 } 153 ] 154 }`, 155 nil, 156 true, 157 }, 158 } 159 runParseTests(t, testcases) 160 } 161 162 func (s) TestParseWaitForReady(t *testing.T) { 163 testcases := []parseTestCase{ 164 { 165 `{ 166 "methodConfig": [ 167 { 168 "name": [ 169 { 170 "service": "foo", 171 "method": "Bar" 172 } 173 ], 174 "waitForReady": true 175 } 176 ] 177 }`, 178 &ServiceConfig{ 179 Methods: map[string]MethodConfig{ 180 "/foo/Bar": { 181 WaitForReady: newBool(true), 182 }, 183 }, 184 }, 185 false, 186 }, 187 { 188 `{ 189 "methodConfig": [ 190 { 191 "name": [ 192 { 193 "service": "foo", 194 "method": "Bar" 195 } 196 ], 197 "waitForReady": false 198 } 199 ] 200 }`, 201 &ServiceConfig{ 202 Methods: map[string]MethodConfig{ 203 "/foo/Bar": { 204 WaitForReady: newBool(false), 205 }, 206 }, 207 }, 208 false, 209 }, 210 { 211 `{ 212 "methodConfig": [ 213 { 214 "name": [ 215 { 216 "service": "foo", 217 "method": "Bar" 218 } 219 ], 220 "waitForReady": fall 221 }, 222 { 223 "name": [ 224 { 225 "service": "foo", 226 "method": "Bar" 227 } 228 ], 229 "waitForReady": true 230 } 231 ] 232 }`, 233 nil, 234 true, 235 }, 236 } 237 238 runParseTests(t, testcases) 239 } 240 241 func (s) TestParseTimeOut(t *testing.T) { 242 testcases := []parseTestCase{ 243 { 244 `{ 245 "methodConfig": [ 246 { 247 "name": [ 248 { 249 "service": "foo", 250 "method": "Bar" 251 } 252 ], 253 "timeout": "1s" 254 } 255 ] 256 }`, 257 &ServiceConfig{ 258 Methods: map[string]MethodConfig{ 259 "/foo/Bar": { 260 Timeout: newDuration(time.Second), 261 }, 262 }, 263 }, 264 false, 265 }, 266 { 267 `{ 268 "methodConfig": [ 269 { 270 "name": [ 271 { 272 "service": "foo", 273 "method": "Bar" 274 } 275 ], 276 "timeout": "3c" 277 } 278 ] 279 }`, 280 nil, 281 true, 282 }, 283 { 284 `{ 285 "methodConfig": [ 286 { 287 "name": [ 288 { 289 "service": "foo", 290 "method": "Bar" 291 } 292 ], 293 "timeout": "3c" 294 }, 295 { 296 "name": [ 297 { 298 "service": "foo", 299 "method": "Bar" 300 } 301 ], 302 "timeout": "1s" 303 } 304 ] 305 }`, 306 nil, 307 true, 308 }, 309 } 310 311 runParseTests(t, testcases) 312 } 313 314 func (s) TestParseMsgSize(t *testing.T) { 315 testcases := []parseTestCase{ 316 { 317 `{ 318 "methodConfig": [ 319 { 320 "name": [ 321 { 322 "service": "foo", 323 "method": "Bar" 324 } 325 ], 326 "maxRequestMessageBytes": 1024, 327 "maxResponseMessageBytes": 2048 328 } 329 ] 330 }`, 331 &ServiceConfig{ 332 Methods: map[string]MethodConfig{ 333 "/foo/Bar": { 334 MaxReqSize: newInt(1024), 335 MaxRespSize: newInt(2048), 336 }, 337 }, 338 }, 339 false, 340 }, 341 { 342 `{ 343 "methodConfig": [ 344 { 345 "name": [ 346 { 347 "service": "foo", 348 "method": "Bar" 349 } 350 ], 351 "maxRequestMessageBytes": "1024", 352 "maxResponseMessageBytes": "2048" 353 }, 354 { 355 "name": [ 356 { 357 "service": "foo", 358 "method": "Bar" 359 } 360 ], 361 "maxRequestMessageBytes": 1024, 362 "maxResponseMessageBytes": 2048 363 } 364 ] 365 }`, 366 nil, 367 true, 368 }, 369 } 370 371 runParseTests(t, testcases) 372 } 373 func (s) TestParseDefaultMethodConfig(t *testing.T) { 374 dc := &ServiceConfig{ 375 Methods: map[string]MethodConfig{ 376 "": {WaitForReady: newBool(true)}, 377 }, 378 } 379 380 runParseTests(t, []parseTestCase{ 381 { 382 `{ 383 "methodConfig": [{ 384 "name": [{}], 385 "waitForReady": true 386 }] 387 }`, 388 dc, 389 false, 390 }, 391 { 392 `{ 393 "methodConfig": [{ 394 "name": [{"service": null}], 395 "waitForReady": true 396 }] 397 }`, 398 dc, 399 false, 400 }, 401 { 402 `{ 403 "methodConfig": [{ 404 "name": [{"service": ""}], 405 "waitForReady": true 406 }] 407 }`, 408 dc, 409 false, 410 }, 411 { 412 `{ 413 "methodConfig": [{ 414 "name": [{"method": "Bar"}], 415 "waitForReady": true 416 }] 417 }`, 418 nil, 419 true, 420 }, 421 { 422 `{ 423 "methodConfig": [{ 424 "name": [{"service": "", "method": "Bar"}], 425 "waitForReady": true 426 }] 427 }`, 428 nil, 429 true, 430 }, 431 }) 432 } 433 434 func (s) TestParseMethodConfigDuplicatedName(t *testing.T) { 435 runParseTests(t, []parseTestCase{ 436 { 437 `{ 438 "methodConfig": [{ 439 "name": [ 440 {"service": "foo"}, 441 {"service": "foo"} 442 ], 443 "waitForReady": true 444 }] 445 }`, nil, true, 446 }, 447 }) 448 } 449 450 func newBool(b bool) *bool { 451 return &b 452 } 453 454 func newDuration(b time.Duration) *time.Duration { 455 return &b 456 } 457 458 func newString(b string) *string { 459 return &b 460 }