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 }