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  }