github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/framework/http/client_test.go (about)

     1  package ddhttp_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/go-resty/resty/v2"
     7  	"github.com/golang/mock/gomock"
     8  	"github.com/hashicorp/go-msgpack/codec"
     9  	. "github.com/smartystreets/goconvey/convey"
    10  	"github.com/stretchr/testify/require"
    11  	ddhttp "github.com/unionj-cloud/go-doudou/framework/http"
    12  	"github.com/unionj-cloud/go-doudou/framework/internal/config"
    13  	"github.com/unionj-cloud/go-doudou/framework/memberlist"
    14  	"github.com/unionj-cloud/go-doudou/framework/registry"
    15  	nmock "github.com/unionj-cloud/go-doudou/framework/registry/nacos/mock"
    16  	"github.com/wubin1989/nacos-sdk-go/common/constant"
    17  	"github.com/wubin1989/nacos-sdk-go/model"
    18  	"github.com/wubin1989/nacos-sdk-go/vo"
    19  	"os"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  var clientConfigTest = *constant.NewClientConfig(
    25  	constant.WithTimeoutMs(10*1000),
    26  	constant.WithBeatInterval(5*1000),
    27  	constant.WithNotLoadCacheAtStart(true),
    28  )
    29  
    30  var serverConfigTest = *constant.NewServerConfig("console.nacos.io", 80, constant.WithContextPath("/nacos"))
    31  
    32  var services = model.Service{
    33  	Name:            "DEFAULT_GROUP@@DEMO",
    34  	CacheMillis:     1000,
    35  	UseSpecifiedURL: false,
    36  	Hosts: []model.Instance{
    37  		{
    38  			Valid:      true,
    39  			Marked:     false,
    40  			InstanceId: "10.10.10.10-80-a-DEMO",
    41  			Port:       80,
    42  			Ip:         "10.10.10.10",
    43  			Weight:     10,
    44  			Metadata: map[string]string{
    45  				"rootPath": "/api",
    46  			},
    47  			ClusterName: "a",
    48  			ServiceName: "DEMO",
    49  			Enable:      true,
    50  			Healthy:     true,
    51  		},
    52  		{
    53  			Valid:       true,
    54  			Marked:      false,
    55  			InstanceId:  "10.10.10.11-80-a-DEMO",
    56  			Port:        80,
    57  			Ip:          "10.10.10.11",
    58  			Weight:      9,
    59  			Metadata:    map[string]string{},
    60  			ClusterName: "a",
    61  			ServiceName: "DEMO",
    62  			Enable:      true,
    63  			Healthy:     true,
    64  		},
    65  		{
    66  			Valid:       true,
    67  			Marked:      false,
    68  			InstanceId:  "10.10.10.12-80-a-DEMO",
    69  			Port:        80,
    70  			Ip:          "10.10.10.12",
    71  			Weight:      8,
    72  			Metadata:    map[string]string{},
    73  			ClusterName: "a",
    74  			ServiceName: "DEMO",
    75  			Enable:      true,
    76  			Healthy:     false,
    77  		},
    78  		{
    79  			Valid:       true,
    80  			Marked:      false,
    81  			InstanceId:  "10.10.10.13-80-a-DEMO",
    82  			Port:        80,
    83  			Ip:          "10.10.10.13",
    84  			Weight:      7,
    85  			Metadata:    map[string]string{},
    86  			ClusterName: "a",
    87  			ServiceName: "DEMO",
    88  			Enable:      false,
    89  			Healthy:     true,
    90  		},
    91  		{
    92  			Valid:       true,
    93  			Marked:      false,
    94  			InstanceId:  "10.10.10.14-80-a-DEMO",
    95  			Port:        80,
    96  			Ip:          "10.10.10.14",
    97  			Weight:      6,
    98  			Metadata:    map[string]string{},
    99  			ClusterName: "a",
   100  			ServiceName: "DEMO",
   101  			Enable:      true,
   102  			Healthy:     true,
   103  		},
   104  	},
   105  	Checksum:    "3bbcf6dd1175203a8afdade0e77a27cd1528787794594",
   106  	LastRefTime: 1528787794594, Env: "", Clusters: "a",
   107  	Metadata: map[string]string(nil),
   108  }
   109  
   110  func TestNacosRRServiceProvider_SelectServer(t *testing.T) {
   111  	ctrl := gomock.NewController(t)
   112  	defer ctrl.Finish()
   113  
   114  	namingClient := nmock.NewMockINamingClient(ctrl)
   115  	namingClient.
   116  		EXPECT().
   117  		SelectInstances(vo.SelectInstancesParam{
   118  			Clusters:    []string{"a"},
   119  			ServiceName: "testsvc",
   120  			HealthyOnly: true,
   121  		}).
   122  		AnyTimes().
   123  		Return(services.Hosts, nil)
   124  
   125  	n := ddhttp.NewNacosRRServiceProvider("testsvc",
   126  		ddhttp.WithNacosNamingClient(namingClient),
   127  		ddhttp.WithNacosClusters([]string{"a"}))
   128  	for i := 0; i < len(services.Hosts)*2; i++ {
   129  		got := n.SelectServer()
   130  		fmt.Println(got)
   131  	}
   132  }
   133  
   134  func TestNacosWRRServiceProvider_SelectServer(t *testing.T) {
   135  	ctrl := gomock.NewController(t)
   136  	defer ctrl.Finish()
   137  
   138  	namingClient := nmock.NewMockINamingClient(ctrl)
   139  	namingClient.
   140  		EXPECT().
   141  		SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
   142  			Clusters:    []string{"a"},
   143  			ServiceName: "testsvc",
   144  		}).
   145  		AnyTimes().
   146  		Return(&services.Hosts[0], nil)
   147  
   148  	n := ddhttp.NewNacosWRRServiceProvider("testsvc",
   149  		ddhttp.WithNacosNamingClient(namingClient),
   150  		ddhttp.WithNacosClusters([]string{"a"}))
   151  	got := n.SelectServer()
   152  	require.Equal(t, got, "http://10.10.10.10:80/api")
   153  }
   154  
   155  type MockDdClient struct {
   156  	provider registry.IServiceProvider
   157  	client   *resty.Client
   158  	rootPath string
   159  }
   160  
   161  func (receiver *MockDdClient) SetRootPath(rootPath string) {
   162  	receiver.rootPath = rootPath
   163  }
   164  
   165  func (receiver *MockDdClient) SetProvider(provider registry.IServiceProvider) {
   166  	receiver.provider = provider
   167  }
   168  
   169  func (receiver *MockDdClient) SetClient(client *resty.Client) {
   170  	receiver.client = client
   171  }
   172  
   173  func NewMockDdClient(opts ...ddhttp.DdClientOption) *MockDdClient {
   174  	defaultProvider := ddhttp.NewServiceProvider("MOCKDDCLIENT")
   175  	defaultClient := ddhttp.NewClient()
   176  
   177  	svcClient := &MockDdClient{
   178  		provider: defaultProvider,
   179  		client:   defaultClient,
   180  	}
   181  
   182  	for _, opt := range opts {
   183  		opt(svcClient)
   184  	}
   185  
   186  	return svcClient
   187  }
   188  
   189  func TestWithProvider(t *testing.T) {
   190  	Convey("Create a DdClient instance with custom provider", t, func() {
   191  		m := NewMockDdClient(ddhttp.WithProvider(ddhttp.NewMemberlistServiceProvider("mock-svc")))
   192  		So(m.provider, ShouldNotBeZeroValue)
   193  	})
   194  }
   195  
   196  func TestWithClient(t *testing.T) {
   197  	Convey("Create a DdClient instance with custom client", t, func() {
   198  		m := NewMockDdClient(ddhttp.WithClient(resty.New()))
   199  		So(m.client, ShouldNotBeZeroValue)
   200  	})
   201  }
   202  
   203  func TestWithRootPath(t *testing.T) {
   204  	Convey("Create a DdClient instance with custom rootPath", t, func() {
   205  		m := NewMockDdClient(ddhttp.WithRootPath("/v1"))
   206  		So(m.rootPath, ShouldEqual, "/v1")
   207  	})
   208  }
   209  
   210  func TestRetryCount(t *testing.T) {
   211  	Convey("Create a DdClient instance with 10 retry count", t, func() {
   212  		os.Setenv("GDD_RETRY_COUNT", "10")
   213  		m := NewMockDdClient()
   214  		So(m.client.RetryCount, ShouldEqual, 10)
   215  	})
   216  }
   217  
   218  func TestMain(m *testing.M) {
   219  	setup()
   220  	err := registry.NewNode()
   221  	if err != nil {
   222  		panic(err)
   223  	}
   224  	defer registry.Shutdown()
   225  	m.Run()
   226  }
   227  
   228  func setup() {
   229  	_ = config.GddMemSeed.Write("")
   230  	_ = config.GddServiceName.Write("ddhttp")
   231  	_ = config.GddMemName.Write("ddhttp")
   232  	_ = config.GddMemWeight.Write("8")
   233  	_ = config.GddMemDeadTimeout.Write("8s")
   234  	_ = config.GddMemSyncInterval.Write("8s")
   235  	_ = config.GddMemReclaimTimeout.Write("8s")
   236  	_ = config.GddMemProbeInterval.Write("8s")
   237  	_ = config.GddMemProbeTimeout.Write("8s")
   238  	_ = config.GddMemSuspicionMult.Write("8")
   239  	_ = config.GddMemGossipNodes.Write("8")
   240  	_ = config.GddMemGossipInterval.Write("8s")
   241  	_ = config.GddMemWeightInterval.Write("8s")
   242  	_ = config.GddMemTCPTimeout.Write("8s")
   243  	_ = config.GddMemHost.Write("localhost")
   244  	_ = config.GddMemIndirectChecks.Write("8")
   245  	_ = config.GddLogLevel.Write("debug")
   246  	_ = config.GddPort.Write("8088")
   247  	_ = config.GddRouteRootPath.Write("/v1")
   248  }
   249  
   250  func Test_base_AddNode(t *testing.T) {
   251  	Convey("Should select one node", t, func() {
   252  		provider := ddhttp.NewMemberlistServiceProvider("ddhttp")
   253  		provider.AddNode(registry.LocalNode())
   254  		So(provider.SelectServer(), ShouldEqual, fmt.Sprintf("http://%s:%d%s", "localhost", 8088, "/v1"))
   255  
   256  		Convey("Node should be removed", func() {
   257  			provider.RemoveNode(registry.LocalNode())
   258  			So(provider.GetServer("ddhttp"), ShouldBeNil)
   259  		})
   260  	})
   261  }
   262  
   263  func Test_base_AddNode_Fail(t *testing.T) {
   264  	Convey("Should select one node", t, func() {
   265  		provider := ddhttp.NewMemberlistServiceProvider("test")
   266  		provider.AddNode(registry.LocalNode())
   267  		So(provider.SelectServer(), ShouldBeZeroValue)
   268  
   269  		Convey("Node should not be removed", func() {
   270  			provider.RemoveNode(registry.LocalNode())
   271  			So(provider.GetServer("ddhttp"), ShouldBeNil)
   272  		})
   273  	})
   274  }
   275  
   276  type nodeMeta struct {
   277  	Service       string     `json:"service"`
   278  	RouteRootPath string     `json:"routeRootPath"`
   279  	Port          int        `json:"port"`
   280  	RegisterAt    *time.Time `json:"registerAt"`
   281  	GoVer         string     `json:"goVer"`
   282  	GddVer        string     `json:"gddVer"`
   283  	BuildUser     string     `json:"buildUser"`
   284  	BuildTime     string     `json:"buildTime"`
   285  	Weight        int        `json:"weight"`
   286  }
   287  
   288  type mergedMeta struct {
   289  	Meta nodeMeta               `json:"_meta,omitempty"`
   290  	Data map[string]interface{} `json:"data,omitempty"`
   291  }
   292  
   293  func setMetaWeight(node *memberlist.Node, weight int) error {
   294  	var mm mergedMeta
   295  	if len(node.Meta) > 0 {
   296  		r := bytes.NewReader(node.Meta)
   297  		dec := codec.NewDecoder(r, &codec.MsgpackHandle{})
   298  		if err := dec.Decode(&mm); err != nil {
   299  			return err
   300  		}
   301  	}
   302  	mm.Meta.Weight = weight
   303  	var buf bytes.Buffer
   304  	enc := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
   305  	if err := enc.Encode(mm); err != nil {
   306  		return err
   307  	}
   308  	node.Meta = buf.Bytes()
   309  	return nil
   310  }
   311  
   312  func Test_base_UpdateWeight(t *testing.T) {
   313  	_ = setMetaWeight(registry.LocalNode(), 0)
   314  	Convey("Weight should change", t, func() {
   315  		provider := ddhttp.NewMemberlistServiceProvider("ddhttp")
   316  		provider.AddNode(registry.LocalNode())
   317  		registry.LocalNode().Weight = 4
   318  		provider.UpdateWeight(registry.LocalNode())
   319  		So(provider.GetServer("ddhttp").Weight(), ShouldEqual, 4)
   320  	})
   321  }
   322  
   323  func Test_base_UpdateWeight_Fail(t *testing.T) {
   324  	_ = setMetaWeight(registry.LocalNode(), 8)
   325  	Convey("Weight should not change as meta weight greater than 0", t, func() {
   326  		provider := ddhttp.NewMemberlistServiceProvider("ddhttp")
   327  		provider.AddNode(registry.LocalNode())
   328  		registry.LocalNode().Weight = 4
   329  		provider.UpdateWeight(registry.LocalNode())
   330  		So(provider.GetServer("ddhttp").Weight(), ShouldEqual, 8)
   331  	})
   332  }
   333  
   334  func Test_base_UpdateWeight_Fail_SvcName_Mismatch(t *testing.T) {
   335  	_ = setMetaWeight(registry.LocalNode(), 0)
   336  	Convey("Weight should not change as service name mismatch", t, func() {
   337  		provider := ddhttp.NewMemberlistServiceProvider("test")
   338  		provider.UpdateWeight(registry.LocalNode())
   339  		So(provider.GetServer("ddhttp"), ShouldBeNil)
   340  	})
   341  }
   342  
   343  func Test_SMRR_AddNode(t *testing.T) {
   344  	Convey("Should select one node", t, func() {
   345  		provider := ddhttp.NewSmoothWeightedRoundRobinProvider("ddhttp")
   346  		provider.AddNode(registry.LocalNode())
   347  		So(provider.SelectServer(), ShouldEqual, fmt.Sprintf("http://%s:%d%s", "localhost", 8088, "/v1"))
   348  	})
   349  }
   350  
   351  func Test_SMRR_SelectServer(t *testing.T) {
   352  	Convey("Should select none node", t, func() {
   353  		provider := ddhttp.NewSmoothWeightedRoundRobinProvider("ddhttp")
   354  		provider.RemoveNode(registry.LocalNode())
   355  		So(provider.SelectServer(), ShouldBeZeroValue)
   356  	})
   357  }