github.com/cloudwego/kitex@v0.9.0/client/option_test.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package client
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/golang/mock/gomock"
    28  
    29  	"github.com/cloudwego/kitex/internal/client"
    30  	"github.com/cloudwego/kitex/internal/mocks"
    31  	mocksloadbalance "github.com/cloudwego/kitex/internal/mocks/loadbalance"
    32  	mocksnetpoll "github.com/cloudwego/kitex/internal/mocks/netpoll"
    33  	mocksproxy "github.com/cloudwego/kitex/internal/mocks/proxy"
    34  	mock_remote "github.com/cloudwego/kitex/internal/mocks/remote"
    35  	"github.com/cloudwego/kitex/internal/mocks/rpc_info"
    36  	"github.com/cloudwego/kitex/internal/test"
    37  	"github.com/cloudwego/kitex/pkg/circuitbreak"
    38  	"github.com/cloudwego/kitex/pkg/connpool"
    39  	"github.com/cloudwego/kitex/pkg/diagnosis"
    40  	"github.com/cloudwego/kitex/pkg/discovery"
    41  	"github.com/cloudwego/kitex/pkg/endpoint"
    42  	"github.com/cloudwego/kitex/pkg/generic"
    43  	"github.com/cloudwego/kitex/pkg/http"
    44  	"github.com/cloudwego/kitex/pkg/loadbalance"
    45  	"github.com/cloudwego/kitex/pkg/proxy"
    46  	"github.com/cloudwego/kitex/pkg/remote/trans/nphttp2/grpc"
    47  	"github.com/cloudwego/kitex/pkg/retry"
    48  	"github.com/cloudwego/kitex/pkg/rpcinfo"
    49  	"github.com/cloudwego/kitex/pkg/rpcinfo/remoteinfo"
    50  	"github.com/cloudwego/kitex/pkg/stats"
    51  	"github.com/cloudwego/kitex/pkg/xds"
    52  	"github.com/cloudwego/kitex/transport"
    53  )
    54  
    55  func TestRetryOptionDebugInfo(t *testing.T) {
    56  	fp := retry.NewFailurePolicy()
    57  	fp.WithDDLStop()
    58  	expectPolicyStr := "WithFailureRetry({StopPolicy:{MaxRetryTimes:2 MaxDurationMS:0 DisableChainStop:false DDLStop:true " +
    59  		"CBPolicy:{ErrorRate:0.1}} BackOffPolicy:&{BackOffType:none CfgItems:map[]} RetrySameNode:false ShouldResultRetry:{ErrorRetry:false, RespRetry:false}})"
    60  	policyStr := fmt.Sprintf("WithFailureRetry(%+v)", *fp)
    61  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    62  
    63  	fp.WithFixedBackOff(10)
    64  	expectPolicyStr = "WithFailureRetry({StopPolicy:{MaxRetryTimes:2 MaxDurationMS:0 DisableChainStop:false DDLStop:true " +
    65  		"CBPolicy:{ErrorRate:0.1}} BackOffPolicy:&{BackOffType:fixed CfgItems:map[fix_ms:10]} RetrySameNode:false ShouldResultRetry:{ErrorRetry:false, RespRetry:false}})"
    66  	policyStr = fmt.Sprintf("WithFailureRetry(%+v)", *fp)
    67  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    68  
    69  	fp.WithRandomBackOff(10, 20)
    70  	fp.DisableChainRetryStop()
    71  	expectPolicyStr = "WithFailureRetry({StopPolicy:{MaxRetryTimes:2 MaxDurationMS:0 DisableChainStop:true DDLStop:true " +
    72  		"CBPolicy:{ErrorRate:0.1}} BackOffPolicy:&{BackOffType:random CfgItems:map[max_ms:20 min_ms:10]} RetrySameNode:false ShouldResultRetry:{ErrorRetry:false, RespRetry:false}})"
    73  	policyStr = fmt.Sprintf("WithFailureRetry(%+v)", *fp)
    74  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    75  
    76  	fp.WithRetrySameNode()
    77  	expectPolicyStr = "WithFailureRetry({StopPolicy:{MaxRetryTimes:2 MaxDurationMS:0 DisableChainStop:true DDLStop:true " +
    78  		"CBPolicy:{ErrorRate:0.1}} BackOffPolicy:&{BackOffType:random CfgItems:map[max_ms:20 min_ms:10]} RetrySameNode:true ShouldResultRetry:{ErrorRetry:false, RespRetry:false}})"
    79  	policyStr = fmt.Sprintf("WithFailureRetry(%+v)", *fp)
    80  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    81  
    82  	fp.WithSpecifiedResultRetry(&retry.ShouldResultRetry{ErrorRetry: func(err error, ri rpcinfo.RPCInfo) bool {
    83  		return false
    84  	}})
    85  	expectPolicyStr = "WithFailureRetry({StopPolicy:{MaxRetryTimes:2 MaxDurationMS:0 DisableChainStop:true DDLStop:true " +
    86  		"CBPolicy:{ErrorRate:0.1}} BackOffPolicy:&{BackOffType:random CfgItems:map[max_ms:20 min_ms:10]} RetrySameNode:true ShouldResultRetry:{ErrorRetry:true, RespRetry:false}})"
    87  	policyStr = fmt.Sprintf("WithFailureRetry(%+v)", *fp)
    88  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    89  
    90  	bp := retry.NewBackupPolicy(20)
    91  	expectPolicyStr = "WithBackupRequest({RetryDelayMS:20 StopPolicy:{MaxRetryTimes:1 MaxDurationMS:0 DisableChainStop:false " +
    92  		"DDLStop:false CBPolicy:{ErrorRate:0.1}} RetrySameNode:false})"
    93  	policyStr = fmt.Sprintf("WithBackupRequest(%+v)", *bp)
    94  	test.Assert(t, policyStr == expectPolicyStr, policyStr)
    95  	WithBackupRequest(bp)
    96  }
    97  
    98  func TestRetryOption(t *testing.T) {
    99  	defer func() {
   100  		err := recover()
   101  		test.Assert(t, err != nil)
   102  	}()
   103  	fp := retry.NewFailurePolicy()
   104  	var options []client.Option
   105  	options = append(options, WithFailureRetry(fp))
   106  	bp := retry.NewBackupPolicy(20)
   107  	options = append(options, WithBackupRequest(bp))
   108  
   109  	client.NewOptions(options)
   110  	// both setup failure and backup retry, panic should happen
   111  	test.Assert(t, false)
   112  }
   113  
   114  func TestTransportProtocolOption(t *testing.T) {
   115  	var options []client.Option
   116  	options = append(options, WithTransportProtocol(transport.GRPC))
   117  	client.NewOptions(options)
   118  }
   119  
   120  func TestWithHostPorts(t *testing.T) {
   121  	var options []client.Option
   122  	options = append(options, WithHostPorts("127.0.0.1:8080"))
   123  	opts := client.NewOptions(options)
   124  	test.Assert(t, opts.Resolver.Name() == "127.0.0.1:8080")
   125  	res, err := opts.Resolver.Resolve(context.Background(), "")
   126  	test.Assert(t, err == nil)
   127  	test.Assert(t, res.Instances[0].Address().String() == "127.0.0.1:8080")
   128  }
   129  
   130  func TestForwardProxy(t *testing.T) {
   131  	ctrl := gomock.NewController(t)
   132  	defer ctrl.Finish()
   133  
   134  	fp := mocksproxy.NewMockForwardProxy(ctrl)
   135  	fp.EXPECT().Configure(gomock.Any()).DoAndReturn(func(cfg *proxy.Config) error {
   136  		test.Assert(t, cfg.Resolver == nil, cfg.Resolver)
   137  		test.Assert(t, cfg.Balancer == nil, cfg.Balancer)
   138  		return nil
   139  	}).AnyTimes()
   140  	fp.EXPECT().ResolveProxyInstance(gomock.Any()).DoAndReturn(func(ctx context.Context) error {
   141  		ri := rpcinfo.GetRPCInfo(ctx)
   142  		re := remoteinfo.AsRemoteInfo(ri.To())
   143  		in := re.GetInstance()
   144  		test.Assert(t, in == nil, in)
   145  		re.SetInstance(instance505)
   146  		return nil
   147  	}).AnyTimes()
   148  
   149  	var opts []Option
   150  	opts = append(opts, WithTransHandlerFactory(newMockCliTransHandlerFactory(ctrl)))
   151  	opts = append(opts, WithDialer(newDialer(ctrl)))
   152  	opts = append(opts, WithDestService("destService"))
   153  	opts = append(opts, WithProxy(fp))
   154  
   155  	svcInfo := mocks.ServiceInfo()
   156  	// Configure long pool and check if the pool is closed when initProxy
   157  	// If the proxy does not configure pool, use the predefined pool
   158  	mockLongPool := mock_remote.NewMockLongConnPool(ctrl)
   159  	var closed bool
   160  	mockLongPool.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mocksnetpoll.NewMockConnection(ctrl), nil).Times(1)
   161  	mockLongPool.EXPECT().Close().Do(func() {
   162  		closed = true
   163  	}).AnyTimes()
   164  	mockLongPool.EXPECT().Put(gomock.Any()).Return(nil).AnyTimes()
   165  	mockLongPool.EXPECT().Discard(gomock.Any()).Return(nil).AnyTimes()
   166  	opts = append(opts, WithConnPool(mockLongPool))
   167  	cli, err := NewClient(svcInfo, opts...)
   168  	test.Assert(t, err == nil)
   169  	test.Assert(t, !closed)
   170  	mtd := mocks.MockMethod
   171  	ctx := context.Background()
   172  	req := new(MockTStruct)
   173  	res := new(MockTStruct)
   174  
   175  	err = cli.Call(ctx, mtd, req, res)
   176  	test.Assert(t, err == nil, err)
   177  }
   178  
   179  func TestProxyWithResolver(t *testing.T) {
   180  	ctrl := gomock.NewController(t)
   181  	defer ctrl.Finish()
   182  
   183  	resolver := resolver404(ctrl)
   184  	fp := mocksproxy.NewMockForwardProxy(ctrl)
   185  	fp.EXPECT().Configure(gomock.Any()).DoAndReturn(func(cfg *proxy.Config) error {
   186  		test.Assert(t, cfg.Resolver == resolver)
   187  		test.Assert(t, cfg.Balancer == nil, cfg.Balancer)
   188  		return nil
   189  	}).AnyTimes()
   190  	fp.EXPECT().ResolveProxyInstance(gomock.Any()).DoAndReturn(func(ctx context.Context) error {
   191  		ri := rpcinfo.GetRPCInfo(ctx)
   192  		re := remoteinfo.AsRemoteInfo(ri.To())
   193  		in := re.GetInstance()
   194  		test.Assert(t, in == instance404[0])
   195  		re.SetInstance(instance505)
   196  		return nil
   197  	}).AnyTimes()
   198  
   199  	var opts []Option
   200  	opts = append(opts, WithTransHandlerFactory(newMockCliTransHandlerFactory(ctrl)))
   201  	opts = append(opts, WithDialer(newDialer(ctrl)))
   202  	opts = append(opts, WithDestService("destService"))
   203  	opts = append(opts, WithProxy(fp))
   204  	opts = append(opts, WithResolver(resolver))
   205  
   206  	svcInfo := mocks.ServiceInfo()
   207  	cli, err := NewClient(svcInfo, opts...)
   208  	test.Assert(t, err == nil)
   209  
   210  	mtd := mocks.MockMethod
   211  	ctx := context.Background()
   212  	req := new(MockTStruct)
   213  	res := new(MockTStruct)
   214  
   215  	err = cli.Call(ctx, mtd, req, res)
   216  	test.Assert(t, err == nil, err)
   217  }
   218  
   219  func TestProxyWithBalancer(t *testing.T) {
   220  	ctrl := gomock.NewController(t)
   221  	defer ctrl.Finish()
   222  
   223  	lb := mocksloadbalance.NewMockLoadbalancer(ctrl)
   224  	lb.EXPECT().GetPicker(gomock.Any()).DoAndReturn(func(entry discovery.Result) loadbalance.Picker {
   225  		picker := mocksloadbalance.NewMockPicker(ctrl)
   226  		picker.EXPECT().Next(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, request interface{}) discovery.Instance {
   227  			if len(entry.Instances) > 0 {
   228  				return entry.Instances[0]
   229  			}
   230  			return nil
   231  		}).AnyTimes()
   232  		return picker
   233  	}).AnyTimes()
   234  	fp := mocksproxy.NewMockForwardProxy(ctrl)
   235  	fp.EXPECT().Configure(gomock.Any()).DoAndReturn(func(cfg *proxy.Config) error {
   236  		test.Assert(t, cfg.Resolver == nil, cfg.Resolver)
   237  		test.Assert(t, cfg.Balancer == lb)
   238  		return nil
   239  	}).AnyTimes()
   240  	fp.EXPECT().ResolveProxyInstance(gomock.Any()).DoAndReturn(func(ctx context.Context) error {
   241  		ri := rpcinfo.GetRPCInfo(ctx)
   242  		re := remoteinfo.AsRemoteInfo(ri.To())
   243  		in := re.GetInstance()
   244  		test.Assert(t, in == nil)
   245  		re.SetInstance(instance505)
   246  		return nil
   247  	}).AnyTimes()
   248  
   249  	var opts []Option
   250  	opts = append(opts, WithTransHandlerFactory(newMockCliTransHandlerFactory(ctrl)))
   251  	opts = append(opts, WithDialer(newDialer(ctrl)))
   252  	opts = append(opts, WithDestService("destService"))
   253  	opts = append(opts, WithProxy(fp))
   254  	opts = append(opts, WithLoadBalancer(lb))
   255  
   256  	svcInfo := mocks.ServiceInfo()
   257  	cli, err := NewClient(svcInfo, opts...)
   258  	test.Assert(t, err == nil)
   259  
   260  	mtd := mocks.MockMethod
   261  	ctx := context.Background()
   262  	req := new(MockTStruct)
   263  	res := new(MockTStruct)
   264  
   265  	err = cli.Call(ctx, mtd, req, res)
   266  	test.Assert(t, err == nil)
   267  }
   268  
   269  func TestProxyWithResolverAndBalancer(t *testing.T) {
   270  	ctrl := gomock.NewController(t)
   271  	defer ctrl.Finish()
   272  
   273  	lb := mocksloadbalance.NewMockLoadbalancer(ctrl)
   274  	lb.EXPECT().GetPicker(gomock.Any()).DoAndReturn(func(entry discovery.Result) loadbalance.Picker {
   275  		picker := mocksloadbalance.NewMockPicker(ctrl)
   276  		picker.EXPECT().Next(gomock.Any(), gomock.Any()).Return(instance505).AnyTimes()
   277  		return picker
   278  	}).AnyTimes()
   279  	lb.EXPECT().Name().Return("mock_load_balancer").AnyTimes()
   280  	resolver := resolver404(ctrl)
   281  	fp := mocksproxy.NewMockForwardProxy(ctrl)
   282  	fp.EXPECT().Configure(gomock.Any()).DoAndReturn(func(cfg *proxy.Config) error {
   283  		test.Assert(t, cfg.Resolver == resolver)
   284  		test.Assert(t, cfg.Balancer == lb)
   285  		return nil
   286  	}).AnyTimes()
   287  	fp.EXPECT().ResolveProxyInstance(gomock.Any()).DoAndReturn(func(ctx context.Context) error {
   288  		ri := rpcinfo.GetRPCInfo(ctx)
   289  		re := remoteinfo.AsRemoteInfo(ri.To())
   290  		in := re.GetInstance()
   291  		test.Assert(t, in == instance505)
   292  		return nil
   293  	}).AnyTimes()
   294  
   295  	var opts []Option
   296  	opts = append(opts, WithTransHandlerFactory(newMockCliTransHandlerFactory(ctrl)))
   297  	opts = append(opts, WithDialer(newDialer(ctrl)))
   298  	opts = append(opts, WithDestService("destService"))
   299  	opts = append(opts, WithProxy(fp))
   300  	opts = append(opts, WithResolver(resolver))
   301  	opts = append(opts, WithLoadBalancer(lb))
   302  
   303  	svcInfo := mocks.ServiceInfo()
   304  	cli, err := NewClient(svcInfo, opts...)
   305  	test.Assert(t, err == nil)
   306  
   307  	mtd := mocks.MockMethod
   308  	ctx := context.Background()
   309  	req := new(MockTStruct)
   310  	res := new(MockTStruct)
   311  
   312  	err = cli.Call(ctx, mtd, req, res)
   313  	test.Assert(t, err == nil)
   314  }
   315  
   316  func TestProxyWithConnPool(t *testing.T) {
   317  	ctrl := gomock.NewController(t)
   318  	defer ctrl.Finish()
   319  
   320  	type mockLongConnPool struct {
   321  		*mock_remote.MockLongConnPool
   322  		*mock_remote.MockConnPoolReporter
   323  	}
   324  
   325  	fp := mocksproxy.NewMockForwardProxy(ctrl)
   326  	fp.EXPECT().Configure(gomock.Any()).DoAndReturn(func(cfg *proxy.Config) error {
   327  		cp := mock_remote.NewMockLongConnPool(ctrl)
   328  		cp.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mocksnetpoll.NewMockConnection(ctrl), nil).Times(1)
   329  		cp.EXPECT().Put(gomock.Any()).Return(nil).AnyTimes()
   330  		cp.EXPECT().Discard(gomock.Any()).Return(nil).AnyTimes()
   331  		cp.EXPECT().Close().Return(nil).Times(1)
   332  		rp := mock_remote.NewMockConnPoolReporter(ctrl)
   333  		rp.EXPECT().EnableReporter().Times(1)
   334  		cfg.Pool = &mockLongConnPool{cp, rp}
   335  		return nil
   336  	}).AnyTimes()
   337  	fp.EXPECT().ResolveProxyInstance(gomock.Any()).Return(nil).AnyTimes()
   338  
   339  	var opts []Option
   340  	opts = append(opts, WithTransHandlerFactory(newMockCliTransHandlerFactory(ctrl)))
   341  	opts = append(opts, WithDestService("destService"))
   342  	opts = append(opts, WithProxy(fp))
   343  	opts = append(opts, WithConnReporterEnabled())
   344  	opts = append(opts, WithResolver(resolver404(ctrl)))
   345  	// Preconfigured longConnPool should be closed because the proxy configured the pool
   346  	mockLongPool := mock_remote.NewMockLongConnPool(ctrl)
   347  	var closed bool
   348  	mockLongPool.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mocksnetpoll.NewMockConnection(ctrl), nil).Times(0)
   349  	mockLongPool.EXPECT().Close().Do(func() {
   350  		closed = true
   351  	}).AnyTimes()
   352  	mockLongPool.EXPECT().Put(gomock.Any()).Return(nil).AnyTimes()
   353  	mockLongPool.EXPECT().Discard(gomock.Any()).Return(nil).AnyTimes()
   354  	opts = append(opts, WithConnPool(mockLongPool))
   355  
   356  	svcInfo := mocks.ServiceInfo()
   357  	cli, err := NewClient(svcInfo, opts...)
   358  	test.Assert(t, err == nil)
   359  	test.Assert(t, closed) // should be true
   360  
   361  	mtd := mocks.MockMethod
   362  	ctx := context.Background()
   363  	req := new(MockTStruct)
   364  	res := new(MockTStruct)
   365  
   366  	err = cli.Call(ctx, mtd, req, res)
   367  	test.Assert(t, err == nil)
   368  
   369  	err = cli.(interface{ Close() error }).Close()
   370  	test.Assert(t, err == nil)
   371  }
   372  
   373  var (
   374  	// mock middleware, do nothing
   375  	md = func(next endpoint.Endpoint) endpoint.Endpoint {
   376  		return func(ctx context.Context, req, resp interface{}) (err error) {
   377  			return nil
   378  		}
   379  	}
   380  	httpResolver = http.NewDefaultResolver()
   381  )
   382  
   383  type mockDiagnosis struct {
   384  	probes map[diagnosis.ProbeName]diagnosis.ProbeFunc
   385  }
   386  
   387  func (m *mockDiagnosis) RegisterProbeFunc(name diagnosis.ProbeName, probeFunc diagnosis.ProbeFunc) {
   388  	m.probes[name] = probeFunc
   389  }
   390  
   391  func (m *mockDiagnosis) ProbePairs() map[diagnosis.ProbeName]diagnosis.ProbeFunc {
   392  	return m.probes
   393  }
   394  
   395  func TestWithInstanceMW(t *testing.T) {
   396  	opts := client.NewOptions([]client.Option{WithInstanceMW(md)})
   397  	test.Assert(t, len(opts.IMWBs) == 1, len(opts.IMWBs))
   398  }
   399  
   400  func TestWithHTTPResolver(t *testing.T) {
   401  	opts := client.NewOptions([]client.Option{WithHTTPResolver(httpResolver)})
   402  	test.DeepEqual(t, opts.HTTPResolver, httpResolver)
   403  }
   404  
   405  func TestShortConnection(t *testing.T) {
   406  	opts := client.NewOptions([]client.Option{WithShortConnection()})
   407  	test.Assert(t, opts.PoolCfg != nil, opts.PoolCfg)
   408  }
   409  
   410  func TestWithMuxConnection(t *testing.T) {
   411  	connNum := 100
   412  	opts := client.NewOptions([]client.Option{WithMuxConnection(connNum)})
   413  	test.Assert(t, opts.RemoteOpt.ConnPool != nil)
   414  	test.Assert(t, opts.RemoteOpt.CliHandlerFactory != nil)
   415  	test.Assert(t, opts.Configs.TransportProtocol() == transport.TTHeader, opts.Configs.TransportProtocol())
   416  }
   417  
   418  func TestWithTimeoutProvider(t *testing.T) {
   419  	ctrl := gomock.NewController(t)
   420  	defer ctrl.Finish()
   421  
   422  	mockTimeoutProvider := rpc_info.NewMockTimeoutProvider(ctrl)
   423  	opts := client.NewOptions([]client.Option{WithTimeoutProvider(mockTimeoutProvider)})
   424  	test.DeepEqual(t, opts.Timeouts, mockTimeoutProvider)
   425  }
   426  
   427  func TestWithStatsLevel(t *testing.T) {
   428  	opts := client.NewOptions([]client.Option{WithStatsLevel(stats.LevelDisabled)})
   429  	test.Assert(t, opts.StatsLevel != nil && *opts.StatsLevel == stats.LevelDisabled, opts.StatsLevel)
   430  }
   431  
   432  func TestWithCodec(t *testing.T) {
   433  	ctrl := gomock.NewController(t)
   434  	defer ctrl.Finish()
   435  
   436  	mockCodec := mock_remote.NewMockCodec(ctrl)
   437  	opts := client.NewOptions([]client.Option{WithCodec(mockCodec)})
   438  	test.DeepEqual(t, opts.RemoteOpt.Codec, mockCodec)
   439  }
   440  
   441  func TestWithPayloadCodec(t *testing.T) {
   442  	ctrl := gomock.NewController(t)
   443  	defer ctrl.Finish()
   444  
   445  	mockPayloadCodec := mock_remote.NewMockPayloadCodec(ctrl)
   446  	opts := client.NewOptions([]client.Option{WithPayloadCodec(mockPayloadCodec)})
   447  	test.DeepEqual(t, opts.RemoteOpt.PayloadCodec, mockPayloadCodec)
   448  }
   449  
   450  func TestWithConnReporterEnabled(t *testing.T) {
   451  	opts := client.NewOptions([]client.Option{WithConnReporterEnabled()})
   452  	test.Assert(t, opts.RemoteOpt.EnableConnPoolReporter)
   453  }
   454  
   455  func TestWithCircuitBreaker(t *testing.T) {
   456  	opts := client.NewOptions([]client.Option{
   457  		WithCircuitBreaker(circuitbreak.NewCBSuite(func(ri rpcinfo.RPCInfo) string { return "" })),
   458  	})
   459  	test.Assert(t, opts.CBSuite != nil)
   460  }
   461  
   462  const mockUint32Size uint32 = 0
   463  
   464  func TestWithGRPCConnPoolSize(t *testing.T) {
   465  	opts := client.NewOptions([]client.Option{WithGRPCConnPoolSize(mockUint32Size)})
   466  	test.Assert(t, opts.GRPCConnPoolSize == mockUint32Size, opts.GRPCConnPoolSize)
   467  }
   468  
   469  func TestWithGRPCInitialWindowSize(t *testing.T) {
   470  	opts := client.NewOptions([]client.Option{WithGRPCInitialWindowSize(mockUint32Size)})
   471  	test.Assert(t, opts.GRPCConnectOpts.InitialWindowSize == mockUint32Size, opts.GRPCConnectOpts.InitialWindowSize)
   472  }
   473  
   474  func TestWithGRPCInitialConnWindowSize(t *testing.T) {
   475  	opts := client.NewOptions([]client.Option{WithGRPCInitialConnWindowSize(mockUint32Size)})
   476  	test.Assert(t, opts.GRPCConnectOpts.InitialConnWindowSize == mockUint32Size,
   477  		opts.GRPCConnectOpts.InitialConnWindowSize)
   478  }
   479  
   480  func TestWithGRPCMaxHeaderListSize(t *testing.T) {
   481  	opts := client.NewOptions([]client.Option{WithGRPCMaxHeaderListSize(mockUint32Size)})
   482  	test.Assert(t,
   483  		opts.GRPCConnectOpts.MaxHeaderListSize != nil &&
   484  			*opts.GRPCConnectOpts.MaxHeaderListSize == mockUint32Size,
   485  		opts.GRPCConnectOpts.MaxHeaderListSize)
   486  }
   487  
   488  func TestWithGRPCKeepaliveParams(t *testing.T) {
   489  	opts := client.NewOptions(
   490  		[]client.Option{
   491  			WithGRPCKeepaliveParams(grpc.ClientKeepalive{
   492  				Time:                5 * time.Second, // less than  grpc.KeepaliveMinPingTime(5s)
   493  				Timeout:             20 * time.Second,
   494  				PermitWithoutStream: true,
   495  			}),
   496  		})
   497  	test.Assert(t, opts.GRPCConnectOpts.KeepaliveParams.Time == grpc.KeepaliveMinPingTime,
   498  		opts.GRPCConnectOpts.KeepaliveParams.Time)
   499  	test.Assert(t, opts.GRPCConnectOpts.KeepaliveParams.Timeout == 20*time.Second,
   500  		opts.GRPCConnectOpts.KeepaliveParams.Timeout)
   501  	test.Assert(t, opts.GRPCConnectOpts.KeepaliveParams.PermitWithoutStream)
   502  }
   503  
   504  func TestWithHTTPConnection(t *testing.T) {
   505  	opts := client.NewOptions([]client.Option{WithHTTPConnection()})
   506  	test.Assert(t, opts.RemoteOpt.CliHandlerFactory != nil)
   507  }
   508  
   509  func TestWithClientBasicInfo(t *testing.T) {
   510  	mockEBI := &rpcinfo.EndpointBasicInfo{}
   511  	opts := client.NewOptions([]client.Option{WithClientBasicInfo(mockEBI)})
   512  	test.Assert(t, opts.Cli == mockEBI)
   513  }
   514  
   515  func TestWithDiagnosisService(t *testing.T) {
   516  	mockDS := &mockDiagnosis{
   517  		make(map[diagnosis.ProbeName]diagnosis.ProbeFunc),
   518  	}
   519  	opts := client.NewOptions([]client.Option{
   520  		WithDiagnosisService(mockDS),
   521  	})
   522  	test.Assert(t, opts.DebugService == mockDS, opts.DebugService)
   523  }
   524  
   525  func mockACLRule(ctx context.Context, request interface{}) (reason error) {
   526  	return nil
   527  }
   528  
   529  func TestWithACLRules(t *testing.T) {
   530  	opts := client.NewOptions([]client.Option{WithACLRules(mockACLRule)})
   531  	test.Assert(t, reflect.ValueOf(mockACLRule).Pointer() == reflect.ValueOf(opts.ACLRules[0]).Pointer())
   532  }
   533  
   534  func TestWithFirstMetaHandler(t *testing.T) {
   535  	mockMetaHandler := &mock_remote.MockMetaHandler{}
   536  	opts := client.NewOptions([]client.Option{WithFirstMetaHandler(mockMetaHandler)})
   537  	test.DeepEqual(t, opts.MetaHandlers[0], mockMetaHandler)
   538  }
   539  
   540  func TestWithMetaHandler(t *testing.T) {
   541  	mockMetaHandler := &mock_remote.MockMetaHandler{}
   542  	opts := client.NewOptions([]client.Option{WithMetaHandler(mockMetaHandler)})
   543  	test.DeepEqual(t, opts.MetaHandlers[1], mockMetaHandler)
   544  }
   545  
   546  func TestWithConnPool(t *testing.T) {
   547  	ctrl := gomock.NewController(t)
   548  	defer ctrl.Finish()
   549  
   550  	mockPool := mock_remote.NewMockConnPool(ctrl)
   551  	opts := client.NewOptions([]client.Option{WithConnPool(mockPool)})
   552  	test.Assert(t, opts.RemoteOpt.ConnPool == mockPool)
   553  }
   554  
   555  func TestWithRetryContainer(t *testing.T) {
   556  	mockRetryContainer := &retry.Container{}
   557  	opts := client.NewOptions([]client.Option{WithRetryContainer(mockRetryContainer)})
   558  	test.Assert(t, opts.RetryContainer == mockRetryContainer)
   559  }
   560  
   561  func TestWithGeneric(t *testing.T) {
   562  	opts := client.NewOptions([]client.Option{WithGeneric(generic.BinaryThriftGeneric())})
   563  	test.DeepEqual(t, opts.RemoteOpt.PayloadCodec, generic.BinaryThriftGeneric().PayloadCodec())
   564  }
   565  
   566  func TestWithCloseCallbacks(t *testing.T) {
   567  	opts := client.NewOptions([]client.Option{WithCloseCallbacks(func() error { return nil })})
   568  	test.Assert(t, len(opts.CloseCallbacks) > 0)
   569  }
   570  
   571  func TestWithErrorHandler(t *testing.T) {
   572  	errHandler := func(context.Context, error) error { return nil }
   573  	opts := client.NewOptions([]client.Option{WithErrorHandler(errHandler)})
   574  	test.Assert(t, reflect.ValueOf(opts.ErrHandle).Pointer() == reflect.ValueOf(errHandler).Pointer())
   575  }
   576  
   577  func TestWithBoundHandler(t *testing.T) {
   578  	ctrl := gomock.NewController(t)
   579  	defer ctrl.Finish()
   580  
   581  	mockInboundHandler := mock_remote.NewMockInboundHandler(ctrl)
   582  	opts := client.NewOptions([]client.Option{WithBoundHandler(mockInboundHandler)})
   583  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
   584  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)
   585  
   586  	opts = client.NewOptions([]client.Option{WithBoundHandler(mockInboundHandler), WithBoundHandler(mockInboundHandler)})
   587  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
   588  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)
   589  
   590  	mockInboundHandler2 := mock_remote.NewMockInboundHandler(ctrl)
   591  	opts = client.NewOptions([]client.Option{WithBoundHandler(mockInboundHandler), WithBoundHandler(mockInboundHandler2)})
   592  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
   593  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)
   594  
   595  	mockOutboundHandler := mock_remote.NewMockOutboundHandler(ctrl)
   596  	opts = client.NewOptions([]client.Option{WithBoundHandler(mockOutboundHandler)})
   597  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
   598  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)
   599  
   600  	opts = client.NewOptions(
   601  		[]client.Option{WithBoundHandler(mockOutboundHandler), WithBoundHandler(mockOutboundHandler)})
   602  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
   603  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)
   604  
   605  	mockOutboundHandler2 := mock_remote.NewMockOutboundHandler(ctrl)
   606  	opts = client.NewOptions([]client.Option{
   607  		WithBoundHandler(mockOutboundHandler), WithBoundHandler(mockOutboundHandler2),
   608  	})
   609  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
   610  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)
   611  
   612  	mockDuplexBoundHandler := mock_remote.NewMockDuplexBoundHandler(ctrl)
   613  	opts = client.NewOptions([]client.Option{WithBoundHandler(mockDuplexBoundHandler), WithBoundHandler(mockDuplexBoundHandler)})
   614  	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
   615  	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)
   616  }
   617  
   618  // collection of options
   619  type mockSuite struct{}
   620  
   621  var (
   622  	mockEndpointBasicInfo = &rpcinfo.EndpointBasicInfo{}
   623  	mockDiagnosisService  = &mockDiagnosis{make(map[diagnosis.ProbeName]diagnosis.ProbeFunc)}
   624  	mockRetryContainer    = &retry.Container{}
   625  )
   626  
   627  func (m *mockSuite) Options() []Option {
   628  	return []Option{
   629  		WithClientBasicInfo(mockEndpointBasicInfo),
   630  		WithDiagnosisService(mockDiagnosisService),
   631  		WithRetryContainer(mockRetryContainer),
   632  	}
   633  }
   634  
   635  func TestWithSuite(t *testing.T) {
   636  	var suite *mockSuite
   637  	options := []client.Option{
   638  		WithSuite(suite),
   639  	}
   640  	opts := client.NewOptions(options)
   641  	test.Assert(t, opts.Cli == mockEndpointBasicInfo)
   642  	test.Assert(t, reflect.DeepEqual(opts.DebugService, mockDiagnosisService))
   643  	test.Assert(t, opts.RetryContainer == mockRetryContainer)
   644  }
   645  
   646  func TestWithLongConnectionOption(t *testing.T) {
   647  	idleCfg := connpool.IdleConfig{}
   648  	options := []client.Option{
   649  		WithLongConnection(idleCfg),
   650  	}
   651  	opts := client.NewOptions(options)
   652  	test.Assert(t, opts.PoolCfg.MaxIdleTimeout == 30*time.Second) // defaultMaxIdleTimeout
   653  	test.Assert(t, opts.PoolCfg.MaxIdlePerAddress == 1)           // default
   654  	test.Assert(t, opts.PoolCfg.MaxIdleGlobal == 1<<20)           // default
   655  }
   656  
   657  func TestWithWarmingUpOption(t *testing.T) {
   658  	options := []client.Option{
   659  		WithWarmingUp(mockWarmupOption),
   660  	}
   661  	opt := client.NewOptions(options)
   662  	test.Assert(t, opt.WarmUpOption == mockWarmupOption)
   663  }
   664  
   665  func TestWithFramedTransport(t *testing.T) {
   666  	options := []client.Option{
   667  		WithFramedTransport(),
   668  	}
   669  	opt := client.NewOptions(options)
   670  	test.Assert(t, opt.Configs.TransportProtocol() == transport.Framed)
   671  }
   672  
   673  func TestWithConnMetric(t *testing.T) {
   674  	options := []client.Option{
   675  		WithConnMetric(),
   676  	}
   677  	opt := client.NewOptions(options)
   678  	test.Assert(t, opt.RemoteOpt.EnableConnPoolReporter == true)
   679  }
   680  
   681  func TestWithXDSSuite(t *testing.T) {
   682  	// failed
   683  	s := xds.ClientSuite{}
   684  	options := []client.Option{
   685  		WithXDSSuite(s),
   686  	}
   687  	opt := client.NewOptions(options)
   688  	test.Assert(t, opt.XDSEnabled == false)
   689  	test.Assert(t, opt.XDSRouterMiddleware == nil)
   690  	test.Assert(t, opt.Resolver == nil)
   691  
   692  	// succeed
   693  	s = xds.ClientSuite{
   694  		RouterMiddleware: endpoint.DummyMiddleware,
   695  		Resolver:         discovery.SynthesizedResolver{},
   696  	}
   697  	options = []client.Option{
   698  		WithXDSSuite(s),
   699  	}
   700  	opt = client.NewOptions(options)
   701  	test.Assert(t, opt.XDSEnabled == true)
   702  	test.Assert(t, opt.XDSRouterMiddleware != nil)
   703  	test.Assert(t, opt.Resolver != nil)
   704  }
   705  
   706  func TestWithGRPCTLSConfig(t *testing.T) {
   707  	cfg := &tls.Config{}
   708  	opts := client.NewOptions([]client.Option{WithGRPCTLSConfig(cfg)})
   709  	test.Assert(t, opts.GRPCConnectOpts != nil)
   710  }