This is an automated email from the ASF dual-hosted git repository.

alexstocks pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new 7d4af3b08 Tst:mock etcd and nacos in ut (#1873)
7d4af3b08 is described below

commit 7d4af3b08f1c9fab82819a02b424db41b94489db
Author: pherzheyu <[email protected]>
AuthorDate: Mon May 2 18:13:05 2022 +0800

    Tst:mock etcd and nacos in ut (#1873)
    
    * godoc (#1755)
    
    * update package comment (#1755)
    
    * imports formatter (#1755)
    
    * filter/graceful_shutdown license (#1755)
    
    * filter/graceful_shutdown license (#1755)
    
    * update some comment (#1755)
    
    * gofmt (#1755)
    
    * Update version.go
    
    comment for blank (#1755)
    
    * comment for blank (#1755)
    
    * comment (#1755)
    
    * ut mock nacos and etcd (#1774)
    
    * mock nacos nolint
    
    * mock etcd nolint
    
    * ut mock update (#1774)
---
 cluster/cluster/zoneaware/cluster_invoker_test.go |   4 +-
 config_center/nacos/impl_test.go                  | 262 +++++++++---
 go.mod                                            |   4 +-
 go.sum                                            |  67 +--
 metadata/report/etcd/report_test.go               | 406 +++++++++++++-----
 metadata/report/nacos/report_test.go              | 489 +++++++++++++++++++---
 registry/etcdv3/listener_test.go                  |  96 ++---
 registry/etcdv3/registry_test.go                  | 170 ++++----
 registry/nacos/registry_test.go                   | 450 ++++++++++++--------
 remoting/etcdv3/listener_test.go                  | 132 +-----
 remoting/nacos/builder_test.go                    | 270 +++++++-----
 11 files changed, 1544 insertions(+), 806 deletions(-)

diff --git a/cluster/cluster/zoneaware/cluster_invoker_test.go 
b/cluster/cluster/zoneaware/cluster_invoker_test.go
index 705218eb9..a9ae7c95d 100644
--- a/cluster/cluster/zoneaware/cluster_invoker_test.go
+++ b/cluster/cluster/zoneaware/cluster_invoker_test.go
@@ -68,7 +68,7 @@ func TestZoneWareInvokerWithPreferredSuccess(t *testing.T) {
                        invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
                                func(invocation protocol.Invocation) 
protocol.Result {
                                        return &protocol.RPCResult{}
-                               })
+                               }).AnyTimes()
                }
 
                invokers = append(invokers, invoker)
@@ -166,7 +166,7 @@ func TestZoneWareInvokerWithZoneSuccess(t *testing.T) {
                                        Attrs: 
map[string]interface{}{constant.RegistryZoneKey: zoneValue},
                                        Rest:  clusterpkg.Rest{Tried: 0, 
Success: true},
                                }
-                       })
+                       }).AnyTimes()
                invokers = append(invokers, invoker)
        }
 
diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go
index 2ed505995..209fbaf45 100644
--- a/config_center/nacos/impl_test.go
+++ b/config_center/nacos/impl_test.go
@@ -15,85 +15,253 @@
  * limitations under the License.
  */
 
+// nolint
 package nacos
 
 import (
-       "net/url"
+       "reflect"
        "sync"
        "testing"
-       "time"
 )
 
 import (
-       "github.com/stretchr/testify/assert"
+       gxset "github.com/dubbogo/gost/container/set"
+       nacosClient "github.com/dubbogo/gost/database/kv/nacos"
+
+       "github.com/golang/mock/gomock"
+
+       "github.com/nacos-group/nacos-sdk-go/model"
+       "github.com/nacos-group/nacos-sdk-go/vo"
 )
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
-       "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/config_center"
        "dubbo.apache.org/dubbo-go/v3/config_center/parser"
 )
 
-func getNacosConfig(t *testing.T) config_center.DynamicConfiguration {
-       params := url.Values{}
-       params.Set(constant.NacosNotLoadLocalCache, "true")
+// MockIConfigClient is a mock of IConfigClient interface
+type MockIConfigClient struct {
+       ctrl     *gomock.Controller
+       recorder *MockIConfigClientMockRecorder
+}
+
+// MockIConfigClientMockRecorder is the mock recorder for MockIConfigClient
+type MockIConfigClientMockRecorder struct {
+       mock *MockIConfigClient
+}
 
-       params.Set(constant.NacosNamespaceID, "nacos")
-       params.Set(constant.TimeoutKey, "5s")
-       params.Set(constant.ClientNameKey, "nacos-client")
+// NewMockIConfigClient creates a new mock instance
+func NewMockIConfigClient(ctrl *gomock.Controller) *MockIConfigClient {
+       mock := &MockIConfigClient{ctrl: ctrl}
+       mock.recorder = &MockIConfigClientMockRecorder{mock}
+       return mock
+}
 
-       registryUrl, err := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(params))
-       assert.Nil(t, err)
-       nacosConfig, err := newNacosDynamicConfiguration(registryUrl)
-       assert.Nil(t, err)
-       return nacosConfig
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockIConfigClient) EXPECT() *MockIConfigClientMockRecorder {
+       return m.recorder
 }
 
-func TestPublishConfig(t *testing.T) {
-       nacosConfig := getNacosConfig(t)
-       data := `dubbo.protocol.name=dubbo`
-       err := nacosConfig.PublishConfig("dubbo.properties", "dubbo-go", data)
-       assert.Nil(t, err)
+// GetConfig mocks base method
+func (m *MockIConfigClient) GetConfig(param vo.ConfigParam) (string, error) {
+       ret := m.ctrl.Call(m, "GetConfig", param)
+       ret0, _ := ret[0].(string)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
 }
 
-func TestGetConfig(t *testing.T) {
-       nacosConfig := getNacosConfig(t)
-       nacosConfig.SetParser(&parser.DefaultConfigurationParser{})
+// GetConfig indicates an expected call of GetConfig
+func (mr *MockIConfigClientMockRecorder) GetConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).GetConfig), param)
+}
 
-       config, err := nacosConfig.GetProperties("dubbo.properties", 
config_center.WithGroup("dubbo-go"))
-       assert.NotEmpty(t, config)
-       assert.NoError(t, err)
+// PublishConfig mocks base method
+func (m *MockIConfigClient) PublishConfig(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "PublishConfig", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
 
-       parse, err := nacosConfig.Parser().Parse(config)
-       assert.NoError(t, err)
-       assert.Equal(t, parse["dubbo.protocol.name"], "dubbo")
+// PublishConfig indicates an expected call of PublishConfig
+func (mr *MockIConfigClientMockRecorder) PublishConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).PublishConfig), param)
 }
 
-func TestGetConfigKeysByGroup(t *testing.T) {
-       nacosConfig := getNacosConfig(t)
-       config, err := nacosConfig.GetConfigKeysByGroup("dubbo-go")
-       assert.NoError(t, err)
-       assert.True(t, config.Contains("dubbo.properties"))
+// DeleteConfig mocks base method
+func (m *MockIConfigClient) DeleteConfig(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "DeleteConfig", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
 }
 
-func TestAddListener(t *testing.T) {
-       nacosConfig := getNacosConfig(t)
-       listener := &mockDataListener{}
-       time.Sleep(time.Second * 2)
-       nacosConfig.AddListener("dubbo.properties", listener)
+// DeleteConfig indicates an expected call of DeleteConfig
+func (mr *MockIConfigClientMockRecorder) DeleteConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).DeleteConfig), param)
 }
 
-func TestRemoveListener(_ *testing.T) {
-       // TODO not supported in current go_nacos_sdk version
+// ListenConfig mocks base method
+func (m *MockIConfigClient) ListenConfig(params vo.ConfigParam) error {
+       ret := m.ctrl.Call(m, "ListenConfig", params)
+       ret0, _ := ret[0].(error)
+       return ret0
 }
 
-type mockDataListener struct {
-       wg    sync.WaitGroup
-       event string
+// ListenConfig indicates an expected call of ListenConfig
+func (mr *MockIConfigClientMockRecorder) ListenConfig(params interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).ListenConfig), params)
 }
 
-func (l *mockDataListener) Process(configType 
*config_center.ConfigChangeEvent) {
-       l.wg.Done()
-       l.event = configType.Key
+// CancelListenConfig mocks base method
+func (m *MockIConfigClient) CancelListenConfig(params vo.ConfigParam) error {
+       ret := m.ctrl.Call(m, "CancelListenConfig", params)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// CancelListenConfig indicates an expected call of CancelListenConfig
+func (mr *MockIConfigClientMockRecorder) CancelListenConfig(params 
interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"CancelListenConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).CancelListenConfig), params)
+}
+
+// SearchConfig mocks base method
+func (m *MockIConfigClient) SearchConfig(param vo.SearchConfigParam) 
(*model.ConfigPage, error) {
+       ret := m.ctrl.Call(m, "SearchConfig", param)
+       ret0, _ := ret[0].(*model.ConfigPage)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// SearchConfig indicates an expected call of SearchConfig
+func (mr *MockIConfigClientMockRecorder) SearchConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).SearchConfig), param)
+}
+
+// PublishAggr mocks base method
+func (m *MockIConfigClient) PublishAggr(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "PublishAggr", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// PublishAggr indicates an expected call of PublishAggr
+func (mr *MockIConfigClientMockRecorder) PublishAggr(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishAggr", 
reflect.TypeOf((*MockIConfigClient)(nil).PublishAggr), param)
+}
+
+type fields struct {
+       BaseDynamicConfiguration config_center.BaseDynamicConfiguration
+       url                      *common.URL
+       rootPath                 string
+       wg                       sync.WaitGroup
+       cltLock                  sync.Mutex
+       done                     chan struct{}
+       client                   *nacosClient.NacosConfigClient
+       keyListeners             sync.Map
+       parser                   parser.ConfigurationParser
+}
+type args struct {
+       key   string
+       group string
+       value string
+}
+
+func newnNacosDynamicConfiguration(f fields) *nacosDynamicConfiguration {
+       return &nacosDynamicConfiguration{
+               BaseDynamicConfiguration: f.BaseDynamicConfiguration,
+               url:                      f.url,
+               rootPath:                 f.rootPath,
+               done:                     f.done,
+               client:                   f.client,
+               parser:                   f.parser,
+       }
+}
+
+func Test_nacosDynamicConfiguration_PublishConfig(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               key:   "dubbo.properties",
+                               group: "dubbogo",
+                               value: "dubbo.protocol.name=dubbo",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newnNacosDynamicConfiguration(tt.fields)
+                       if err := n.PublishConfig(tt.args.key, tt.args.group, 
tt.args.value); (err != nil) != tt.wantErr {
+                               t.Errorf("PublishConfig() error = %v, wantErr 
%v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosDynamicConfiguration_GetConfigKeysByGroup(t *testing.T) {
+       cp := &model.ConfigPage{
+               PageItems: []model.ConfigItem{
+                       {
+                               DataId: "dubbogo",
+                       },
+               },
+       }
+       result := gxset.NewSet()
+       result.Add("dubbogo")
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().SearchConfig(gomock.Any()).Return(cp, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               want    *gxset.HashSet
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               group: "dubbo",
+                       },
+                       want:    result,
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newnNacosDynamicConfiguration(tt.fields)
+                       got, err := n.GetConfigKeysByGroup(tt.args.group)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("GetConfigKeysByGroup() error = %v, 
wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("GetConfigKeysByGroup() got = %v, want 
%v", got, tt.want)
+                       }
+               })
+       }
 }
diff --git a/go.mod b/go.mod
index 6b7542613..051901b32 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
        github.com/RoaringBitmap/roaring v0.7.1
        github.com/Workiva/go-datastructures v1.0.52
        github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
+       github.com/agiledragon/gomonkey v2.0.2+incompatible
        github.com/alibaba/sentinel-golang v1.0.4
        github.com/apache/dubbo-getty v1.4.8
        github.com/apache/dubbo-go-hessian2 v1.11.0
@@ -25,7 +26,7 @@ require (
        github.com/go-co-op/gocron v1.9.0
        github.com/go-playground/validator/v10 v10.10.1
        github.com/go-resty/resty/v2 v2.7.0
-       github.com/golang/mock v1.4.4
+       github.com/golang/mock v1.5.0
        github.com/golang/protobuf v1.5.2
        github.com/google/go-cmp v0.5.8
        github.com/grpc-ecosystem/grpc-opentracing 
v0.0.0-20180507213350-8e809c8a8645
@@ -46,7 +47,6 @@ require (
        github.com/zouyx/agollo/v3 v3.4.5
        go.etcd.io/etcd/api/v3 v3.5.4
        go.etcd.io/etcd/client/v3 v3.5.4
-       go.etcd.io/etcd/server/v3 v3.5.4
        go.uber.org/atomic v1.9.0
        go.uber.org/zap v1.21.0
        google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247
diff --git a/go.sum b/go.sum
index 351ed2433..e02c4e527 100644
--- a/go.sum
+++ b/go.sum
@@ -108,7 +108,6 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url 
v1.3.2/go.mod h1:72H
 github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod 
h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
 github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod 
h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
 github.com/aws/smithy-go v1.8.0/go.mod 
h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
-github.com/benbjohnson/clock v1.0.3/go.mod 
h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
 github.com/benbjohnson/clock v1.1.0 
h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/benbjohnson/clock v1.1.0/go.mod 
h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod 
h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -126,9 +125,8 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod 
h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod 
h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/census-instrumentation/opencensus-proto v0.2.1 
h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod 
h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40 
h1:xvUo53O5MRZhVMJAxWCJcS5HHrqAiAG9SJ1LpMu6aAI=
 github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod 
h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 
h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
-github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod 
h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/cespare/xxhash v1.1.0 
h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod 
h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod 
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -238,9 +236,8 @@ github.com/fatih/camelcase v1.0.0/go.mod 
h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
 github.com/fatih/color v1.7.0/go.mod 
h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/structs v1.1.0/go.mod 
h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod 
h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible 
h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod 
h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/form3tech-oss/jwt-go v3.2.3+incompatible 
h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
-github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod 
h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod 
h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod 
h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/frankban/quicktest v1.10.0 
h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
@@ -329,8 +326,9 @@ github.com/golang/mock v1.3.1/go.mod 
h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
 github.com/golang/mock v1.4.0/go.mod 
h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.1/go.mod 
h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.3/go.mod 
h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
 github.com/golang/mock v1.4.4/go.mod 
h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
+github.com/golang/mock v1.5.0/go.mod 
h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod 
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0/go.mod 
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod 
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -347,7 +345,6 @@ github.com/golang/protobuf v1.4.1/go.mod 
h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod 
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod 
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod 
h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 github.com/golang/protobuf v1.5.2 
h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod 
h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod 
h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -362,9 +359,8 @@ github.com/gonum/lapack 
v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2
 github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod 
h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
 github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod 
h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
-github.com/google/btree v1.0.1/go.mod 
h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 github.com/google/go-cmp v0.2.0/go.mod 
h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -415,9 +411,8 @@ github.com/gorilla/websocket v1.4.2/go.mod 
h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod 
h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod 
h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware 
v1.0.1-0.20190118093823-f849b5445de4/go.mod 
h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 
h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
 github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod 
h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 
h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod 
h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 
h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod 
h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod 
h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@@ -759,9 +754,8 @@ github.com/smartystreets/goconvey 
v0.0.0-20190330032615-68dc04aab96a/go.mod h1:s
 github.com/smartystreets/goconvey v1.6.4 
h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 github.com/smartystreets/goconvey v1.6.4/go.mod 
h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod 
h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5 
h1:GJTW+uNMIV1RKwox+T4aN0/sQlYRg78uHZf2H0aBcDw=
 github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod 
h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
-github.com/soheilhy/cmux v0.1.5/go.mod 
h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
 github.com/sony/gobreaker v0.4.1/go.mod 
h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod 
h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spaolacci/murmur3 v1.1.0 
h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
@@ -773,7 +767,6 @@ github.com/spf13/cast v1.3.0 
h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod 
h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.3/go.mod 
h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v1.1.1/go.mod 
h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
-github.com/spf13/cobra v1.1.3/go.mod 
h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/jwalterweatherman v1.0.0 
h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod 
h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -838,9 +831,8 @@ github.com/zouyx/agollo/v3 v3.4.5 
h1:7YCxzY9ZYaH9TuVUBvmI6Tk0mwMggikah+cfbYogcHQ
 github.com/zouyx/agollo/v3 v3.4.5/go.mod 
h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
-go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 
h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod 
h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod 
h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw=
@@ -848,21 +840,17 @@ go.etcd.io/etcd/api/v3 v3.5.4 
h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
 go.etcd.io/etcd/api/v3 v3.5.4/go.mod 
h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
 go.etcd.io/etcd/client/pkg/v3 v3.5.4 
h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
 go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod 
h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0-alpha.0 
h1:jZepGpOeJATxsbMNBZczDS2jHdK/QVHM1iPe9jURJ8o=
 go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod 
h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU=
-go.etcd.io/etcd/client/v2 v2.305.4 
h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao=
-go.etcd.io/etcd/client/v2 v2.305.4/go.mod 
h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
 go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod 
h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8=
 go.etcd.io/etcd/client/v3 v3.5.4 
h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
 go.etcd.io/etcd/client/v3 v3.5.4/go.mod 
h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
+go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 
h1:3yLUEC0nFCxw/RArImOyRUI4OAFbg4PFpBbAhSNzKNY=
 go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod 
h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY=
-go.etcd.io/etcd/pkg/v3 v3.5.4 h1:V5Dvl7S39ZDwjkKqJG2BfXgxZ3QREqqKifWQgIw5IM0=
-go.etcd.io/etcd/pkg/v3 v3.5.4/go.mod 
h1:OI+TtO+Aa3nhQSppMbwE4ld3uF1/fqqwbpfndbbrEe0=
+go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 
h1:DvYJotxV9q1Lkn7pknzAbFO/CLtCVidCr2K9qRLJ8pA=
 go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod 
h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w=
-go.etcd.io/etcd/raft/v3 v3.5.4 h1:YGrnAgRfgXloBNuqa+oBI/aRZMcK/1GS6trJePJ/Gqc=
-go.etcd.io/etcd/raft/v3 v3.5.4/go.mod 
h1:SCuunjYvZFC0fBX0vxMSPjuZmpcSk+XaAcMrD6Do03w=
+go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 
h1:fYv7CmmdyuIu27UmKQjS9K/1GtcCa+XnPKqiKBbQkrk=
 go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod 
h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ=
-go.etcd.io/etcd/server/v3 v3.5.4 
h1:CMAZd0g8Bn5NRhynW6pKhc4FRg41/0QYy3d7aNm9874=
-go.etcd.io/etcd/server/v3 v3.5.4/go.mod 
h1:S5/YTU15KxymM5l3T6b09sNOHPXqGYIZStpuuGbb65c=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -872,27 +860,6 @@ go.opencensus.io v0.22.3/go.mod 
h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/contrib v0.20.0 
h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0=
-go.opentelemetry.io/contrib v0.20.0/go.mod 
h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
-go.opentelemetry.io/otel v0.20.0 
h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
-go.opentelemetry.io/otel v0.20.0/go.mod 
h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
-go.opentelemetry.io/otel/exporters/otlp v0.20.0 
h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg=
-go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod 
h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
-go.opentelemetry.io/otel/metric v0.20.0 
h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
-go.opentelemetry.io/otel/metric v0.20.0/go.mod 
h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
-go.opentelemetry.io/otel/oteltest v0.20.0 
h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw=
-go.opentelemetry.io/otel/oteltest v0.20.0/go.mod 
h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
-go.opentelemetry.io/otel/sdk v0.20.0 
h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
-go.opentelemetry.io/otel/sdk v0.20.0/go.mod 
h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
-go.opentelemetry.io/otel/sdk/export/metric v0.20.0 
h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g=
-go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod 
h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
-go.opentelemetry.io/otel/sdk/metric v0.20.0 
h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8=
-go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod 
h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
-go.opentelemetry.io/otel/trace v0.20.0 
h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
-go.opentelemetry.io/otel/trace v0.20.0/go.mod 
h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
-go.opentelemetry.io/proto/otlp v0.7.0 
h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod 
h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.3.2/go.mod 
h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod 
h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -901,7 +868,6 @@ go.uber.org/atomic v1.6.0/go.mod 
h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.7.0/go.mod 
h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/atomic v1.9.0/go.mod 
h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v1.1.10/go.mod 
h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod 
h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
 go.uber.org/goleak v1.1.11/go.mod 
h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
@@ -931,9 +897,8 @@ golang.org/x/crypto 
v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 
h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 
h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE=
-golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1099,7 +1064,6 @@ golang.org/x/sys 
v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1139,9 +1103,8 @@ golang.org/x/time 
v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 
h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba 
h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1166,7 +1129,6 @@ golang.org/x/tools 
v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1301,7 +1263,6 @@ google.golang.org/grpc v1.32.0/go.mod 
h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
 google.golang.org/grpc v1.33.1/go.mod 
h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.33.2/go.mod 
h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
 google.golang.org/grpc v1.36.0/go.mod 
h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod 
h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.38.0/go.mod 
h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.40.0/go.mod 
h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.41.0/go.mod 
h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
diff --git a/metadata/report/etcd/report_test.go 
b/metadata/report/etcd/report_test.go
index 663c6918e..09a32e212 100644
--- a/metadata/report/etcd/report_test.go
+++ b/metadata/report/etcd/report_test.go
@@ -19,15 +19,17 @@ package etcd
 
 import (
        "encoding/json"
-       "net/url"
+       "reflect"
        "strconv"
        "testing"
 )
 
 import (
-       "github.com/stretchr/testify/assert"
+       "github.com/agiledragon/gomonkey"
 
-       "go.etcd.io/etcd/server/v3/embed"
+       gxetcd "github.com/dubbogo/gost/database/kv/etcd/v3"
+
+       "go.etcd.io/etcd/client/v3"
 )
 
 import (
@@ -36,114 +38,6 @@ import (
        "dubbo.apache.org/dubbo-go/v3/metadata/identifier"
 )
 
-const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-registry.etcd"
-
-func initEtcd(t *testing.T) *embed.Etcd {
-       DefaultListenPeerURLs := "http://localhost:2380";
-       DefaultListenClientURLs := "http://localhost:2379";
-       lpurl, _ := url.Parse(DefaultListenPeerURLs)
-       lcurl, _ := url.Parse(DefaultListenClientURLs)
-       cfg := embed.NewConfig()
-       cfg.LPUrls = []url.URL{*lpurl}
-       cfg.LCUrls = []url.URL{*lcurl}
-       cfg.Dir = defaultEtcdV3WorkDir
-       e, err := embed.StartEtcd(cfg)
-       if err != nil {
-               t.Fatal(err)
-       }
-       return e
-}
-
-func TestEtcdMetadataReportFactory_CreateMetadataReport(t *testing.T) {
-       e := initEtcd(t)
-       url, err := common.NewURL("registry://127.0.0.1:2379", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-       if err != nil {
-               t.Fatal(err)
-       }
-       metadataReportFactory := &etcdMetadataReportFactory{}
-       metadataReport := metadataReportFactory.CreateMetadataReport(url)
-       assert.NotNil(t, metadataReport)
-       e.Close()
-}
-
-func TestEtcdMetadataReport_CRUD(t *testing.T) {
-       e := initEtcd(t)
-       url, err := common.NewURL("registry://127.0.0.1:2379", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-       if err != nil {
-               t.Fatal(err)
-       }
-       metadataReportFactory := &etcdMetadataReportFactory{}
-       metadataReport := metadataReportFactory.CreateMetadataReport(url)
-       assert.NotNil(t, metadataReport)
-
-       err = 
metadataReport.StoreConsumerMetadata(newMetadataIdentifier("consumer"), 
"consumer metadata")
-       assert.Nil(t, err)
-
-       err = 
metadataReport.StoreProviderMetadata(newMetadataIdentifier("provider"), 
"provider metadata")
-       assert.Nil(t, err)
-
-       serviceMi := newServiceMetadataIdentifier()
-       serviceUrl, err := common.NewURL("registry://localhost:8848", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-       assert.Nil(t, err)
-       err = metadataReport.SaveServiceMetadata(serviceMi, serviceUrl)
-       assert.Nil(t, err)
-
-       subMi := newSubscribeMetadataIdentifier()
-       urlList := make([]string, 0, 1)
-       urlList = append(urlList, serviceUrl.String())
-       urls, _ := json.Marshal(urlList)
-       err = metadataReport.SaveSubscribedData(subMi, string(urls))
-       assert.Nil(t, err)
-
-       serviceUrl, _ = 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
-       metadataInfo := common.NewMetadataInfo(subMi.Application, "", 
map[string]*common.ServiceInfo{
-               "com.ikurento.user.UserProvider": 
common.NewServiceInfoWithURL(serviceUrl),
-       })
-       err = metadataReport.RemoveServiceMetadata(serviceMi)
-       assert.Nil(t, err)
-       err = metadataReport.PublishAppMetadata(subMi, metadataInfo)
-       assert.Nil(t, err)
-
-       mdInfo, err := metadataReport.GetAppMetadata(subMi)
-       assert.Nil(t, err)
-       assert.Equal(t, metadataInfo.App, mdInfo.App)
-       assert.Equal(t, metadataInfo.Revision, mdInfo.Revision)
-       assert.Equal(t, 1, len(mdInfo.Services))
-       assert.NotNil(t, 
metadataInfo.Services["com.ikurento.user.UserProvider"])
-
-       e.Close()
-}
-
-func TestEtcdMetadataReport_ServiceAppMapping(t *testing.T) {
-       e := initEtcd(t)
-       url, err := common.NewURL("registry://127.0.0.1:2379", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-       if err != nil {
-               t.Fatal(err)
-       }
-       metadataReportFactory := &etcdMetadataReportFactory{}
-       metadataReport := metadataReportFactory.CreateMetadataReport(url)
-       assert.NotNil(t, metadataReport)
-
-       _, err = 
metadataReport.GetServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter", 
"mapping")
-       assert.NotNil(t, err)
-
-       err = 
metadataReport.RegisterServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter",
 "mapping", "demo_provider")
-       assert.Nil(t, err)
-       set, err := 
metadataReport.GetServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter", 
"mapping")
-       assert.Nil(t, err)
-       assert.Equal(t, 1, set.Size())
-
-       err = 
metadataReport.RegisterServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter",
 "mapping", "demo_provider2")
-       assert.Nil(t, err)
-       err = 
metadataReport.RegisterServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter",
 "mapping", "demo_provider")
-       assert.Nil(t, err)
-       set, err = 
metadataReport.GetServiceAppMapping("com.apache.dubbo.sample.basic.IGreeter", 
"mapping")
-       assert.Nil(t, err)
-       assert.Equal(t, 2, set.Size())
-
-       e.Close()
-}
-
 func newSubscribeMetadataIdentifier() *identifier.SubscriberMetadataIdentifier 
{
        return &identifier.SubscriberMetadataIdentifier{
                Revision: "subscribe",
@@ -177,3 +71,293 @@ func newMetadataIdentifier(side string) 
*identifier.MetadataIdentifier {
                },
        }
 }
+
+type fields struct {
+       client *gxetcd.Client
+       root   string
+}
+type args struct {
+       subscriberMetadataIdentifier *identifier.SubscriberMetadataIdentifier
+       info                         *common.MetadataInfo
+       providerIdentifier           *identifier.MetadataIdentifier
+       serviceDefinitions           string
+       consumerMetadataIdentifier   *identifier.MetadataIdentifier
+       serviceParameterString       string
+       serviceMetadataIdentifier    *identifier.ServiceMetadataIdentifier
+       url                          *common.URL
+       urls                         string
+}
+
+func newEtcdMetadataReport(f fields) *etcdMetadataReport {
+       return &etcdMetadataReport{
+               client: f.client,
+               root:   f.root,
+       }
+}
+
+func Test_etcdMetadataReport_PublishAppMetadata(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Put", func(_ 
*gxetcd.Client, k, v string, opts ...clientv3.OpOption) error {
+               return nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   "/dubbo",
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
newSubscribeMetadataIdentifier(),
+                               info:                         
&common.MetadataInfo{},
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.PublishAppMetadata(tt.args.subscriberMetadataIdentifier, tt.args.info); (err 
!= nil) != tt.wantErr {
+                               t.Errorf("PublishAppMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_StoreProviderMetadata(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Put", func(_ 
*gxetcd.Client, k, v string, opts ...clientv3.OpOption) error {
+               return nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   "/dubbo",
+                       },
+                       args: args{
+                               providerIdentifier: 
newMetadataIdentifier("provuder"),
+                               serviceDefinitions: "provider",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.StoreProviderMetadata(tt.args.providerIdentifier, 
tt.args.serviceDefinitions); (err != nil) != tt.wantErr {
+                               t.Errorf("StoreProviderMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_StoreConsumerMetadata(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Put", func(_ 
*gxetcd.Client, k, v string, opts ...clientv3.OpOption) error {
+               return nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   "/dubbo",
+                       },
+                       args: args{
+                               consumerMetadataIdentifier: 
newMetadataIdentifier("conusmer"),
+                               serviceParameterString:     "conusmer",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.StoreConsumerMetadata(tt.args.consumerMetadataIdentifier, 
tt.args.serviceParameterString); (err != nil) != tt.wantErr {
+                               t.Errorf("StoreConsumerMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_SaveServiceMetadata(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Put", func(_ 
*gxetcd.Client, k, v string, opts ...clientv3.OpOption) error {
+               return nil
+       })
+       defer patches.Reset()
+       serviceURL, _ := common.NewURL("registry://localhost:8848", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   "/dubbo",
+                       },
+                       args: args{
+                               serviceMetadataIdentifier: 
newServiceMetadataIdentifier(),
+                               url:                       serviceURL,
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.SaveServiceMetadata(tt.args.serviceMetadataIdentifier, tt.args.url); (err != 
nil) != tt.wantErr {
+                               t.Errorf("SaveServiceMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_SaveSubscribedData(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Put", func(_ 
*gxetcd.Client, k, v string, opts ...clientv3.OpOption) error {
+               return nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   "/dubbo",
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
newSubscribeMetadataIdentifier(),
+                               urls:                         "dubbogo",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.SaveSubscribedData(tt.args.subscriberMetadataIdentifier, tt.args.urls); (err 
!= nil) != tt.wantErr {
+                               t.Errorf("SaveSubscribedData() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_RemoveServiceMetadata(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Delete", func(_ 
*gxetcd.Client, k string) error {
+               return nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   DEFAULT_ROOT,
+                       },
+                       args: args{
+                               serviceMetadataIdentifier: 
newServiceMetadataIdentifier(),
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       if err := 
e.RemoveServiceMetadata(tt.args.serviceMetadataIdentifier); (err != nil) != 
tt.wantErr {
+                               t.Errorf("RemoveServiceMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_etcdMetadataReport_GetAppMetadata(t *testing.T) {
+       info := &common.MetadataInfo{}
+       target, _ := json.Marshal(info)
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "Get", func(_ 
*gxetcd.Client, k string) (string, error) {
+               return string(target), nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               want    *common.MetadataInfo
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                               root:   DEFAULT_ROOT,
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
newSubscribeMetadataIdentifier(),
+                       },
+                       want:    &common.MetadataInfo{},
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       e := newEtcdMetadataReport(tt.fields)
+                       got, err := 
e.GetAppMetadata(tt.args.subscriberMetadataIdentifier)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("GetAppMetadata() error = %v, wantErr 
%v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("GetAppMetadata() got = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/metadata/report/nacos/report_test.go 
b/metadata/report/nacos/report_test.go
index c6532a7ef..388bf81a5 100644
--- a/metadata/report/nacos/report_test.go
+++ b/metadata/report/nacos/report_test.go
@@ -18,59 +18,27 @@
 package nacos
 
 import (
-       "net/http"
-       "net/url"
+       "encoding/json"
+       "reflect"
        "strconv"
        "testing"
-       "time"
 )
 
 import (
-       "github.com/stretchr/testify/assert"
+       nacosClient "github.com/dubbogo/gost/database/kv/nacos"
+
+       "github.com/golang/mock/gomock"
+
+       "github.com/nacos-group/nacos-sdk-go/model"
+       "github.com/nacos-group/nacos-sdk-go/vo"
 )
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
-       "dubbo.apache.org/dubbo-go/v3/common/extension"
        "dubbo.apache.org/dubbo-go/v3/metadata/identifier"
-       "dubbo.apache.org/dubbo-go/v3/metadata/report"
 )
 
-func TestNacosMetadataReport_CRUD(t *testing.T) {
-       if !checkNacosServerAlive() {
-               return
-       }
-       rpt := newTestReport()
-       assert.NotNil(t, rpt)
-
-       providerMi := newMetadataIdentifier("server")
-       providerMeta := "provider"
-       err := rpt.StoreProviderMetadata(providerMi, providerMeta)
-       assert.Nil(t, err)
-
-       consumerMi := newMetadataIdentifier("client")
-       consumerMeta := "consumer"
-       err = rpt.StoreConsumerMetadata(consumerMi, consumerMeta)
-       assert.Nil(t, err)
-
-       serviceMi := newServiceMetadataIdentifier()
-
-       serviceUrl, _ := common.NewURL("registry://console.nacos.io:80",
-               common.WithParamsValue(constant.RegistryRoleKey, 
strconv.Itoa(common.PROVIDER)),
-               common.WithParamsValue(constant.ClientNameKey, "nacos-client"))
-
-       err = rpt.SaveServiceMetadata(serviceMi, serviceUrl)
-       assert.Nil(t, err)
-
-       exportedUrls, err := rpt.GetExportedURLs(serviceMi)
-       assert.Equal(t, 1, len(exportedUrls))
-       assert.Nil(t, err)
-
-       err = rpt.RemoveServiceMetadata(serviceMi)
-       assert.Nil(t, err)
-}
-
 func newServiceMetadataIdentifier() *identifier.ServiceMetadataIdentifier {
        return &identifier.ServiceMetadataIdentifier{
                Protocol: "nacos",
@@ -96,30 +64,435 @@ func newMetadataIdentifier(side string) 
*identifier.MetadataIdentifier {
        }
 }
 
-func TestNacosMetadataReportFactory_CreateMetadataReport(t *testing.T) {
-       res := newTestReport()
-       assert.NotNil(t, res)
+// MockIConfigClient is a mock of IConfigClient interface
+type MockIConfigClient struct {
+       ctrl     *gomock.Controller
+       recorder *MockIConfigClientMockRecorder
+}
+
+// MockIConfigClientMockRecorder is the mock recorder for MockIConfigClient
+type MockIConfigClientMockRecorder struct {
+       mock *MockIConfigClient
+}
+
+// NewMockIConfigClient creates a new mock instance
+func NewMockIConfigClient(ctrl *gomock.Controller) *MockIConfigClient {
+       mock := &MockIConfigClient{ctrl: ctrl}
+       mock.recorder = &MockIConfigClientMockRecorder{mock}
+       return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockIConfigClient) EXPECT() *MockIConfigClientMockRecorder {
+       return m.recorder
+}
+
+// GetConfig mocks base method
+func (m *MockIConfigClient) GetConfig(param vo.ConfigParam) (string, error) {
+       ret := m.ctrl.Call(m, "GetConfig", param)
+       ret0, _ := ret[0].(string)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// GetConfig indicates an expected call of GetConfig
+func (mr *MockIConfigClientMockRecorder) GetConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).GetConfig), param)
+}
+
+// PublishConfig mocks base method
+func (m *MockIConfigClient) PublishConfig(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "PublishConfig", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
 }
 
-func newTestReport() report.MetadataReport {
-       params := url.Values{}
-       params.Set(constant.NacosNotLoadLocalCache, "true")
+// PublishConfig indicates an expected call of PublishConfig
+func (mr *MockIConfigClientMockRecorder) PublishConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).PublishConfig), param)
+}
 
-       params.Set(constant.NacosNamespaceID, "nacos")
-       params.Set(constant.TimeoutKey, "5s")
-       params.Set(constant.ClientNameKey, "nacos-client")
-       params.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
+// DeleteConfig mocks base method
+func (m *MockIConfigClient) DeleteConfig(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "DeleteConfig", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
 
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(params))
+// DeleteConfig indicates an expected call of DeleteConfig
+func (mr *MockIConfigClientMockRecorder) DeleteConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).DeleteConfig), param)
+}
 
-       res := 
extension.GetMetadataReportFactory("nacos").CreateMetadataReport(regurl)
-       return res
+// ListenConfig mocks base method
+func (m *MockIConfigClient) ListenConfig(params vo.ConfigParam) error {
+       ret := m.ctrl.Call(m, "ListenConfig", params)
+       ret0, _ := ret[0].(error)
+       return ret0
 }
 
-func checkNacosServerAlive() bool {
-       c := http.Client{Timeout: time.Second}
-       if _, err := c.Get("http://console.nacos.io/nacos/";); err != nil {
-               return false
+// ListenConfig indicates an expected call of ListenConfig
+func (mr *MockIConfigClientMockRecorder) ListenConfig(params interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).ListenConfig), params)
+}
+
+// CancelListenConfig mocks base method
+func (m *MockIConfigClient) CancelListenConfig(params vo.ConfigParam) error {
+       ret := m.ctrl.Call(m, "CancelListenConfig", params)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// CancelListenConfig indicates an expected call of CancelListenConfig
+func (mr *MockIConfigClientMockRecorder) CancelListenConfig(params 
interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"CancelListenConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).CancelListenConfig), params)
+}
+
+// SearchConfig mocks base method
+func (m *MockIConfigClient) SearchConfig(param vo.SearchConfigParam) 
(*model.ConfigPage, error) {
+       ret := m.ctrl.Call(m, "SearchConfig", param)
+       ret0, _ := ret[0].(*model.ConfigPage)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// SearchConfig indicates an expected call of SearchConfig
+func (mr *MockIConfigClientMockRecorder) SearchConfig(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchConfig", 
reflect.TypeOf((*MockIConfigClient)(nil).SearchConfig), param)
+}
+
+// PublishAggr mocks base method
+func (m *MockIConfigClient) PublishAggr(param vo.ConfigParam) (bool, error) {
+       ret := m.ctrl.Call(m, "PublishAggr", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// PublishAggr indicates an expected call of PublishAggr
+func (mr *MockIConfigClientMockRecorder) PublishAggr(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishAggr", 
reflect.TypeOf((*MockIConfigClient)(nil).PublishAggr), param)
+}
+
+type fields struct {
+       client *nacosClient.NacosConfigClient
+}
+type args struct {
+       subscriberMetadataIdentifier *identifier.SubscriberMetadataIdentifier
+       info                         *common.MetadataInfo
+       providerIdentifier           *identifier.MetadataIdentifier
+       serviceDefinitions           string
+       consumerMetadataIdentifier   *identifier.MetadataIdentifier
+       serviceParameterString       string
+       url                          *common.URL
+       serviceMetadataIdentifier    *identifier.ServiceMetadataIdentifier
+       urls                         string
+       key                          string
+       group                        string
+       value                        string
+}
+
+func newNacosMetadataReport(f fields) *nacosMetadataReport {
+       return &nacosMetadataReport{
+               client: f.client,
+       }
+}
+
+func Test_nacosMetadataReport_GetAppMetadata(t *testing.T) {
+       mi := common.MetadataInfo{
+               App: "GetAppMetadata",
+       }
+       data, _ := json.Marshal(mi)
+
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().GetConfig(gomock.Any()).Return(string(data), nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               want    *common.MetadataInfo
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
&identifier.SubscriberMetadataIdentifier{},
+                       },
+                       want:    &mi,
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       got, err := 
n.GetAppMetadata(tt.args.subscriberMetadataIdentifier)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("GetAppMetadata() error = %v, wantErr 
%v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("GetAppMetadata() got = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_PublishAppMetadata(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
&identifier.SubscriberMetadataIdentifier{},
+                               info: &common.MetadataInfo{
+                                       App: "PublishAppMetadata",
+                               },
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.PublishAppMetadata(tt.args.subscriberMetadataIdentifier, tt.args.info); (err 
!= nil) != tt.wantErr {
+                               t.Errorf("PublishAppMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_StoreProviderMetadata(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               providerIdentifier: 
newMetadataIdentifier("provider"),
+                               serviceDefinitions: "provider",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.StoreProviderMetadata(tt.args.providerIdentifier, 
tt.args.serviceDefinitions); (err != nil) != tt.wantErr {
+                               t.Errorf("StoreProviderMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_StoreConsumerMetadata(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               consumerMetadataIdentifier: 
newMetadataIdentifier("conusmer"),
+                               serviceParameterString:     "conusmer",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.StoreConsumerMetadata(tt.args.consumerMetadataIdentifier, 
tt.args.serviceParameterString); (err != nil) != tt.wantErr {
+                               t.Errorf("StoreConsumerMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_SaveServiceMetadata(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       serviceURL, _ := common.NewURL("registry://test.nacos.io:80",
+               common.WithParamsValue(constant.RegistryRoleKey, 
strconv.Itoa(common.PROVIDER)),
+               common.WithParamsValue(constant.ClientNameKey, "nacos-client"))
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               serviceMetadataIdentifier: 
newServiceMetadataIdentifier(),
+                               url:                       serviceURL,
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.SaveServiceMetadata(tt.args.serviceMetadataIdentifier, tt.args.url); (err != 
nil) != tt.wantErr {
+                               t.Errorf("SaveServiceMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_RemoveServiceMetadata(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().DeleteConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               serviceMetadataIdentifier: 
newServiceMetadataIdentifier(),
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.RemoveServiceMetadata(tt.args.serviceMetadataIdentifier); (err != nil) != 
tt.wantErr {
+                               t.Errorf("RemoveServiceMetadata() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_SaveSubscribedData(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().PublishConfig(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               subscriberMetadataIdentifier: 
&identifier.SubscriberMetadataIdentifier{},
+                               urls:                         "urls",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := 
n.SaveSubscribedData(tt.args.subscriberMetadataIdentifier, tt.args.urls); (err 
!= nil) != tt.wantErr {
+                               t.Errorf("SaveSubscribedData() error = %v, 
wantErr %v", err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func Test_nacosMetadataReport_RegisterServiceAppMapping(t *testing.T) {
+       ctrl := gomock.NewController(t)
+       mnc := NewMockIConfigClient(ctrl)
+       mnc.EXPECT().GetConfig(gomock.Any()).Return("oldValue", nil)
+       nc := &nacosClient.NacosConfigClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: nc,
+                       },
+                       args: args{
+                               key:   "test",
+                               group: "test",
+                               value: "oldValue",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       n := newNacosMetadataReport(tt.fields)
+                       if err := n.RegisterServiceAppMapping(tt.args.key, 
tt.args.group, tt.args.value); (err != nil) != tt.wantErr {
+                               t.Errorf("RegisterServiceAppMapping() error = 
%v, wantErr %v", err, tt.wantErr)
+                       }
+               })
        }
-       return true
 }
diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go
index 97f95e0f4..da3154c24 100644
--- a/registry/etcdv3/listener_test.go
+++ b/registry/etcdv3/listener_test.go
@@ -18,17 +18,7 @@
 package etcdv3
 
 import (
-       "os"
        "testing"
-       "time"
-)
-
-import (
-       gxtime "github.com/dubbogo/gost/time"
-
-       "github.com/stretchr/testify/suite"
-
-       "go.etcd.io/etcd/server/v3/embed"
 )
 
 import (
@@ -37,60 +27,48 @@ import (
        "dubbo.apache.org/dubbo-go/v3/remoting"
 )
 
-type RegistryTestSuite struct {
-       suite.Suite
-       etcd *embed.Etcd
-}
-
-const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-registry.etcd"
-
-// start etcd server
-func (suite *RegistryTestSuite) SetupSuite() {
-       t := suite.T()
-
-       cfg := embed.NewConfig()
-       // avoid conflict with default etcd work-dir
-       cfg.Dir = defaultEtcdV3WorkDir
-       e, err := embed.StartEtcd(cfg)
-       if err != nil {
-               t.Fatal(err)
-       }
+type MockDataListener struct{}
 
-       select {
-       case <-e.Server.ReadyNotify():
-               t.Log("Server is ready!")
-       case <-gxtime.After(60 * time.Second):
-               e.Server.Stop() // trigger a shutdown
-               t.Logf("Server took too long to start!")
-       }
+func (*MockDataListener) Process(configType *config_center.ConfigChangeEvent) 
{}
 
-       suite.etcd = e
+type dataListenerFields struct {
+       interestedURL []*common.URL
+       listener      config_center.ConfigurationListener
 }
 
-// stop etcd server
-func (suite *RegistryTestSuite) TearDownSuite() {
-       suite.etcd.Close()
-       // clean the etcd workdir
-       if err := os.RemoveAll(defaultEtcdV3WorkDir); err != nil {
-               suite.FailNow(err.Error())
+func newDataListener(listenerFields dataListenerFields) *dataListener {
+       return &dataListener{
+               interestedURL: listenerFields.interestedURL,
+               listener:      listenerFields.listener,
        }
 }
-
-func (suite *RegistryTestSuite) TestDataChange() {
-       t := suite.T()
-
-       listener := NewRegistryDataListener(&MockDataListener{})
-       url, _ := 
common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bs
 [...]
-       listener.AddInterestedURL(url)
-       if !listener.DataChange(remoting.Event{Path: 
"/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retrie
 [...]
-               t.Fatal("data change not ok")
+func Test_dataListener_DataChange(t *testing.T) {
+       tests := []struct {
+               name   string
+               fields dataListenerFields
+               args   args
+               want   bool
+       }{
+               {
+                       name: "test",
+                       fields: dataListenerFields{
+                               interestedURL: nil,
+                               listener:      &MockDataListener{},
+                       },
+                       args: args{
+                               eventType: remoting.Event{
+                                       Path: 
"com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26modul
 [...]
+                               },
+                       },
+                       want: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       l := newDataListener(tt.fields)
+                       if got := l.DataChange(tt.args.eventType); got != 
tt.want {
+                               t.Errorf("DataChange() = %v, want %v", got, 
tt.want)
+                       }
+               })
        }
 }
-
-func TestRegistrySuite(t *testing.T) {
-       suite.Run(t, &RegistryTestSuite{})
-}
-
-type MockDataListener struct{}
-
-func (*MockDataListener) Process(configType *config_center.ConfigChangeEvent) 
{}
diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go
index 7ff64057f..56aa2e7b0 100644
--- a/registry/etcdv3/registry_test.go
+++ b/registry/etcdv3/registry_test.go
@@ -15,113 +15,105 @@
  * limitations under the License.
  */
 
+// nolint
 package etcdv3
 
 import (
-       "strconv"
+       "reflect"
+       "sync"
        "testing"
-       "time"
 )
 
 import (
-       "github.com/stretchr/testify/assert"
+       "github.com/agiledragon/gomonkey"
+
+       gxetcd "github.com/dubbogo/gost/database/kv/etcd/v3"
 )
 
 import (
-       "dubbo.apache.org/dubbo-go/v3/common"
-       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/registry"
+       "dubbo.apache.org/dubbo-go/v3/remoting"
+       "dubbo.apache.org/dubbo-go/v3/remoting/etcdv3"
 )
 
-func initRegistry(t *testing.T) *etcdV3Registry {
-       regurl, err := common.NewURL("registry://127.0.0.1:2379", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-       if err != nil {
-               t.Fatal(err)
-       }
+type fields struct {
+       BaseRegistry   registry.BaseRegistry
+       cltLock        sync.Mutex
+       client         *gxetcd.Client
+       listenerLock   sync.RWMutex
+       listener       *etcdv3.EventListener
+       dataListener   *dataListener
+       configListener *configurationListener
+}
+type args struct {
+       root      string
+       node      string
+       eventType remoting.Event
+}
 
-       reg, err := newETCDV3Registry(regurl)
-       if err != nil {
-               t.Fatal(err)
+func newEtcdV3Registry(f fields) *etcdV3Registry {
+       return &etcdV3Registry{
+               client:         f.client,
+               listener:       f.listener,
+               dataListener:   f.dataListener,
+               configListener: f.configListener,
        }
-
-       out := reg.(*etcdV3Registry)
-       err = out.client.CleanKV()
-       assert.NoError(t, err)
-       return out
 }
 
-//
-//func (suite *RegistryTestSuite) TestRegister() {
-//     t := suite.T()
-//
-//     url, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParamsValue(constant.ClusterKey, "mock"), 
common.WithMethods([]string{"GetUser", "AddUser"}))
-//
-//     reg := initRegistry(t)
-//     err := reg.Register(url)
-//     assert.NoError(t, err)
-//     children, _, err := 
reg.client.GetChildrenKVList("/dubbo/com.ikurento.user.UserProvider/providers")
-//     if err != nil {
-//             t.Fatal(err)
-//     }
-//     assert.Regexp(t, 
".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock",
 children)
-//     assert.NoError(t, err)
-//}
-//
-//func (suite *RegistryTestSuite) TestSubscribe() {
-//     t := suite.T()
-//     regurl, _ := common.NewURL("registry://127.0.0.1:1111", 
common.WithParamsValue(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER)))
-//     url, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParamsValue(constant.ClusterKey, "mock"), 
common.WithMethods([]string{"GetUser", "AddUser"}))
-//
-//     reg := initRegistry(t)
-//     // provider register
-//     err := reg.Register(url)
-//     if err != nil {
-//             t.Fatal(err)
-//     }
-//
-//     // consumer register
-//     regurl.SetParam(constant.RegistryRoleKey, strconv.Itoa(common.Consumer))
-//     reg2 := initRegistry(t)
-//
-//     err = reg2.Register(url)
-//     assert.NoError(t, err)
-//     listener, err := reg2.DoSubscribe(url)
-//     if err != nil {
-//             t.Fatal(err)
-//     }
-//
-//     serviceEvent, err := listener.Next()
-//     if err != nil {
-//             t.Fatal(err)
-//     }
-//     assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent.String())
-//}
-
-func (suite *RegistryTestSuite) TestConsumerDestroy() {
-       t := suite.T()
-       url, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParamsValue(constant.ClusterKey, "mock"), 
common.WithMethods([]string{"GetUser", "AddUser"}))
+func Test_etcdV3Registry_DoRegister(t *testing.T) {
+       var client *gxetcd.Client
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyMethod(reflect.TypeOf(client), "RegisterTemp", 
func(_ *gxetcd.Client, k, v string) error {
+               return nil
+       })
+       defer patches.Reset()
 
-       reg := initRegistry(t)
-       _, err := reg.DoSubscribe(url)
-       if err != nil {
-               t.Fatal(err)
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               client: client,
+                       },
+                       args: args{
+                               root: "/dubbo",
+                               node: "/go",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       r := newEtcdV3Registry(tt.fields)
+                       if err := r.DoRegister(tt.args.root, tt.args.node); 
(err != nil) != tt.wantErr {
+                               t.Errorf("DoRegister() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
        }
-
-       // listener.Close()
-       time.Sleep(1e9)
-       reg.Destroy()
-
-       assert.Equal(t, false, reg.IsAvailable())
 }
 
-func (suite *RegistryTestSuite) TestProviderDestroy() {
-       t := suite.T()
-       reg := initRegistry(t)
-       url, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParamsValue(constant.ClusterKey, "mock"), 
common.WithMethods([]string{"GetUser", "AddUser"}))
-       err := reg.Register(url)
-       assert.NoError(t, err)
-
-       // listener.Close()
-       time.Sleep(1e9)
-       reg.Destroy()
-       assert.Equal(t, false, reg.IsAvailable())
+func Test_etcdV3Registry_DoUnregister(t *testing.T) {
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name:    "test",
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       r := newEtcdV3Registry(tt.fields)
+                       if err := r.DoUnregister(tt.args.root, tt.args.node); 
(err != nil) != tt.wantErr {
+                               t.Errorf("DoUnregister() error = %v, wantErr 
%v", err, tt.wantErr)
+                       }
+               })
+       }
 }
diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go
index 1e467b78c..1012b8487 100644
--- a/registry/nacos/registry_test.go
+++ b/registry/nacos/registry_test.go
@@ -18,36 +18,203 @@
 package nacos
 
 import (
-       "encoding/json"
-       "net/http"
        "net/url"
+       "reflect"
        "strconv"
        "testing"
-       "time"
 )
 
 import (
-       "github.com/nacos-group/nacos-sdk-go/vo"
+       nacosClient "github.com/dubbogo/gost/database/kv/nacos"
+
+       "github.com/golang/mock/gomock"
 
-       "github.com/stretchr/testify/assert"
+       "github.com/nacos-group/nacos-sdk-go/model"
+       "github.com/nacos-group/nacos-sdk-go/vo"
 )
 
 import (
        "dubbo.apache.org/dubbo-go/v3/common"
        "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/registry"
 )
 
-func TestNacosRegistry_Register(t *testing.T) {
-       //t.Skip()
-       if !checkNacosServerAlive() {
-               return
+// MockINamingClient is a mock of INamingClient interface
+type MockINamingClient struct {
+       ctrl     *gomock.Controller
+       recorder *MockINamingClientMockRecorder
+}
+
+// MockINamingClientMockRecorder is the mock recorder for MockINamingClient
+type MockINamingClientMockRecorder struct {
+       mock *MockINamingClient
+}
+
+// NewMockINamingClient creates a new mock instance
+func NewMockINamingClient(ctrl *gomock.Controller) *MockINamingClient {
+       mock := &MockINamingClient{ctrl: ctrl}
+       mock.recorder = &MockINamingClientMockRecorder{mock}
+       return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockINamingClient) EXPECT() *MockINamingClientMockRecorder {
+       return m.recorder
+}
+
+// RegisterInstance mocks base method
+func (m *MockINamingClient) RegisterInstance(param vo.RegisterInstanceParam) 
(bool, error) {
+       ret := m.ctrl.Call(m, "RegisterInstance", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// RegisterInstance indicates an expected call of RegisterInstance
+func (mr *MockINamingClientMockRecorder) RegisterInstance(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"RegisterInstance", reflect.TypeOf((*MockINamingClient)(nil).RegisterInstance), 
param)
+}
+
+// DeregisterInstance mocks base method
+func (m *MockINamingClient) DeregisterInstance(param 
vo.DeregisterInstanceParam) (bool, error) {
+       ret := m.ctrl.Call(m, "DeregisterInstance", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// DeregisterInstance indicates an expected call of DeregisterInstance
+func (mr *MockINamingClientMockRecorder) DeregisterInstance(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"DeregisterInstance", 
reflect.TypeOf((*MockINamingClient)(nil).DeregisterInstance), param)
+}
+
+// UpdateInstance mocks base method
+func (m *MockINamingClient) UpdateInstance(param vo.UpdateInstanceParam) 
(bool, error) {
+       ret := m.ctrl.Call(m, "UpdateInstance", param)
+       ret0, _ := ret[0].(bool)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// UpdateInstance indicates an expected call of UpdateInstance
+func (mr *MockINamingClientMockRecorder) UpdateInstance(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstance", 
reflect.TypeOf((*MockINamingClient)(nil).UpdateInstance), param)
+}
+
+// GetService mocks base method
+func (m *MockINamingClient) GetService(param vo.GetServiceParam) 
(model.Service, error) {
+       ret := m.ctrl.Call(m, "GetService", param)
+       ret0, _ := ret[0].(model.Service)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// GetService indicates an expected call of GetService
+func (mr *MockINamingClientMockRecorder) GetService(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", 
reflect.TypeOf((*MockINamingClient)(nil).GetService), param)
+}
+
+// SelectAllInstances mocks base method
+func (m *MockINamingClient) SelectAllInstances(param 
vo.SelectAllInstancesParam) ([]model.Instance, error) {
+       ret := m.ctrl.Call(m, "SelectAllInstances", param)
+       ret0, _ := ret[0].([]model.Instance)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// SelectAllInstances indicates an expected call of SelectAllInstances
+func (mr *MockINamingClientMockRecorder) SelectAllInstances(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"SelectAllInstances", 
reflect.TypeOf((*MockINamingClient)(nil).SelectAllInstances), param)
+}
+
+// SelectInstances mocks base method
+func (m *MockINamingClient) SelectInstances(param vo.SelectInstancesParam) 
([]model.Instance, error) {
+       ret := m.ctrl.Call(m, "SelectInstances", param)
+       ret0, _ := ret[0].([]model.Instance)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// SelectInstances indicates an expected call of SelectInstances
+func (mr *MockINamingClientMockRecorder) SelectInstances(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"SelectInstances", reflect.TypeOf((*MockINamingClient)(nil).SelectInstances), 
param)
+}
+
+// SelectOneHealthyInstance mocks base method
+func (m *MockINamingClient) SelectOneHealthyInstance(param 
vo.SelectOneHealthInstanceParam) (*model.Instance, error) {
+       ret := m.ctrl.Call(m, "SelectOneHealthyInstance", param)
+       ret0, _ := ret[0].(*model.Instance)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// SelectOneHealthyInstance indicates an expected call of 
SelectOneHealthyInstance
+func (mr *MockINamingClientMockRecorder) SelectOneHealthyInstance(param 
interface{}) *gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"SelectOneHealthyInstance", 
reflect.TypeOf((*MockINamingClient)(nil).SelectOneHealthyInstance), param)
+}
+
+// Subscribe mocks base method
+func (m *MockINamingClient) Subscribe(param *vo.SubscribeParam) error {
+       ret := m.ctrl.Call(m, "Subscribe", param)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// Subscribe indicates an expected call of Subscribe
+func (mr *MockINamingClientMockRecorder) Subscribe(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", 
reflect.TypeOf((*MockINamingClient)(nil).Subscribe), param)
+}
+
+// Unsubscribe mocks base method
+func (m *MockINamingClient) Unsubscribe(param *vo.SubscribeParam) error {
+       ret := m.ctrl.Call(m, "Unsubscribe", param)
+       ret0, _ := ret[0].(error)
+       return ret0
+}
+
+// Unsubscribe indicates an expected call of Unsubscribe
+func (mr *MockINamingClientMockRecorder) Unsubscribe(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", 
reflect.TypeOf((*MockINamingClient)(nil).Unsubscribe), param)
+}
+
+// GetAllServicesInfo mocks base method
+func (m *MockINamingClient) GetAllServicesInfo(param 
vo.GetAllServiceInfoParam) (model.ServiceList, error) {
+       ret := m.ctrl.Call(m, "GetAllServicesInfo", param)
+       ret0, _ := ret[0].(model.ServiceList)
+       ret1, _ := ret[1].(error)
+       return ret0, ret1
+}
+
+// GetAllServicesInfo indicates an expected call of GetAllServicesInfo
+func (mr *MockINamingClientMockRecorder) GetAllServicesInfo(param interface{}) 
*gomock.Call {
+       return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, 
"GetAllServicesInfo", 
reflect.TypeOf((*MockINamingClient)(nil).GetAllServicesInfo), param)
+}
+
+type fields struct {
+       URL          *common.URL
+       namingClient *nacosClient.NacosNamingClient
+       registryUrls []*common.URL
+}
+type args struct {
+       url            *common.URL
+       notifyListener registry.NotifyListener
+}
+
+func newNacosRegistryForTest(f fields) *nacosRegistry {
+       return &nacosRegistry{
+               URL:          f.URL,
+               namingClient: f.namingClient,
+               registryUrls: f.registryUrls,
        }
+}
+
+func Test_nacosRegistry_Register(t *testing.T) {
        params := url.Values{}
        params.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
        params.Set(constant.NacosNotLoadLocalCache, "true")
        params.Set(constant.ClientNameKey, "nacos-client")
 
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(params))
+       regURL, _ := common.NewURL("registry://test.nacos.io:80", 
common.WithParams(params))
 
        urlMap := url.Values{}
        urlMap.Set(constant.GroupKey, "guangzhou-idc")
@@ -56,185 +223,142 @@ func TestNacosRegistry_Register(t *testing.T) {
        urlMap.Set(constant.VersionKey, "1.0.0")
        urlMap.Set(constant.ClusterKey, "mock")
        urlMap.Set(constant.ClientNameKey, "nacos-client")
-       testUrl, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+       testURL, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
-       reg, err := newNacosRegistry(regurl)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("new nacos registry error:%s \n", err.Error())
-               return
+       ctrl := gomock.NewController(t)
+       mnc := NewMockINamingClient(ctrl)
+       mnc.EXPECT().RegisterInstance(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosNamingClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               URL:          regURL,
+                               namingClient: nc,
+                               registryUrls: nil,
+                       },
+                       args: args{
+                               url: testURL,
+                       },
+                       wantErr: false,
+               },
        }
-       err = reg.Register(testUrl)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("register error:%s \n", err.Error())
-               return
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       nr := newNacosRegistryForTest(tt.fields)
+                       if err := nr.Register(tt.args.url); (err != nil) != 
tt.wantErr {
+                               t.Errorf("Register() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
        }
-       time.Sleep(5 * time.Second)
-       nacosReg := reg.(*nacosRegistry)
-       service, _ := 
nacosReg.namingClient.Client().GetService(vo.GetServiceParam{ServiceName: 
"providers:com.ikurento.user.UserProvider:1.0.0:guangzhou-idc"})
-       data, _ := json.Marshal(service)
-       t.Logf(string(data))
-       assert.Equal(t, 1, len(service.Hosts))
 }
 
-func TestNacosRegistry_Subscribe(t *testing.T) {
-       if !checkNacosServerAlive() {
-               return
-       }
-       regurlMap := url.Values{}
-       regurlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
-       regurlMap.Set(constant.NacosNotLoadLocalCache, "true")
-       regurlMap.Set(constant.ClientNameKey, "nacos-client")
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
+func Test_nacosRegistry_UnRegister(t *testing.T) {
+       params := url.Values{}
+       params.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
+       params.Set(constant.NacosNotLoadLocalCache, "true")
+       params.Set(constant.ClientNameKey, "nacos-client")
+
+       regURL, _ := common.NewURL("registry://test.nacos.io:80", 
common.WithParams(params))
 
        urlMap := url.Values{}
        urlMap.Set(constant.GroupKey, "guangzhou-idc")
        urlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
-       urlMap.Set(constant.InterfaceKey, "com.dubbo.user.UserProvider")
+       urlMap.Set(constant.InterfaceKey, "com.ikurento.user.UserProvider")
        urlMap.Set(constant.VersionKey, "1.0.0")
        urlMap.Set(constant.ClusterKey, "mock")
-       urlMap.Set(constant.NacosPathKey, "")
        urlMap.Set(constant.ClientNameKey, "nacos-client")
-       testUrl, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.dubbo.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
-
-       reg, _ := newNacosRegistry(regurl)
-       err := reg.Register(testUrl)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("new nacos registry error:%s \n", err.Error())
-               return
-       }
+       testURL, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
-       regurl.SetParam(constant.RegistryRoleKey, strconv.Itoa(common.CONSUMER))
-       reg2, _ := newNacosRegistry(regurl)
-       listener, err := reg2.(*nacosRegistry).subscribe(testUrl)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("subscribe error:%s \n", err.Error())
-               return
+       ctrl := gomock.NewController(t)
+       mnc := NewMockINamingClient(ctrl)
+       mnc.EXPECT().DeregisterInstance(gomock.Any()).Return(true, nil)
+       nc := &nacosClient.NacosNamingClient{}
+       nc.SetClient(mnc)
+
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               URL:          regURL,
+                               namingClient: nc,
+                               registryUrls: nil,
+                       },
+                       args: args{
+                               url: testURL,
+                       },
+                       wantErr: false,
+               },
        }
-       serviceEvent, _ := listener.Next()
-       assert.NoError(t, err)
-       if err != nil {
-               t.Errorf("listener error:%s \n", err.Error())
-               return
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       nr := newNacosRegistryForTest(tt.fields)
+                       if err := nr.UnRegister(tt.args.url); (err != nil) != 
tt.wantErr {
+                               t.Errorf("UnRegister() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
        }
-       t.Logf("serviceEvent:%+v \n", serviceEvent)
-       assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent.String())
 }
 
-func TestNacosRegistry_Subscribe_del(t *testing.T) {
-       if !checkNacosServerAlive() {
-               return
-       }
-       regurlMap := url.Values{}
-       regurlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
-       regurlMap.Set(constant.NacosNotLoadLocalCache, "true")
-       regurlMap.Set(constant.ClientNameKey, "nacos-client")
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
+func Test_nacosRegistry_Subscribe(t *testing.T) {
+       params := url.Values{}
+       params.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
+       params.Set(constant.NacosNotLoadLocalCache, "true")
+       params.Set(constant.ClientNameKey, "nacos-client")
+
+       regURL, _ := common.NewURL("registry://test.nacos.io:80", 
common.WithParams(params))
 
        urlMap := url.Values{}
        urlMap.Set(constant.GroupKey, "guangzhou-idc")
        urlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
        urlMap.Set(constant.InterfaceKey, "com.ikurento.user.UserProvider")
-       urlMap.Set(constant.VersionKey, "2.0.0")
+       urlMap.Set(constant.VersionKey, "1.0.0")
        urlMap.Set(constant.ClusterKey, "mock")
-       urlMap.Set(constant.NacosPathKey, "")
        urlMap.Set(constant.ClientNameKey, "nacos-client")
-       url1, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
-       url2, _ := 
common.NewURL("dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
-
-       reg, _ := newNacosRegistry(regurl)
-       err := reg.Register(url1)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("register1 error:%s \n", err.Error())
-               return
-       }
-       err = reg.Register(url2)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("register2 error:%s \n", err.Error())
-               return
-       }
-
-       regurl.SetParam(constant.RegistryRoleKey, strconv.Itoa(common.CONSUMER))
-       reg2, _ := newNacosRegistry(regurl)
-       listener, err := reg2.(*nacosRegistry).subscribe(url1)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("subscribe error:%s \n", err.Error())
-               return
-       }
-
-       serviceEvent1, _ := listener.Next()
-       assert.NoError(t, err)
-       if err != nil {
-               t.Errorf("listener1 error:%s \n", err.Error())
-               return
-       }
-       t.Logf("serviceEvent1:%+v \n", serviceEvent1)
-       assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent1.String())
-
-       serviceEvent2, _ := listener.Next()
-       assert.NoError(t, err)
-       if err != nil {
-               t.Errorf("listener2 error:%s \n", err.Error())
-               return
-       }
-       t.Logf("serviceEvent2:%+v \n", serviceEvent2)
-       assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent2.String())
-
-       nacosReg := reg.(*nacosRegistry)
-       // deregister instance to mock instance offline
-       _, err = 
nacosReg.namingClient.Client().DeregisterInstance(vo.DeregisterInstanceParam{
-               Ip: "127.0.0.2", Port: 20000,
-               ServiceName: 
"providers:com.ikurento.user.UserProvider:2.0.0:guangzhou-idc",
-       })
-       assert.NoError(t, err)
-
-       serviceEvent3, _ := listener.Next()
-       assert.NoError(t, err)
-       if err != nil {
-               return
-       }
-       t.Logf("serviceEvent3:%+v \n", serviceEvent3)
-       assert.Regexp(t, ".*ServiceEvent{Action{delete}.*", 
serviceEvent3.String())
-}
+       testURL, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
-func TestNacosListener_Close(t *testing.T) {
-       regurlMap := url.Values{}
-       regurlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
-       regurlMap.Set(constant.NacosNotLoadLocalCache, "true")
-       regurlMap.Set(constant.ClientNameKey, "nacos-client")
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
+       ctrl := gomock.NewController(t)
+       mnc := NewMockINamingClient(ctrl)
+       nc := &nacosClient.NacosNamingClient{}
+       nc.SetClient(mnc)
 
-       urlMap := url.Values{}
-       urlMap.Set(constant.RegistryGroupKey, "guangzhou-idc")
-       urlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.PROVIDER))
-       urlMap.Set(constant.InterfaceKey, "com.ikurento.user.UserProvider2")
-       urlMap.Set(constant.VersionKey, "1.0.0")
-       urlMap.Set(constant.ClusterKey, "mock")
-       urlMap.Set(constant.NacosPathKey, "")
-       urlMap.Set(constant.ClientNameKey, "nacos-client")
-       url1, _ := 
common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", 
common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
-       reg, _ := newNacosRegistry(regurl)
-       listener, err := reg.(*nacosRegistry).subscribe(url1)
-       assert.Nil(t, err)
-       if err != nil {
-               t.Errorf("subscribe error:%s \n", err.Error())
-               return
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       fields: fields{
+                               URL:          regURL,
+                               namingClient: nc,
+                               registryUrls: nil,
+                       },
+                       args: args{
+                               url: testURL,
+                       },
+                       wantErr: false,
+               },
        }
-       listener.Close()
-       _, err = listener.Next()
-       assert.NotNil(t, err)
-}
-
-func checkNacosServerAlive() bool {
-       c := http.Client{Timeout: time.Second}
-       if _, err := c.Get("http://console.nacos.io/nacos/";); err != nil {
-               return false
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       nr := newNacosRegistryForTest(tt.fields)
+                       if err := nr.Subscribe(tt.args.url, 
tt.args.notifyListener); (err != nil) != tt.wantErr {
+                               t.Errorf("Subscribe() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
        }
-       return true
 }
diff --git a/remoting/etcdv3/listener_test.go b/remoting/etcdv3/listener_test.go
index b6b7397c4..f8b4263ea 100644
--- a/remoting/etcdv3/listener_test.go
+++ b/remoting/etcdv3/listener_test.go
@@ -18,129 +18,37 @@
 package etcdv3
 
 import (
-       "net/url"
-       "os"
+       "reflect"
        "testing"
-       "time"
 )
 
 import (
        gxetcd "github.com/dubbogo/gost/database/kv/etcd/v3"
-
-       "github.com/stretchr/testify/assert"
-
-       "go.etcd.io/etcd/server/v3/embed"
-)
-
-import (
-       "dubbo.apache.org/dubbo-go/v3/remoting"
 )
 
-const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-remote.etcd"
-
-var changedData = `
-       dubbo.consumer.request_timeout=3s
-       dubbo.consumer.connect_timeout=5s
-       dubbo.application.organization=ikurento.com
-       dubbo.application.name=BDTService
-       dubbo.application.module=dubbogo user-info server
-       dubbo.application.version=0.0.1
-       dubbo.application.owner=ZX
-       dubbo.application.environment=dev
-       dubbo.registries.hangzhouzk.protocol=zookeeper
-       dubbo.registries.hangzhouzk.timeout=3s
-       dubbo.registries.hangzhouzk.address=127.0.0.1:2181
-       dubbo.registries.shanghaizk.protocol=zookeeper
-       dubbo.registries.shanghaizk.timeout=3s
-       dubbo.registries.shanghaizk.address=127.0.0.1:2182
-       dubbo.service.com.ikurento.user.UserProvider.protocol=dubbo
-       
dubbo.service.com.ikurento.user.UserProvider.interface=com.ikurento.user.UserProvider
-       dubbo.service.com.ikurento.user.UserProvider.loadbalance=random
-       dubbo.service.com.ikurento.user.UserProvider.warmup=100
-       dubbo.service.com.ikurento.user.UserProvider.cluster=failover
-`
-
-var etcd *embed.Etcd
-
-func SetUpEtcdServer(t *testing.T) {
-       var err error
-       DefaultListenPeerURLs := "http://localhost:2382";
-       DefaultListenClientURLs := "http://localhost:2381";
-       lpurl, _ := url.Parse(DefaultListenPeerURLs)
-       lcurl, _ := url.Parse(DefaultListenClientURLs)
-       cfg := embed.NewConfig()
-       cfg.LPUrls = []url.URL{*lpurl}
-       cfg.LCUrls = []url.URL{*lcurl}
-       cfg.Dir = defaultEtcdV3WorkDir
-       etcd, err = embed.StartEtcd(cfg)
-       if err != nil {
-               t.Fatal(err)
-       }
-       select {
-       case <-etcd.Server.ReadyNotify():
-               t.Log("Server is ready!")
-       case <-time.After(60 * time.Second):
-               etcd.Server.Stop() // trigger a shutdown
-               t.Logf("Server took too long to start!")
-       }
-}
-
-func ClearEtcdServer(t *testing.T) {
-       etcd.Close()
-       if err := os.RemoveAll(defaultEtcdV3WorkDir); err != nil {
-               t.Fail()
-       }
+type args struct {
+       client *gxetcd.Client
 }
 
-func TestListener(t *testing.T) {
-
+func TestNewEventListener(t *testing.T) {
        tests := []struct {
-               input struct {
-                       k string
-                       v string
-               }
+               name string
+               args args
+               want *EventListener
        }{
-               {input: struct {
-                       k string
-                       v string
-               }{k: "/dubbo/", v: changedData}},
-       }
-       SetUpEtcdServer(t)
-       c, err := gxetcd.NewClient("test", []string{"localhost:2381"}, 
time.Second, 1)
-       assert.NoError(t, err)
-
-       listener := NewEventListener(c)
-       dataListener := &mockDataListener{client: c, changedData: changedData, 
rc: make(chan remoting.Event)}
-       listener.ListenServiceEvent("/dubbo/", dataListener)
-
-       // NOTICE:  direct listen will lose create msg
-       time.Sleep(time.Second)
-       for _, tc := range tests {
-
-               k := tc.input.k
-               v := tc.input.v
-               if err := c.Update(k, v); err != nil {
-                       t.Fatal(err)
-               }
-
+               {
+                       name: "test",
+                       args: args{
+                               client: nil,
+                       },
+                       want: NewEventListener(nil),
+               },
        }
-       msg := <-dataListener.rc
-       assert.Equal(t, changedData, msg.Content)
-       ClearEtcdServer(t)
-}
-
-type mockDataListener struct {
-       eventList   []remoting.Event
-       client      *gxetcd.Client
-       changedData string
-
-       rc chan remoting.Event
-}
-
-func (m *mockDataListener) DataChange(eventType remoting.Event) bool {
-       m.eventList = append(m.eventList, eventType)
-       if eventType.Content == m.changedData {
-               m.rc <- eventType
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := NewEventListener(tt.args.client); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewEventListener() = %v, want %v", 
got, tt.want)
+                       }
+               })
        }
-       return true
 }
diff --git a/remoting/nacos/builder_test.go b/remoting/nacos/builder_test.go
index 2fdfa10e1..267d25f2b 100644
--- a/remoting/nacos/builder_test.go
+++ b/remoting/nacos/builder_test.go
@@ -19,11 +19,17 @@ package nacos
 
 import (
        "net/url"
+       "reflect"
        "testing"
-       "time"
 )
 
 import (
+       "github.com/agiledragon/gomonkey"
+
+       nacosClient "github.com/dubbogo/gost/database/kv/nacos"
+
+       nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
+
        "github.com/stretchr/testify/assert"
 )
 
@@ -33,125 +39,169 @@ import (
        "dubbo.apache.org/dubbo-go/v3/config"
 )
 
-func TestNewNacosClient(t *testing.T) {
-       t.Run("AddressIsNil", func(t *testing.T) {
-               rc := &config.RemoteConfig{}
-               rc.Protocol = "nacos"
-               rc.Username = "nacos"
-               client, err := NewNacosClient(rc)
-
-               // address is nil
-               assert.Nil(t, client)
-               assert.NotNil(t, err)
-       })
-
-       t.Run("InvalidAddress", func(t *testing.T) {
-               rc := &config.RemoteConfig{}
-               rc.Address = "console.nacos.io:80:123"
-               client, err := NewNacosClient(rc)
-               // invalid address
-               assert.Nil(t, client)
-               assert.NotNil(t, err)
-       })
+func getRegURL() *common.URL {
+       regURLMap := url.Values{}
+       regURLMap.Set(constant.NacosNotLoadLocalCache, "true")
+       regURLMap.Set(constant.NacosNamespaceID, "nacos")
+       regURLMap.Set(constant.TimeoutKey, "5s")
+       regURLMap.Set(constant.ClientNameKey, "nacos-client")
+       regURL, _ := common.NewURL("registry://test.nacos.io:80", 
common.WithParams(regURLMap))
 
-       t.Run("Normal", func(t *testing.T) {
-               rc := &config.RemoteConfig{}
-               rc.Address = "console.nacos.io:80"
-               rc.Protocol = "nacos"
-               rc.Timeout = "10s"
-               client, err := NewNacosClient(rc)
-               assert.NotNil(t, client)
-               assert.Nil(t, err)
-       })
-
-       t.Run("NormalHasContextPath", func(t *testing.T) {
-               rc := &config.RemoteConfig{}
-               rc.Address = "console.nacos.io:80/nacos"
-               rc.Protocol = "nacos"
-               client, err := NewNacosClient(rc)
-               assert.NotNil(t, client)
-               assert.Nil(t, err)
-       })
+       return regURL
 }
 
-func TestGetNacosConfig(t *testing.T) {
-       regurl := getRegUrl()
-       sc, cc, err := GetNacosConfig(regurl)
-
-       assert.Nil(t, err)
-       assert.NotNil(t, sc)
-       assert.NotNil(t, cc)
-       assert.Equal(t, cc.TimeoutMs, uint64(5000))
-}
-
-func TestNewNacosConfigClient(t *testing.T) {
-
-       regurl := getRegUrl()
-       client, err := NewNacosConfigClientByUrl(regurl)
-
-       assert.Nil(t, err)
-       assert.NotNil(t, client)
+type args struct {
+       url *common.URL
+       rc  *config.RemoteConfig
 }
 
 func TestNewNacosClientByURL(t *testing.T) {
-       regurl := getRegUrl()
-       client, err := NewNacosClientByURL(regurl)
-
-       assert.Nil(t, err)
-       assert.NotNil(t, client)
-}
-
-func TestTimeoutConfig(t *testing.T) {
-       regurlMap := url.Values{}
-       regurlMap.Set(constant.NacosNotLoadLocalCache, "true")
-       // regurlMap.Set(constant.NacosUsername, "nacos")
-       // regurlMap.Set(constant.NacosPassword, "nacos")
-       regurlMap.Set(constant.NacosNamespaceID, "nacos")
-
-       t.Run("default timeout", func(t *testing.T) {
-               newURL, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
-
-               _, cc, err := GetNacosConfig(newURL)
-               assert.Nil(t, err)
-
-               assert.Equal(t, cc.TimeoutMs, 
uint64(int32(5*time.Second/time.Millisecond)))
-       })
-
-       t.Run("right timeout", func(t *testing.T) {
-
-               regurlMap.Set(constant.NacosTimeout, "7s")
-
-               newURL, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
-
-               _, cc, err := GetNacosConfig(newURL)
-               assert.Nil(t, err)
-
-               assert.Equal(t, cc.TimeoutMs, 
uint64(int32(7*time.Second/time.Millisecond)))
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyFunc(nacosClient.NewNacosNamingClient, func(name 
string, share bool, sc []nacosConstant.ServerConfig,
+               cc nacosConstant.ClientConfig) (*nacosClient.NacosNamingClient, 
error) {
+               return &nacosClient.NacosNamingClient{}, nil
        })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               args    args
+               want    *nacosClient.NacosNamingClient
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       args: args{
+                               url: getRegURL(),
+                       },
+                       want:    &nacosClient.NacosNamingClient{},
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := NewNacosClientByURL(tt.args.url)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("NewNacosClientByURL() error = %v, 
wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewNacosClientByURL() got = %v, want 
%v", got, tt.want)
+                       }
+               })
+       }
+}
 
-       t.Run("invalid timeout", func(t *testing.T) {
-               regurlMap.Set(constant.TimeoutKey, "5ab")
-
-               newURL, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
-               _, cc, err := GetNacosConfig(newURL)
-               assert.Nil(t, err)
-
-               assert.NotEqual(t, cc.TimeoutMs, 
uint64(int32(3*time.Second/time.Millisecond)))
+func TestNewNacosClient(t *testing.T) {
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyFunc(nacosClient.NewNacosNamingClient, func(name 
string, share bool, sc []nacosConstant.ServerConfig,
+               cc nacosConstant.ClientConfig) (*nacosClient.NacosNamingClient, 
error) {
+               return &nacosClient.NacosNamingClient{}, nil
        })
-
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               args    args
+               want    *nacosClient.NacosNamingClient
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       args: args{
+                               rc: &config.RemoteConfig{
+                                       Address:  "test.nacos.io:80/nacos",
+                                       Protocol: "nacos",
+                                       Timeout:  "10s",
+                                       Username: "naocs",
+                                       Password: "nacos",
+                               },
+                       },
+                       want:    &nacosClient.NacosNamingClient{},
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := NewNacosClient(tt.args.rc)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("NewNacosClient() error = %v, wantErr 
%v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewNacosClient() got = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
 }
 
-func getRegUrl() *common.URL {
-
-       regurlMap := url.Values{}
-       regurlMap.Set(constant.NacosNotLoadLocalCache, "true")
-       // regurlMap.Set(constant.NacosUsername, "nacos")
-       // regurlMap.Set(constant.NacosPassword, "nacos")
-       regurlMap.Set(constant.NacosNamespaceID, "nacos")
-       regurlMap.Set(constant.TimeoutKey, "5s")
-       regurlMap.Set(constant.ClientNameKey, "nacos-client")
-
-       regurl, _ := common.NewURL("registry://console.nacos.io:80", 
common.WithParams(regurlMap))
+func TestGetNacosConfig(t *testing.T) {
+       tests := []struct {
+               name    string
+               args    args
+               want    []nacosConstant.ServerConfig
+               want1   nacosConstant.ClientConfig
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       args: args{
+                               url: getRegURL(),
+                       },
+                       want: []nacosConstant.ServerConfig{
+                               {
+                                       IpAddr: "test.nacos.io",
+                                       Port:   80,
+                               },
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, got1, err := GetNacosConfig(tt.args.url)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("GetNacosConfig() error = %v, wantErr 
%v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("GetNacosConfig() got = %v, want %v", 
got, tt.want)
+                       }
+                       assert.NotNil(t, got1)
+               })
+       }
+}
 
-       return regurl
+func TestNewNacosConfigClientByUrl(t *testing.T) {
+       patches := gomonkey.NewPatches()
+       patches = patches.ApplyFunc(nacosClient.NewNacosNamingClient, func(name 
string, share bool, sc []nacosConstant.ServerConfig,
+               cc nacosConstant.ClientConfig) (*nacosClient.NacosNamingClient, 
error) {
+               return &nacosClient.NacosNamingClient{}, nil
+       })
+       defer patches.Reset()
+
+       tests := []struct {
+               name    string
+               args    args
+               want    *nacosClient.NacosConfigClient
+               wantErr bool
+       }{
+               {
+                       name: "test",
+                       args: args{
+                               url: getRegURL(),
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := NewNacosConfigClientByUrl(tt.args.url)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("NewNacosConfigClientByUrl() error = 
%v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       assert.NotNil(t, got)
+               })
+       }
 }

Reply via email to