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

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


The following commit(s) were added to refs/heads/dev-3.3 by this push:
     new cefda66da add metadata unit test (#2665)
cefda66da is described below

commit cefda66da7e0f97547a7142d8499e1b822782e18
Author: foghost <foghost...@gmail.com>
AuthorDate: Mon Apr 29 14:38:33 2024 +0800

    add metadata unit test (#2665)
---
 global/application_config.go                       |  19 +-
 metadata/client.go                                 |  41 +--
 metadata/client_test.go                            | 280 +++++++++++++++
 metadata/info/metadata_info.go                     |  29 +-
 metadata/info/metadata_info_test.go                |  94 ++++-
 metadata/mapping/metadata/service_name_mapping.go  |   2 -
 .../mapping/metadata/service_name_mapping_test.go  | 182 ++++++++++
 metadata/metadata.go                               |  18 +-
 metadata/metadata_service.go                       |  34 +-
 metadata/metadata_service_test.go                  | 377 +++++++++++++++++++++
 metadata/metadata_test.go                          | 141 ++++++++
 metadata/options.go                                |  37 +-
 metadata/report/report_factory.go                  |   2 -
 metadata/report_instance.go                        |  33 +-
 metadata/report_instance_test.go                   | 264 +++++++++++++++
 options.go                                         |  13 +-
 .../service_instance_host_port_customizer.go       |   2 +-
 17 files changed, 1443 insertions(+), 125 deletions(-)

diff --git a/global/application_config.go b/global/application_config.go
index dbb35a9ac..977899753 100644
--- a/global/application_config.go
+++ b/global/application_config.go
@@ -44,14 +44,15 @@ func (c *ApplicationConfig) Clone() *ApplicationConfig {
        }
 
        return &ApplicationConfig{
-               Organization: c.Organization,
-               Name:         c.Name,
-               Module:       c.Module,
-               Group:        c.Group,
-               Version:      c.Version,
-               Owner:        c.Owner,
-               Environment:  c.Environment,
-               MetadataType: c.MetadataType,
-               Tag:          c.Tag,
+               Organization:        c.Organization,
+               Name:                c.Name,
+               Module:              c.Module,
+               Group:               c.Group,
+               Version:             c.Version,
+               Owner:               c.Owner,
+               Environment:         c.Environment,
+               MetadataType:        c.MetadataType,
+               Tag:                 c.Tag,
+               MetadataServicePort: c.MetadataServicePort,
        }
 }
diff --git a/metadata/client.go b/metadata/client.go
index 6b9120e1e..a3e7dd425 100644
--- a/metadata/client.go
+++ b/metadata/client.go
@@ -20,7 +20,6 @@ package metadata
 import (
        "context"
        "encoding/json"
-       "time"
 )
 
 import (
@@ -37,47 +36,33 @@ import (
        "dubbo.apache.org/dubbo-go/v3/registry"
 )
 
-const metadataProxyDefaultTimeout = 5000
+const defaultTimeout = "5s" // s
 
-// GetMetadataFromMetadataReport test depends on dubbo protocol, if dubbo not 
dependent on config package, can move to metadata dir
 func GetMetadataFromMetadataReport(revision string, instance 
registry.ServiceInstance) (*info.MetadataInfo, error) {
        report := GetMetadataReport()
+       if report == nil {
+               return nil, perrors.New("no metadata report instance 
found,please check ")
+       }
        return report.GetAppMetadata(instance.GetServiceName(), revision)
 }
 
 func GetMetadataFromRpc(revision string, instance registry.ServiceInstance) 
(*info.MetadataInfo, error) {
-       service, destroy, err := createRpcClient(instance)
-       if err != nil {
-               return nil, err
-       }
-       ctx, cancel := context.WithTimeout(context.Background(), 
time.Duration(metadataProxyDefaultTimeout))
-       defer cancel()
-       defer destroy()
-       return service.GetMetadataInfo(ctx, revision)
-}
-
-type remoteMetadataService struct {
-       GetMetadataInfo func(context context.Context, revision string) 
(*info.MetadataInfo, error) `dubbo:"getMetadataInfo"`
-}
-
-func createRpcClient(instance registry.ServiceInstance) 
(*remoteMetadataService, func(), error) {
        params := 
getMetadataServiceUrlParams(instance.GetMetadata()[constant.MetadataServiceURLParamsPropertyName])
        url := buildMetadataServiceURL(instance.GetServiceName(), 
instance.GetHost(), params)
-       return createRpcClientByUrl(url)
-}
-
-func createRpcClientByUrl(url *common.URL) (*remoteMetadataService, func(), 
error) {
+       url.SetParam(constant.TimeoutKey, defaultTimeout)
        rpcService := &remoteMetadataService{}
        invoker := extension.GetProtocol(constant.Dubbo).Refer(url)
        if invoker == nil {
-               return nil, nil, perrors.New("create invoker error, can not 
connect to the metadata report server: " + url.Ip + ":" + url.Port)
+               return nil, perrors.New("create invoker error, can not connect 
to the metadata report server: " + url.Ip + ":" + url.Port)
        }
        proxy := 
extension.GetProxyFactory(constant.DefaultKey).GetProxy(invoker, url)
        proxy.Implement(rpcService)
-       destroy := func() {
-               invoker.Destroy()
-       }
-       return rpcService, destroy, nil
+       defer invoker.Destroy()
+       return rpcService.GetMetadataInfo(context.TODO(), revision)
+}
+
+type remoteMetadataService struct {
+       GetMetadataInfo func(context context.Context, revision string) 
(*info.MetadataInfo, error) `dubbo:"getMetadataInfo"`
 }
 
 // buildMetadataServiceURL will use standard format to build the metadata 
service url.
@@ -108,7 +93,7 @@ func getMetadataServiceUrlParams(jsonStr string) 
map[string]string {
        if len(jsonStr) > 0 {
                err := json.Unmarshal([]byte(jsonStr), &res)
                if err != nil {
-                       logger.Errorf("could not parse the metadata service url 
parameters to map", err)
+                       logger.Errorf("could not parse the metadata service url 
parameters '%s' to map", jsonStr)
                }
        }
        return res
diff --git a/metadata/client_test.go b/metadata/client_test.go
new file mode 100644
index 000000000..aa9fe7691
--- /dev/null
+++ b/metadata/client_test.go
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+       "context"
+       "testing"
+)
+
+import (
+       "github.com/pkg/errors"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+)
+
+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/info"
+       "dubbo.apache.org/dubbo-go/v3/protocol"
+       _ "dubbo.apache.org/dubbo-go/v3/proxy/proxy_factory"
+       "dubbo.apache.org/dubbo-go/v3/registry"
+)
+
+var (
+       ins = &registry.DefaultServiceInstance{
+               ID: "1",
+               Metadata: map[string]string{
+                       constant.MetadataServiceURLParamsPropertyName: `{
+                               "application": "dubbo-go",
+                               "group": "BDTService",
+                               "port": "64658",
+                               "protocol": "dubbo",
+                               "version": "1.0.0"
+                       }`,
+               },
+               Host:        "dubbo.io",
+               ServiceName: "dubbo-app",
+       }
+       metadataInfo = &info.MetadataInfo{
+               App: "dubbo-app",
+       }
+)
+
+func TestGetMetadataFromMetadataReport(t *testing.T) {
+       t.Run("no report instance", func(t *testing.T) {
+               _, err := GetMetadataFromMetadataReport("1", ins)
+               assert.NotNil(t, err)
+       })
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       instances["default"] = mockReport
+       t.Run("normal", func(t *testing.T) {
+               mockReport.On("GetAppMetadata").Return(metadataInfo, nil).Once()
+               got, err := GetMetadataFromMetadataReport("1", ins)
+               assert.Nil(t, err)
+               assert.Equal(t, metadataInfo, got)
+       })
+       t.Run("error", func(t *testing.T) {
+               mockReport.On("GetAppMetadata").Return(metadataInfo, 
errors.New("mock error")).Once()
+               _, err := GetMetadataFromMetadataReport("1", ins)
+               assert.NotNil(t, err)
+       })
+}
+
+func TestGetMetadataFromRpc(t *testing.T) {
+       mockInvoker := new(mockInvoker)
+       defer mockInvoker.AssertExpectations(t)
+       mockProtocol := new(mockProtocol)
+       defer mockProtocol.AssertExpectations(t)
+       extension.SetProtocol("dubbo", func() protocol.Protocol {
+               return mockProtocol
+       })
+
+       result := &protocol.RPCResult{
+               Attrs: map[string]interface{}{},
+               Err:   nil,
+               Rest:  metadataInfo,
+       }
+       t.Run("normal", func(t *testing.T) {
+               mockProtocol.On("Refer").Return(mockInvoker).Once()
+               mockInvoker.On("Invoke").Return(result).Once()
+               mockInvoker.On("Destroy").Once()
+               metadata, err := GetMetadataFromRpc("111", ins)
+               assert.Nil(t, err)
+               assert.Equal(t, metadata, result.Rest)
+       })
+       t.Run("refer error", func(t *testing.T) {
+               mockProtocol.On("Refer").Return(nil).Once()
+               _, err := GetMetadataFromRpc("111", ins)
+               assert.NotNil(t, err)
+       })
+       t.Run("invoke timeout", func(t *testing.T) {
+               mockProtocol.On("Refer").Return(mockInvoker).Once()
+               mockInvoker.On("Invoke").Return(&protocol.RPCResult{
+                       Attrs: map[string]interface{}{},
+                       Err:   errors.New("timeout error"),
+                       Rest:  metadataInfo,
+               }).Once()
+               mockInvoker.On("Destroy").Once()
+               _, err := GetMetadataFromRpc("111", ins)
+               assert.NotNil(t, err)
+       })
+}
+
+func Test_buildMetadataServiceURL(t *testing.T) {
+       type args struct {
+               serviceName string
+               host        string
+               params      map[string]string
+       }
+       tests := []struct {
+               name string
+               args args
+               want *common.URL
+       }{
+               {
+                       name: "normal",
+                       args: args{
+                               serviceName: "dubbo-app",
+                               host:        "dubbo.io",
+                               params: map[string]string{
+                                       constant.ProtocolKey: "dubbo",
+                                       constant.PortKey:     "3000",
+                               },
+                       },
+                       want: common.NewURLWithOptions(
+                               common.WithIp("dubbo.io"),
+                               common.WithProtocol("dubbo"),
+                               common.WithPath(constant.MetadataServiceName),
+                               common.WithProtocol("dubbo"),
+                               common.WithPort("3000"),
+                               common.WithParams(map[string][]string{
+                                       constant.ProtocolKey: {"dubbo"},
+                                       constant.PortKey:     {"3000"},
+                               }),
+                               common.WithParamsValue(constant.GroupKey, 
"dubbo-app"),
+                               common.WithParamsValue(constant.InterfaceKey, 
constant.MetadataServiceName),
+                       ),
+               },
+               {
+                       name: "no protocol",
+                       args: args{
+                               serviceName: "dubbo-app",
+                               host:        "dubbo.io",
+                               params:      map[string]string{},
+                       },
+                       want: nil,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       assert.Equalf(t, tt.want, 
buildMetadataServiceURL(tt.args.serviceName, tt.args.host, tt.args.params), 
"buildMetadataServiceURL(%v, %v, %v)", tt.args.serviceName, tt.args.host, 
tt.args.params)
+               })
+       }
+}
+
+func Test_getMetadataServiceUrlParams(t *testing.T) {
+       type args struct {
+               jsonStr string
+       }
+       tests := []struct {
+               name string
+               args args
+               want map[string]string
+       }{
+               {
+                       name: "normal",
+                       args: args{
+                               jsonStr: `{
+                                       "application": "BDTService",
+                                       "group": "BDTService",
+                                       "port": "64658",
+                                       "protocol": "dubbo",
+                                       "release": "dubbo-golang-3.0.0",
+                                       "timestamp": "1713432877",
+                                       "version": "1.0.0"
+                               }`,
+                       },
+                       want: map[string]string{
+                               "application": "BDTService",
+                               "group":       "BDTService",
+                               "port":        "64658",
+                               "protocol":    "dubbo",
+                               "release":     "dubbo-golang-3.0.0",
+                               "timestamp":   "1713432877",
+                               "version":     "1.0.0",
+                       },
+               },
+               {
+                       name: "wrong format",
+                       args: args{
+                               jsonStr: "xxx",
+                       },
+                       want: map[string]string{},
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       assert.Equalf(t, tt.want, 
getMetadataServiceUrlParams(tt.args.jsonStr), 
"getMetadataServiceUrlParams(%v)", tt.args.jsonStr)
+               })
+       }
+}
+
+type mockProtocol struct {
+       mock.Mock
+}
+
+func (m *mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+       args := m.Called()
+       return args.Get(0).(protocol.Exporter)
+}
+
+func (m *mockProtocol) Refer(url *common.URL) protocol.Invoker {
+       args := m.Called()
+       if args.Get(0) == nil {
+               return nil
+       }
+       return args.Get(0).(protocol.Invoker)
+}
+
+func (m *mockProtocol) Destroy() {
+}
+
+type mockInvoker struct {
+       mock.Mock
+}
+
+func (m *mockInvoker) GetURL() *common.URL {
+       return nil
+}
+
+func (m *mockInvoker) IsAvailable() bool {
+       return true
+}
+
+func (m *mockInvoker) Destroy() {
+       m.Called()
+}
+
+func (m *mockInvoker) Invoke(ctx context.Context, inv protocol.Invocation) 
protocol.Result {
+       args := m.Mock.Called()
+       meta := args.Get(0).(protocol.Result).Result().(*info.MetadataInfo)
+       reply := inv.Reply().(*info.MetadataInfo)
+       reply.App = meta.App
+       reply.Tag = meta.Tag
+       reply.Revision = meta.Revision
+       reply.Services = meta.Services
+       return args.Get(0).(protocol.Result)
+}
+
+type mockExporter struct {
+       mock.Mock
+}
+
+func (m *mockExporter) GetInvoker() protocol.Invoker {
+       args := m.Called()
+       return args.Get(0).(protocol.Invoker)
+}
+
+func (m *mockExporter) UnExport() {
+       m.Called()
+}
diff --git a/metadata/info/metadata_info.go b/metadata/info/metadata_info.go
index 471a97d81..126d27c91 100644
--- a/metadata/info/metadata_info.go
+++ b/metadata/info/metadata_info.go
@@ -22,6 +22,7 @@ import (
        "hash/crc32"
        "net/url"
        "sort"
+       "strconv"
        "strings"
 )
 
@@ -66,8 +67,18 @@ type MetadataInfo struct {
        subscribedServiceURLs map[string][]*common.URL `hessian:"-"` // client 
subscribed service urls
 }
 
+func NewAppMetadataInfo(app string) *MetadataInfo {
+       return NewMetadataInfo(app, "")
+}
+
 func NewMetadataInfo(app, tag string) *MetadataInfo {
-       return NewMetadataInfoWithParams(app, "", make(map[string]*ServiceInfo))
+       return &MetadataInfo{
+               App:                   app,
+               Tag:                   tag,
+               Services:              make(map[string]*ServiceInfo),
+               exportedServiceURLs:   make(map[string][]*common.URL),
+               subscribedServiceURLs: make(map[string][]*common.URL),
+       }
 }
 
 func NewMetadataInfoWithParams(app string, revision string, services 
map[string]*ServiceInfo) *MetadataInfo {
@@ -207,9 +218,9 @@ type ServiceInfo struct {
        URL        *common.URL `json:"-" hessian:"-"`
 }
 
-// nolint
 func NewServiceInfoWithURL(url *common.URL) *ServiceInfo {
        service := NewServiceInfo(url.Service(), url.Group(), url.Version(), 
url.Protocol, url.Path, nil)
+       service.Port, _ = strconv.Atoi(url.Port)
        service.URL = url
        // TODO includeKeys load dynamic
        p := make(map[string]string, 8)
@@ -231,7 +242,6 @@ func NewServiceInfoWithURL(url *common.URL) *ServiceInfo {
        return service
 }
 
-// nolint
 func NewServiceInfo(name, group, version, protocol, path string, params 
map[string]string) *ServiceInfo {
        serviceKey := common.ServiceKey(name, group, version)
        matchKey := common.MatchKey(serviceKey, protocol)
@@ -256,16 +266,13 @@ func (si *ServiceInfo) GetMethods() []string {
        return strings.Split(s, ",")
 }
 
-// nolint
 func (si *ServiceInfo) GetParams() url.Values {
        v := url.Values{}
-       methodNames := si.Params[constant.MethodsKey]
-       if len(methodNames) == 0 {
-               return v
-       }
        methods := gxset.NewSet()
-       for _, method := range strings.Split(si.Params[constant.MethodsKey], 
",") {
-               methods.Add(method)
+       if methodNames, ok := si.Params[constant.MethodsKey]; ok {
+               for _, method := range strings.Split(methodNames, ",") {
+                       methods.Add(method)
+               }
        }
        for k, p := range si.Params {
                ms := strings.Index(k, ".")
@@ -278,7 +285,6 @@ func (si *ServiceInfo) GetParams() url.Values {
        return v
 }
 
-// nolint
 func (si *ServiceInfo) GetMatchKey() string {
        if si.MatchKey != "" {
                return si.MatchKey
@@ -288,7 +294,6 @@ func (si *ServiceInfo) GetMatchKey() string {
        return si.MatchKey
 }
 
-// nolint
 func (si *ServiceInfo) GetServiceKey() string {
        if si.ServiceKey != "" {
                return si.ServiceKey
diff --git a/metadata/info/metadata_info_test.go 
b/metadata/info/metadata_info_test.go
index 41889bfdc..94cd6e025 100644
--- a/metadata/info/metadata_info_test.go
+++ b/metadata/info/metadata_info_test.go
@@ -19,6 +19,8 @@ package info
 
 import (
        "encoding/json"
+       "strconv"
+       "strings"
        "testing"
 )
 
@@ -32,6 +34,19 @@ import (
        "dubbo.apache.org/dubbo-go/v3/common"
 )
 
+var (
+       serviceUrl = common.NewURLWithOptions(
+               common.WithProtocol("tri"),
+               common.WithIp("127.0.0.1"),
+               common.WithPort("20035"),
+               common.WithPath("/org.apache.dubbo.samples.proto.GreetService"),
+               
common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+               common.WithMethods([]string{"Greet", "SayHello"}),
+               common.WithParamsValue("loadbalance", "random"),
+               common.WithParamsValue("methods.Greet.timeout", "1000"),
+       )
+)
+
 func TestMetadataInfoAddService(t *testing.T) {
        metadataInfo := &MetadataInfo{
                Services:              make(map[string]*ServiceInfo),
@@ -42,11 +57,11 @@ func TestMetadataInfoAddService(t *testing.T) {
        url, _ := 
common.NewURL("dubbo://127.0.0.1:20000?application=foo&category=providers&check=false&dubbo=dubbo-go+v1.5.0&interface=com.foo.Bar&methods=GetPetByID%2CGetPetTypes&organization=Apache&owner=foo&revision=1.0.0&side=provider&version=1.0.0")
        metadataInfo.AddService(url)
        assert.True(t, len(metadataInfo.Services) > 0)
-       assert.True(t, len(metadataInfo.exportedServiceURLs) > 0)
+       assert.True(t, len(metadataInfo.GetExportedServiceURLs()) > 0)
 
        metadataInfo.RemoveService(url)
        assert.True(t, len(metadataInfo.Services) == 0)
-       assert.True(t, len(metadataInfo.exportedServiceURLs) == 0)
+       assert.True(t, len(metadataInfo.GetExportedServiceURLs()) == 0)
 }
 
 func TestHessian(t *testing.T) {
@@ -67,3 +82,78 @@ func TestHessian(t *testing.T) {
        metaJson, _ := json.Marshal(metadataInfo)
        assert.Equal(t, objJson, metaJson)
 }
+
+func TestMetadataInfoAddSubscribeURL(t *testing.T) {
+       info := NewMetadataInfo("dubbo", "tag")
+       info.AddSubscribeURL(serviceUrl)
+       assert.True(t, len(info.GetSubscribedURLs()) > 0)
+       info.RemoveSubscribeURL(serviceUrl)
+       assert.True(t, len(info.GetSubscribedURLs()) == 0)
+}
+
+func TestMetadataInfoCalAndGetRevision(t *testing.T) {
+       metadata := NewAppMetadataInfo("dubbo")
+       assert.Equalf(t, "0", metadata.CalAndGetRevision(), 
"CalAndGetRevision()")
+       metadata.AddService(serviceUrl)
+       assert.True(t, metadata.CalAndGetRevision() != "0")
+
+       v := metadata.Revision
+       assert.Equal(t, v, metadata.CalAndGetRevision(), "CalAndGetRevision() 
test cache")
+
+       metadata = NewAppMetadataInfo("dubbo")
+       url1 := serviceUrl.Clone()
+       url1.Methods = []string{}
+       metadata.AddService(url1)
+       assert.True(t, metadata.CalAndGetRevision() != "0", 
"CalAndGetRevision() test empty methods")
+}
+
+func TestNewMetadataInfo(t *testing.T) {
+       info := NewMetadataInfo("dubbo", "tag")
+       assert.Equal(t, info.App, "dubbo")
+       assert.Equal(t, info.Tag, "tag")
+}
+
+func TestNewMetadataInfoWithParams(t *testing.T) {
+       info := NewMetadataInfoWithParams("dubbo", "",
+               
map[string]*ServiceInfo{"org.apache.dubbo.samples.proto.GreetService": 
NewServiceInfoWithURL(serviceUrl)})
+       assert.Equal(t, info.App, "dubbo")
+       assert.Equal(t, info.Revision, "")
+       assert.Equal(t, info.Services, 
map[string]*ServiceInfo{"org.apache.dubbo.samples.proto.GreetService": 
NewServiceInfoWithURL(serviceUrl)})
+}
+
+func TestNewServiceInfoWithURL(t *testing.T) {
+       info := NewServiceInfoWithURL(serviceUrl)
+       assert.True(t, info.URL == serviceUrl)
+       assert.Equal(t, info.Protocol, serviceUrl.Protocol)
+       assert.Equal(t, info.Name, serviceUrl.Interface())
+       assert.Equal(t, info.Group, serviceUrl.Group())
+       assert.Equal(t, info.Version, serviceUrl.Version())
+       assert.Equal(t, strconv.Itoa(info.Port), serviceUrl.Port)
+       assert.Equal(t, info.Path, strings.TrimPrefix(serviceUrl.Path, "/"))
+       assert.Equal(t, info.Params["Greet.timeout"], "1000")
+}
+
+func TestServiceInfoGetMethods(t *testing.T) {
+       service := NewServiceInfoWithURL(serviceUrl)
+       assert.Equal(t, service.GetMethods(), []string{"Greet", "SayHello"})
+}
+
+func TestServiceInfoGetParams(t *testing.T) {
+       service := NewServiceInfoWithURL(serviceUrl)
+       assert.Equal(t, service.GetParams()["loadbalance"], []string{"random"})
+}
+
+func TestServiceInfoGetMatchKey(t *testing.T) {
+       si := NewServiceInfoWithURL(serviceUrl)
+       matchKey := si.MatchKey
+       assert.Equal(t, si.GetMatchKey(), matchKey)
+       si.MatchKey = ""
+       assert.True(t, si.GetMatchKey() != "")
+       si.MatchKey = ""
+       si.ServiceKey = ""
+       assert.True(t, si.GetMatchKey() != "")
+}
+
+func TestServiceInfoJavaClassName(t *testing.T) {
+       assert.Equalf(t, "org.apache.dubbo.metadata.MetadataInfo", 
NewAppMetadataInfo("dubbo").JavaClassName(), "JavaClassName()")
+}
diff --git a/metadata/mapping/metadata/service_name_mapping.go 
b/metadata/mapping/metadata/service_name_mapping.go
index 95e588ef9..1346436b2 100644
--- a/metadata/mapping/metadata/service_name_mapping.go
+++ b/metadata/mapping/metadata/service_name_mapping.go
@@ -23,7 +23,6 @@ import (
 
 import (
        gxset "github.com/dubbogo/gost/container/set"
-       "github.com/dubbogo/gost/log/logger"
 
        perrors "github.com/pkg/errors"
 )
@@ -81,7 +80,6 @@ func (d *ServiceNameMapping) Map(url *common.URL) error {
                        }
                }
                if err != nil {
-                       logger.Errorf("Failed registering mapping to remote, 
&v", err)
                        return err
                }
        }
diff --git a/metadata/mapping/metadata/service_name_mapping_test.go 
b/metadata/mapping/metadata/service_name_mapping_test.go
new file mode 100644
index 000000000..01b25f569
--- /dev/null
+++ b/metadata/mapping/metadata/service_name_mapping_test.go
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+       "errors"
+       "testing"
+)
+
+import (
+       gxset "github.com/dubbogo/gost/container/set"
+       "github.com/dubbogo/gost/gof/observer"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+)
+
+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"
+       "dubbo.apache.org/dubbo-go/v3/metadata/info"
+       "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
+       "dubbo.apache.org/dubbo-go/v3/metadata/report"
+)
+
+func TestGetNameMappingInstance(t *testing.T) {
+       ins := GetNameMappingInstance()
+       assert.NotNil(t, ins)
+}
+
+func TestNoReportInstance(t *testing.T) {
+       ins := GetNameMappingInstance()
+       lis := &listener{}
+       serviceUrl := common.NewURLWithOptions(
+               
common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+               common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+       )
+       _, err := ins.Get(serviceUrl, lis)
+       assert.NotNil(t, err, "test Get no report instance")
+       err = ins.Map(serviceUrl)
+       assert.NotNil(t, err, "test Map with no report instance")
+       err = ins.Remove(serviceUrl)
+       assert.NotNil(t, err, "test Remove with no report instance")
+}
+
+func TestServiceNameMappingGet(t *testing.T) {
+       ins := GetNameMappingInstance()
+       lis := &listener{}
+       serviceUrl := common.NewURLWithOptions(
+               
common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+               common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+       )
+       mockReport, err := initMock()
+       assert.Nil(t, err)
+       t.Run("test normal", func(t *testing.T) {
+               
mockReport.On("GetServiceAppMapping").Return(gxset.NewSet("dubbo"), nil).Once()
+               apps, er := ins.Get(serviceUrl, lis)
+               assert.Nil(t, er)
+               assert.True(t, !apps.Empty())
+       })
+       t.Run("test error", func(t *testing.T) {
+               mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), 
errors.New("mock error")).Once()
+               _, err = ins.Get(serviceUrl, lis)
+               assert.NotNil(t, err)
+       })
+       mockReport.AssertExpectations(t)
+}
+
+func TestServiceNameMappingMap(t *testing.T) {
+       ins := GetNameMappingInstance()
+       serviceUrl := common.NewURLWithOptions(
+               
common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+               common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+       )
+       mockReport, err := initMock()
+       assert.Nil(t, err)
+       t.Run("test normal", func(t *testing.T) {
+               mockReport.On("RegisterServiceAppMapping").Return(nil).Once()
+               err = ins.Map(serviceUrl)
+               assert.Nil(t, err)
+       })
+       t.Run("test error", func(t *testing.T) {
+               
mockReport.On("RegisterServiceAppMapping").Return(errors.New("mock 
error")).Times(retryTimes)
+               err = ins.Map(serviceUrl)
+               assert.NotNil(t, err, "test mapping error")
+       })
+       mockReport.AssertExpectations(t)
+}
+
+func TestServiceNameMappingRemove(t *testing.T) {
+       ins := GetNameMappingInstance()
+       serviceUrl := common.NewURLWithOptions(
+               
common.WithInterface("org.apache.dubbo.samples.proto.GreetService"),
+               common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+       )
+       mockReport, err := initMock()
+       assert.Nil(t, err)
+       t.Run("test normal", func(t *testing.T) {
+               
mockReport.On("RemoveServiceAppMappingListener").Return(nil).Once()
+               err = ins.Remove(serviceUrl)
+               assert.Nil(t, err)
+       })
+       t.Run("test error", func(t *testing.T) {
+               
mockReport.On("RemoveServiceAppMappingListener").Return(errors.New("mock 
error")).Once()
+               err = ins.Remove(serviceUrl)
+               assert.NotNil(t, err)
+       })
+       mockReport.AssertExpectations(t)
+}
+
+func initMock() (*mockMetadataReport, error) {
+       metadataReport := new(mockMetadataReport)
+       extension.SetMetadataReportFactory("mock", func() 
report.MetadataReportFactory {
+               return metadataReport
+       })
+       opts := metadata.NewReportOptions(
+               metadata.WithProtocol("mock"),
+               metadata.WithAddress("127.0.0.1"),
+       )
+       err := opts.Init()
+       return metadataReport, err
+}
+
+type listener struct {
+}
+
+func (l listener) OnEvent(e observer.Event) error {
+       return nil
+}
+
+func (l listener) Stop() {
+}
+
+type mockMetadataReport struct {
+       mock.Mock
+}
+
+func (m *mockMetadataReport) CreateMetadataReport(*common.URL) 
report.MetadataReport {
+       return m
+}
+
+func (m *mockMetadataReport) GetAppMetadata(string, string) 
(*info.MetadataInfo, error) {
+       args := m.Called()
+       return args.Get(0).(*info.MetadataInfo), args.Error(1)
+}
+
+func (m *mockMetadataReport) PublishAppMetadata(string, string, 
*info.MetadataInfo) error {
+       args := m.Called()
+       return args.Error(0)
+}
+
+func (m *mockMetadataReport) RegisterServiceAppMapping(string, string, string) 
error {
+       args := m.Called()
+       return args.Error(0)
+}
+
+func (m *mockMetadataReport) GetServiceAppMapping(string, string, 
mapping.MappingListener) (*gxset.HashSet, error) {
+       args := m.Called()
+       return args.Get(0).(*gxset.HashSet), args.Error(1)
+}
+
+func (m *mockMetadataReport) RemoveServiceAppMappingListener(string, string) 
error {
+       args := m.Called()
+       return args.Error(0)
+}
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 6fbbfcd8c..03077bbeb 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -25,8 +25,8 @@ import (
 )
 
 var (
-       metadataService    MetadataService = &DefaultMetadataService{}
-       appMetadataInfoMap                 = make(map[string]*info.MetadataInfo)
+       registryMetadataInfo                 = 
make(map[string]*info.MetadataInfo)
+       metadataService      MetadataService = 
&DefaultMetadataService{metadataMap: registryMetadataInfo}
 )
 
 func GetMetadataService() MetadataService {
@@ -34,25 +34,25 @@ func GetMetadataService() MetadataService {
 }
 
 func GetMetadataInfo(registryId string) *info.MetadataInfo {
-       return appMetadataInfoMap[registryId]
+       return registryMetadataInfo[registryId]
 }
 
 func AddService(registryId string, url *common.URL) {
-       if _, exist := appMetadataInfoMap[registryId]; !exist {
-               appMetadataInfoMap[registryId] = info.NewMetadataInfo(
+       if _, exist := registryMetadataInfo[registryId]; !exist {
+               registryMetadataInfo[registryId] = info.NewMetadataInfo(
                        url.GetParam(constant.ApplicationKey, ""),
                        url.GetParam(constant.ApplicationTagKey, ""),
                )
        }
-       appMetadataInfoMap[registryId].AddService(url)
+       registryMetadataInfo[registryId].AddService(url)
 }
 
 func AddSubscribeURL(registryId string, url *common.URL) {
-       if _, exist := appMetadataInfoMap[registryId]; !exist {
-               appMetadataInfoMap[registryId] = info.NewMetadataInfo(
+       if _, exist := registryMetadataInfo[registryId]; !exist {
+               registryMetadataInfo[registryId] = info.NewMetadataInfo(
                        url.GetParam(constant.ApplicationKey, ""),
                        url.GetParam(constant.ApplicationTagKey, ""),
                )
        }
-       appMetadataInfoMap[registryId].AddSubscribeURL(url)
+       registryMetadataInfo[registryId].AddSubscribeURL(url)
 }
diff --git a/metadata/metadata_service.go b/metadata/metadata_service.go
index 5a3591c54..80eb159d5 100644
--- a/metadata/metadata_service.go
+++ b/metadata/metadata_service.go
@@ -40,8 +40,8 @@ import (
 
 // version will be used by Version func
 const (
-       version              = "1.0.0"
-       allServiceInterfaces = "*"
+       version  = "1.0.0"
+       allMatch = "*"
 )
 
 // MetadataService is used to define meta data related behaviors
@@ -58,12 +58,11 @@ type MetadataService interface {
        GetMetadataInfo(revision string) (*info.MetadataInfo, error)
        // GetMetadataServiceURL will return the url of metadata service
        GetMetadataServiceURL() (*common.URL, error)
-       // SetMetadataServiceURL exporter to set url of metadata service, will 
not be exported by exporter,cause no error return
-       SetMetadataServiceURL(*common.URL)
 }
 
 // DefaultMetadataService is store and query the metadata info in memory when 
each service registry
 type DefaultMetadataService struct {
+       metadataMap map[string]*info.MetadataInfo
        metadataUrl *common.URL
 }
 
@@ -73,19 +72,16 @@ func (mts *DefaultMetadataService) 
SetMetadataServiceURL(url *common.URL) {
 
 // GetExportedURLs get all exported urls
 func (mts *DefaultMetadataService) GetExportedURLs(serviceInterface string, 
group string, version string, protocol string) ([]*common.URL, error) {
-       if allServiceInterfaces == serviceInterface {
-               return mts.GetExportedServiceURLs()
-       }
        all, err := mts.GetExportedServiceURLs()
        if err != nil {
                return nil, err
        }
        urls := make([]*common.URL, 0)
        for _, url := range all {
-               if url.GetParam(constant.InterfaceKey, "") == serviceInterface 
&&
-                       url.GetParam(constant.GroupKey, "") == group &&
-                       url.GetParam(constant.ProtocolKey, "") == protocol &&
-                       url.GetParam(constant.VersionKey, "") == version {
+               if (url.Interface() == serviceInterface || serviceInterface == 
allMatch) &&
+                       (url.Group() == group || group == allMatch) &&
+                       (url.Protocol == protocol || protocol == allMatch) &&
+                       (url.Version() == version || version == allMatch) {
                        urls = append(urls, url)
                }
        }
@@ -97,7 +93,7 @@ func (mts *DefaultMetadataService) GetMetadataInfo(revision 
string) (*info.Metad
        if revision == "" {
                return nil, nil
        }
-       for _, metadataInfo := range appMetadataInfoMap {
+       for _, metadataInfo := range mts.metadataMap {
                if metadataInfo.Revision == revision {
                        return metadataInfo, nil
                }
@@ -109,7 +105,7 @@ func (mts *DefaultMetadataService) GetMetadataInfo(revision 
string) (*info.Metad
 // GetExportedServiceURLs get exported service urls
 func (mts *DefaultMetadataService) GetExportedServiceURLs() ([]*common.URL, 
error) {
        urls := make([]*common.URL, 0)
-       for _, metadataInfo := range appMetadataInfoMap {
+       for _, metadataInfo := range mts.metadataMap {
                urls = append(urls, metadataInfo.GetExportedServiceURLs()...)
        }
        return urls, nil
@@ -127,7 +123,7 @@ func (mts *DefaultMetadataService) GetMetadataServiceURL() 
(*common.URL, error)
 
 func (mts *DefaultMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
        urls := make([]*common.URL, 0)
-       for _, metadataInfo := range appMetadataInfoMap {
+       for _, metadataInfo := range mts.metadataMap {
                urls = append(urls, metadataInfo.GetSubscribedURLs()...)
        }
        return urls, nil
@@ -141,15 +137,15 @@ func (mts *DefaultMetadataService) MethodMapper() 
map[string]string {
        }
 }
 
-// ServiceExporter is the ConfigurableMetadataServiceExporter which implement 
MetadataServiceExporter interface
-type ServiceExporter struct {
+// serviceExporter export MetadataService with dubbo protocol
+type serviceExporter struct {
        opts             *Options
        service          MetadataService
        protocolExporter protocol.Exporter
 }
 
 // Export will export the metadataService
-func (e *ServiceExporter) Export() error {
+func (e *serviceExporter) Export() error {
        version, _ := e.service.Version()
        var port string
        if e.opts.port == 0 {
@@ -181,7 +177,7 @@ func (e *ServiceExporter) Export() error {
        proxyFactory := extension.GetProxyFactory("")
        invoker := proxyFactory.GetInvoker(ivkURL)
        e.protocolExporter = 
extension.GetProtocol(ivkURL.Protocol).Export(invoker)
-       e.service.SetMetadataServiceURL(ivkURL)
+       e.service.(*DefaultMetadataService).SetMetadataServiceURL(ivkURL)
        logger.Infof("[Metadata Service] The MetadataService exports urls : %v 
", ivkURL)
        return nil
 }
@@ -199,6 +195,6 @@ func randomPort() string {
 }
 
 // UnExport will unExport the metadataService
-func (e *ServiceExporter) UnExport() {
+func (e *serviceExporter) UnExport() {
        e.protocolExporter.UnExport()
 }
diff --git a/metadata/metadata_service_test.go 
b/metadata/metadata_service_test.go
new file mode 100644
index 000000000..093995577
--- /dev/null
+++ b/metadata/metadata_service_test.go
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+       "strconv"
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+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/info"
+       "dubbo.apache.org/dubbo-go/v3/protocol"
+       _ "dubbo.apache.org/dubbo-go/v3/proxy/proxy_factory"
+)
+
+var (
+       url, _ = 
common.NewURL("dubbo://127.0.0.1:20000?application=foo&category=providers&check=false&dubbo=dubbo-go+v1.5.0&interface=com.foo.Bar&methods=GetPetByID%2CGetPetTypes&organization=Apache&owner=foo&revision=1.0.0&side=provider&version=1.0.0")
+)
+
+func newMetadataMap() map[string]*info.MetadataInfo {
+       metadataInfo := info.NewAppMetadataInfo("dubbo-app")
+       metadataInfo.Revision = "1"
+       metadataInfo.AddService(url)
+       metadataInfo.AddSubscribeURL(url)
+       registryMetadataInfo["default"] = metadataInfo
+       return map[string]*info.MetadataInfo{
+               "default": metadataInfo,
+       }
+}
+
+func TestDefaultMetadataServiceGetExportedServiceURLs(t *testing.T) {
+       mts := &DefaultMetadataService{
+               metadataMap: newMetadataMap(),
+       }
+       got, err := mts.GetExportedServiceURLs()
+       assert.Nil(t, err)
+       assert.True(t, len(got) == 1)
+       assert.Equal(t, url, got[0])
+}
+
+func TestDefaultMetadataServiceGetExportedURLs(t *testing.T) {
+       type args struct {
+               serviceInterface string
+               group            string
+               version          string
+               protocol         string
+       }
+       tests := []struct {
+               name string
+               args args
+               want []*common.URL
+       }{
+               {
+                       name: "all exact",
+                       args: args{
+                               serviceInterface: url.Interface(),
+                               group:            url.Group(),
+                               version:          url.Version(),
+                               protocol:         url.Protocol,
+                       },
+                       want: []*common.URL{url},
+               },
+               {
+                       name: "interface *",
+                       args: args{
+                               serviceInterface: "*",
+                               group:            url.Group(),
+                               version:          url.Version(),
+                               protocol:         url.Protocol,
+                       },
+                       want: []*common.URL{url},
+               },
+               {
+                       name: "group *",
+                       args: args{
+                               serviceInterface: url.Interface(),
+                               group:            "*",
+                               version:          url.Version(),
+                               protocol:         url.Protocol,
+                       },
+                       want: []*common.URL{url},
+               },
+               {
+                       name: "version *",
+                       args: args{
+                               serviceInterface: url.Interface(),
+                               group:            url.Group(),
+                               version:          "*",
+                               protocol:         url.Protocol,
+                       },
+                       want: []*common.URL{url},
+               },
+               {
+                       name: "protocol *",
+                       args: args{
+                               serviceInterface: url.Interface(),
+                               group:            url.Group(),
+                               version:          url.Version(),
+                               protocol:         "*",
+                       },
+                       want: []*common.URL{url},
+               },
+               {
+                       name: "all *",
+                       args: args{
+                               serviceInterface: "*",
+                               group:            "*",
+                               version:          "*",
+                               protocol:         "*",
+                       },
+                       want: []*common.URL{url},
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataMap: newMetadataMap(),
+                       }
+                       got, err := 
mts.GetExportedURLs(tt.args.serviceInterface, tt.args.group, tt.args.version, 
tt.args.protocol)
+                       assert.Nil(t, err)
+                       assert.Equalf(t, tt.want, got, "GetExportedURLs(%v, %v, 
%v, %v)", tt.args.serviceInterface, tt.args.group, tt.args.version, 
tt.args.protocol)
+               })
+       }
+}
+
+func TestDefaultMetadataServiceGetMetadataInfo(t *testing.T) {
+       type args struct {
+               revision string
+       }
+       tests := []struct {
+               name string
+               args args
+               want *info.MetadataInfo
+       }{
+               {
+                       name: "normal",
+                       args: args{
+                               revision: "1",
+                       },
+                       want: newMetadataMap()["default"],
+               },
+               {
+                       name: "empty revision",
+                       args: args{
+                               revision: "",
+                       },
+                       want: nil,
+               },
+               {
+                       name: "revision not match",
+                       args: args{
+                               revision: "2",
+                       },
+                       want: nil,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataMap: newMetadataMap(),
+                       }
+                       got, err := mts.GetMetadataInfo(tt.args.revision)
+                       assert.Nil(t, err)
+                       assert.Equalf(t, tt.want, got, "GetMetadataInfo(%v)", 
tt.args.revision)
+               })
+       }
+}
+
+func TestDefaultMetadataServiceGetMetadataServiceURL(t *testing.T) {
+       type fields struct {
+               metadataUrl *common.URL
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   *common.URL
+       }{
+               {
+                       name: "normal",
+                       fields: fields{
+                               metadataUrl: url,
+                       },
+                       want: url,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataUrl: tt.fields.metadataUrl,
+                       }
+                       got, err := mts.GetMetadataServiceURL()
+                       assert.Nil(t, err)
+                       assert.Equalf(t, tt.want, got, 
"GetMetadataServiceURL()")
+               })
+       }
+}
+
+func TestDefaultMetadataServiceGetSubscribedURLs(t *testing.T) {
+       tests := []struct {
+               name string
+               want []*common.URL
+       }{
+               {
+                       name: "normal",
+                       want: []*common.URL{url},
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataMap: newMetadataMap(),
+                       }
+                       got, err := mts.GetSubscribedURLs()
+                       assert.Nil(t, err)
+                       assert.Equalf(t, tt.want, got, "GetSubscribedURLs()")
+               })
+       }
+}
+
+func TestDefaultMetadataServiceMethodMapper(t *testing.T) {
+       tests := []struct {
+               name string
+               want map[string]string
+       }{
+               {
+                       name: "normal",
+                       want: map[string]string{
+                               "GetExportedURLs": "getExportedURLs",
+                               "GetMetadataInfo": "getMetadataInfo",
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataMap: newMetadataMap(),
+                       }
+                       assert.Equalf(t, tt.want, mts.MethodMapper(), 
"MethodMapper()")
+               })
+       }
+}
+
+func TestDefaultMetadataServiceSetMetadataServiceURL(t *testing.T) {
+       type args struct {
+               url *common.URL
+       }
+       tests := []struct {
+               name string
+               args args
+               want *common.URL
+       }{
+               {
+                       name: "normal",
+                       args: args{
+                               url: url,
+                       },
+                       want: url,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       mts := &DefaultMetadataService{
+                               metadataMap: map[string]*info.MetadataInfo{},
+                       }
+                       mts.SetMetadataServiceURL(tt.args.url)
+                       assert.Equal(t, tt.want, mts.metadataUrl)
+               })
+       }
+}
+
+func TestDefaultMetadataServiceVersion(t *testing.T) {
+       mts := &DefaultMetadataService{}
+       got, err := mts.Version()
+       assert.Nil(t, err)
+       assert.Equal(t, version, got)
+}
+
+func Test_randomPort(t *testing.T) {
+       port := randomPort()
+       assert.True(t, port != "")
+}
+
+func Test_serviceExporterExport(t *testing.T) {
+       mockExporter := new(mockExporter)
+       defer mockExporter.AssertExpectations(t)
+       mockProtocol := new(mockProtocol)
+       defer mockProtocol.AssertExpectations(t)
+       extension.SetProtocol("dubbo", func() protocol.Protocol {
+               return mockProtocol
+       })
+       t.Run("normal", func(t *testing.T) {
+               port := randomPort()
+               p, err := strconv.Atoi(port)
+               assert.Nil(t, err)
+               opts := &Options{
+                       appName:      "dubbo-app",
+                       metadataType: constant.RemoteMetadataStorageType,
+                       port:         p,
+               }
+               mockProtocol.On("Export").Return(mockExporter).Once()
+               mockExporter.On("UnExport").Once()
+               e := &serviceExporter{
+                       opts:    opts,
+                       service: &DefaultMetadataService{},
+               }
+               err = e.Export()
+               assert.Nil(t, err)
+               e.UnExport()
+       })
+       // first t.Run has called commom.ServiceMap.Register ,second will fail
+       t.Run("get methods error", func(t *testing.T) {
+               port := randomPort()
+               p, err := strconv.Atoi(port)
+               assert.Nil(t, err)
+               opts := &Options{
+                       appName:      "dubbo-app",
+                       metadataType: constant.RemoteMetadataStorageType,
+                       port:         p,
+               }
+               e := &serviceExporter{
+                       opts:    opts,
+                       service: &DefaultMetadataService{},
+               }
+               err = e.Export()
+               assert.NotNil(t, err)
+       })
+       t.Run("port == 0", func(t *testing.T) {
+               opts := &Options{
+                       appName:      "dubbo-app",
+                       metadataType: constant.RemoteMetadataStorageType,
+                       port:         0,
+               }
+               // UnRegister first otherwise will fail
+               err := 
common.ServiceMap.UnRegister(constant.MetadataServiceName, 
constant.DefaultProtocol,
+                       common.ServiceKey(constant.MetadataServiceName, 
opts.appName, version))
+               assert.Nil(t, err)
+               mockProtocol.On("Export").Return(mockExporter).Once()
+               mockExporter.On("UnExport").Once()
+               e := &serviceExporter{
+                       opts:    opts,
+                       service: &DefaultMetadataService{},
+               }
+               err = e.Export()
+               assert.Nil(t, err)
+               e.UnExport()
+       })
+}
+
+func Test_serviceExporterUnExport(t *testing.T) {
+       mockExporter := new(mockExporter)
+       defer mockExporter.AssertExpectations(t)
+       serviceExporter := &serviceExporter{protocolExporter: mockExporter}
+       mockExporter.On("UnExport").Once()
+       serviceExporter.UnExport()
+}
diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go
new file mode 100644
index 000000000..ae890773a
--- /dev/null
+++ b/metadata/metadata_test.go
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+       "reflect"
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/common"
+       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/metadata/info"
+)
+
+func TestAddService(t *testing.T) {
+       type args struct {
+               registryId string
+               url        *common.URL
+       }
+       tests := []struct {
+               name string
+               args args
+       }{
+               {
+                       name: "add",
+                       args: args{
+                               registryId: "reg1",
+                               url: common.NewURLWithOptions(
+                                       common.WithProtocol("dubbo"),
+                                       
common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+                                       
common.WithParamsValue(constant.ApplicationTagKey, "v1"),
+                               ),
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       AddService(tt.args.registryId, tt.args.url)
+                       assert.True(t, registryMetadataInfo[tt.args.registryId] 
!= nil)
+                       meta := registryMetadataInfo[tt.args.registryId]
+                       meta.App = 
tt.args.url.GetParam(constant.ApplicationKey, "")
+                       meta.Tag = 
tt.args.url.GetParam(constant.ApplicationTagKey, "")
+                       assert.True(t, 
reflect.DeepEqual(meta.GetExportedServiceURLs()[0], tt.args.url))
+               })
+       }
+}
+
+func TestAddSubscribeURL(t *testing.T) {
+       type args struct {
+               registryId string
+               url        *common.URL
+       }
+       tests := []struct {
+               name string
+               args args
+       }{
+               {
+                       name: "addSub",
+                       args: args{
+                               registryId: "reg2",
+                               url: common.NewURLWithOptions(
+                                       common.WithProtocol("dubbo"),
+                                       
common.WithParamsValue(constant.ApplicationKey, "dubbo"),
+                                       
common.WithParamsValue(constant.ApplicationTagKey, "v1"),
+                               ),
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       AddSubscribeURL(tt.args.registryId, tt.args.url)
+                       assert.True(t, registryMetadataInfo[tt.args.registryId] 
!= nil)
+                       meta := registryMetadataInfo[tt.args.registryId]
+                       meta.App = 
tt.args.url.GetParam(constant.ApplicationKey, "")
+                       meta.Tag = 
tt.args.url.GetParam(constant.ApplicationTagKey, "")
+                       assert.True(t, 
reflect.DeepEqual(meta.GetSubscribedURLs()[0], tt.args.url))
+               })
+       }
+}
+
+func TestGetMetadataInfo(t *testing.T) {
+       type args struct {
+               registryId string
+       }
+       tests := []struct {
+               name string
+               args args
+               want *info.MetadataInfo
+       }{
+               {
+                       name: "get",
+                       args: args{
+                               registryId: "reg3",
+                       },
+                       want: info.NewMetadataInfo("dubbo", "v2"),
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       registryMetadataInfo[tt.args.registryId] = tt.want
+                       assert.Equalf(t, tt.want, 
GetMetadataInfo(tt.args.registryId), "GetMetadataInfo(%v)", tt.args.registryId)
+               })
+       }
+}
+
+func TestGetMetadataService(t *testing.T) {
+       tests := []struct {
+               name string
+               want MetadataService
+       }{
+               {
+                       name: "getMetadataService",
+                       want: metadataService,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       assert.Equalf(t, tt.want, GetMetadataService(), 
"GetMetadataService()")
+               })
+       }
+}
diff --git a/metadata/options.go b/metadata/options.go
index 30e866abe..5a5eb5b42 100644
--- a/metadata/options.go
+++ b/metadata/options.go
@@ -26,11 +26,13 @@ import (
 
 import (
        "github.com/dubbogo/gost/log/logger"
+
+       perrors "github.com/pkg/errors"
 )
 
 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/global"
 )
 
@@ -62,7 +64,7 @@ func (opts *Options) Init() error {
        var err error
        exportOnce.Do(func() {
                if opts.metadataType != constant.RemoteMetadataStorageType {
-                       exporter := &ServiceExporter{service: metadataService, 
opts: opts}
+                       exporter := &serviceExporter{service: metadataService, 
opts: opts}
                        defer func() {
                                // TODO remove this recover func,this just to 
avoid some unit test failed,this will not happen in user side mostly
                                // config test -> metadata exporter -> dubbo 
protocol/remoting -> config,cycle import will occur
@@ -103,18 +105,33 @@ type ReportOptions struct {
 }
 
 func (opts *ReportOptions) Init() error {
-       fac := extension.GetMetadataReportFactory(opts.Protocol)
-       if fac == nil {
-               logger.Errorf("no metadata report factory of protocol %s 
found!", opts.Protocol)
-               return nil
-       }
-       url, err := toUrl(opts)
+       url, err := opts.toUrl()
        if err != nil {
                logger.Errorf("metadata report create error %v", err)
                return err
        }
-       instances[opts.registryId] = &DelegateMetadataReport{instance: 
fac.CreateMetadataReport(url)}
-       return nil
+       return addMetadataReport(opts.registryId, url)
+}
+
+func (opts *ReportOptions) toUrl() (*common.URL, error) {
+       res, err := common.NewURL(opts.Address,
+               common.WithUsername(opts.Username),
+               common.WithPassword(opts.Password),
+               common.WithLocation(opts.Address),
+               common.WithProtocol(opts.Protocol),
+               common.WithParamsValue(constant.TimeoutKey, opts.Timeout),
+               common.WithParamsValue(constant.MetadataReportGroupKey, 
opts.Group),
+               common.WithParamsValue(constant.MetadataReportNamespaceKey, 
opts.Namespace),
+               common.WithParamsValue(constant.ClientNameKey, 
strings.Join([]string{constant.MetadataReportPrefix, opts.Protocol, 
opts.Address}, "-")),
+       )
+       if err != nil || len(res.Protocol) == 0 {
+               return nil, perrors.New("Invalid MetadataReport Config.")
+       }
+       res.SetParam("metadata", res.Protocol)
+       for key, val := range opts.Params {
+               res.SetParam(key, val)
+       }
+       return res, nil
 }
 
 func defaultReportOptions() *ReportOptions {
diff --git a/metadata/report/report_factory.go 
b/metadata/report/report_factory.go
index 2c2af7349..cd2cf7c7a 100644
--- a/metadata/report/report_factory.go
+++ b/metadata/report/report_factory.go
@@ -25,5 +25,3 @@ import (
 type MetadataReportFactory interface {
        CreateMetadataReport(*common.URL) MetadataReport
 }
-
-type BaseMetadataReportFactory struct{}
diff --git a/metadata/report_instance.go b/metadata/report_instance.go
index 2238144f0..f7bceb458 100644
--- a/metadata/report_instance.go
+++ b/metadata/report_instance.go
@@ -18,19 +18,18 @@
 package metadata
 
 import (
-       "strings"
        "time"
 )
 
 import (
        "github.com/dubbogo/gost/container/set"
-
-       perrors "github.com/pkg/errors"
+       "github.com/dubbogo/gost/log/logger"
 )
 
 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/info"
        "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
        "dubbo.apache.org/dubbo-go/v3/metadata/report"
@@ -42,25 +41,14 @@ var (
        instances = make(map[string]report.MetadataReport)
 )
 
-func toUrl(opts *ReportOptions) (*common.URL, error) {
-       res, err := common.NewURL(opts.Address,
-               common.WithUsername(opts.Username),
-               common.WithPassword(opts.Password),
-               common.WithLocation(opts.Address),
-               common.WithProtocol(opts.Protocol),
-               common.WithParamsValue(constant.TimeoutKey, opts.Timeout),
-               common.WithParamsValue(constant.MetadataReportGroupKey, 
opts.Group),
-               common.WithParamsValue(constant.MetadataReportNamespaceKey, 
opts.Namespace),
-               common.WithParamsValue(constant.ClientNameKey, 
strings.Join([]string{constant.MetadataReportPrefix, opts.Protocol, 
opts.Address}, "-")),
-       )
-       if err != nil || len(res.Protocol) == 0 {
-               return nil, perrors.New("Invalid MetadataReport Config.")
-       }
-       res.SetParam("metadata", res.Protocol)
-       for key, val := range opts.Params {
-               res.SetParam(key, val)
+func addMetadataReport(registryId string, url *common.URL) error {
+       fac := extension.GetMetadataReportFactory(url.Protocol)
+       if fac == nil {
+               logger.Warnf("no metadata report factory of protocol %s found, 
please check if the metadata report factory is imported", url.Protocol)
+               return nil
        }
-       return res, nil
+       instances[registryId] = &DelegateMetadataReport{instance: 
fac.CreateMetadataReport(url)}
+       return nil
 }
 
 func GetMetadataReport() report.MetadataReport {
@@ -91,6 +79,9 @@ func GetMetadataReports() []report.MetadataReport {
 }
 
 func GetMetadataType() string {
+       if metadataOptions == nil || metadataOptions.metadataType == "" {
+               return constant.DefaultMetadataStorageType
+       }
        return metadataOptions.metadataType
 }
 
diff --git a/metadata/report_instance_test.go b/metadata/report_instance_test.go
new file mode 100644
index 000000000..4b89cdec9
--- /dev/null
+++ b/metadata/report_instance_test.go
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+       "reflect"
+       "testing"
+)
+
+import (
+       gxset "github.com/dubbogo/gost/container/set"
+       "github.com/dubbogo/gost/gof/observer"
+
+       "github.com/pkg/errors"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+)
+
+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/info"
+       "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
+       "dubbo.apache.org/dubbo-go/v3/metadata/report"
+       "dubbo.apache.org/dubbo-go/v3/metrics"
+       metricsMetadata "dubbo.apache.org/dubbo-go/v3/metrics/metadata"
+)
+
+func TestDelegateMetadataReportGetAppMetadata(t *testing.T) {
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       delegate := &DelegateMetadataReport{instance: mockReport}
+       metadataInfo := info.NewAppMetadataInfo("dubbo")
+       var ch = make(chan metrics.MetricsEvent, 10)
+       metrics.Subscribe(constant.MetricsMetadata, ch)
+       defer close(ch)
+       t.Run("normal", func(t *testing.T) {
+               mockReport.On("GetAppMetadata").Return(metadataInfo, nil).Once()
+               got, err := delegate.GetAppMetadata("dubbo", "1")
+               assert.Nil(t, err)
+               if !reflect.DeepEqual(got, metadataInfo) {
+                       t.Errorf("GetAppMetadata() got = %v, want %v", got, 
metadataInfo)
+               }
+               assert.True(t, len(ch) == 1)
+               metricEvent := <-ch
+               assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+               event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+               assert.True(t, ok)
+               assert.NotNil(t, event.Name, metricsMetadata.MetadataSub)
+               assert.NotNil(t, event.Start)
+               assert.NotNil(t, event.End)
+               assert.True(t, event.Succ)
+       })
+       t.Run("error", func(t *testing.T) {
+               
mockReport.On("GetAppMetadata").Return(info.NewAppMetadataInfo("dubbo"), 
errors.New("mock error")).Once()
+               _, err := delegate.GetAppMetadata("dubbo", "1111")
+               assert.NotNil(t, err)
+               assert.True(t, len(ch) == 1)
+               metricEvent := <-ch
+               assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+               event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+               assert.True(t, ok)
+               assert.NotNil(t, event.Name, metricsMetadata.MetadataSub)
+               assert.NotNil(t, event.Start)
+               assert.NotNil(t, event.End)
+               assert.True(t, !event.Succ)
+       })
+}
+
+func TestDelegateMetadataReportPublishAppMetadata(t *testing.T) {
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       delegate := &DelegateMetadataReport{instance: mockReport}
+       metadataInfo := info.NewAppMetadataInfo("dubbo")
+       var ch = make(chan metrics.MetricsEvent, 10)
+       metrics.Subscribe(constant.MetricsMetadata, ch)
+       defer close(ch)
+       t.Run("normal", func(t *testing.T) {
+               mockReport.On("PublishAppMetadata").Return(nil).Once()
+               err := delegate.PublishAppMetadata("application", "revision", 
metadataInfo)
+               assert.Nil(t, err)
+               assert.True(t, len(ch) == 1)
+               metricEvent := <-ch
+               assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+               event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+               assert.True(t, ok)
+               assert.NotNil(t, event.Name, metricsMetadata.MetadataPush)
+               assert.NotNil(t, event.Start)
+               assert.NotNil(t, event.End)
+               assert.True(t, event.Succ)
+       })
+       t.Run("error", func(t *testing.T) {
+               mockReport.On("PublishAppMetadata").Return(errors.New("mock 
error")).Once()
+               err := delegate.PublishAppMetadata("application", "revision", 
metadataInfo)
+               assert.NotNil(t, err)
+               assert.True(t, len(ch) == 1)
+               metricEvent := <-ch
+               assert.Equal(t, metricEvent.Type(), constant.MetricsMetadata)
+               event, ok := metricEvent.(*metricsMetadata.MetadataMetricEvent)
+               assert.True(t, ok)
+               assert.NotNil(t, event.Name, metricsMetadata.MetadataPush)
+               assert.NotNil(t, event.Start)
+               assert.NotNil(t, event.End)
+               assert.True(t, !event.Succ)
+       })
+}
+
+func TestDelegateMetadataReportGetServiceAppMapping(t *testing.T) {
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       delegate := &DelegateMetadataReport{instance: mockReport}
+       t.Run("normal", func(t *testing.T) {
+               mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), 
nil).Once()
+               got, err := delegate.GetServiceAppMapping("dubbo", "dev", 
&listener{})
+               assert.Nil(t, err)
+               assert.True(t, got.Empty())
+       })
+       t.Run("error", func(t *testing.T) {
+               mockReport.On("GetServiceAppMapping").Return(gxset.NewSet(), 
errors.New("mock error")).Once()
+               _, err := delegate.GetServiceAppMapping("dubbo", "dev", 
&listener{})
+               assert.NotNil(t, err)
+       })
+}
+
+func TestDelegateMetadataReportRegisterServiceAppMapping(t *testing.T) {
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       delegate := &DelegateMetadataReport{instance: mockReport}
+       t.Run("normal", func(t *testing.T) {
+               mockReport.On("RegisterServiceAppMapping").Return(nil).Once()
+               err := delegate.RegisterServiceAppMapping("interfaceName", 
"group", "application")
+               assert.Nil(t, err)
+       })
+       t.Run("error", func(t *testing.T) {
+               
mockReport.On("RegisterServiceAppMapping").Return(errors.New("mock 
error")).Once()
+               err := delegate.RegisterServiceAppMapping("interfaceName", 
"group", "application")
+               assert.NotNil(t, err)
+       })
+}
+
+func TestDelegateMetadataReportRemoveServiceAppMappingListener(t *testing.T) {
+       mockReport := new(mockMetadataReport)
+       defer mockReport.AssertExpectations(t)
+       delegate := &DelegateMetadataReport{instance: mockReport}
+       t.Run("normal", func(t *testing.T) {
+               
mockReport.On("RemoveServiceAppMappingListener").Return(nil).Once()
+               err := 
delegate.RemoveServiceAppMappingListener("interfaceName", "group")
+               assert.Nil(t, err)
+       })
+       t.Run("error", func(t *testing.T) {
+               
mockReport.On("RemoveServiceAppMappingListener").Return(errors.New("mock 
error")).Once()
+               err := 
delegate.RemoveServiceAppMappingListener("interfaceName", "group")
+               assert.NotNil(t, err)
+       })
+}
+
+func TestGetMetadataReport(t *testing.T) {
+       instances = make(map[string]report.MetadataReport)
+       assert.Nil(t, GetMetadataReport())
+       instances["default"] = new(mockMetadataReport)
+       assert.NotNil(t, GetMetadataReport())
+}
+
+func TestGetMetadataReportByRegistry(t *testing.T) {
+       instances = make(map[string]report.MetadataReport)
+       assert.Nil(t, GetMetadataReportByRegistry("reg"))
+       instances["default"] = new(mockMetadataReport)
+       assert.NotNil(t, GetMetadataReportByRegistry("default"))
+       assert.NotNil(t, GetMetadataReportByRegistry("reg"))
+       assert.NotNil(t, GetMetadataReportByRegistry(""))
+}
+
+func TestGetMetadataReports(t *testing.T) {
+       instances = make(map[string]report.MetadataReport)
+       assert.True(t, len(GetMetadataReports()) == 0)
+       instances["default"] = new(mockMetadataReport)
+       assert.True(t, len(GetMetadataReports()) == 1)
+}
+
+func TestGetMetadataType(t *testing.T) {
+       assert.Equal(t, GetMetadataType(), constant.DefaultMetadataStorageType)
+       metadataOptions = &Options{}
+       assert.Equal(t, GetMetadataType(), constant.DefaultMetadataStorageType)
+       metadataOptions = &Options{
+               metadataType: constant.RemoteMetadataStorageType,
+       }
+       assert.Equal(t, GetMetadataType(), constant.RemoteMetadataStorageType)
+}
+
+func TestAddMetadataReport(t *testing.T) {
+       url := common.NewURLWithOptions(
+               common.WithProtocol("registryId"),
+       )
+       err := addMetadataReport("registryId", url)
+       assert.Nil(t, err)
+       assert.True(t, instances["registryId"] == nil)
+       mockReport := new(mockMetadataReport)
+       extension.SetMetadataReportFactory("registryId", func() 
report.MetadataReportFactory {
+               return mockReport
+       })
+       err = addMetadataReport("registryId", url)
+       assert.Nil(t, err)
+       assert.True(t, instances["registryId"] != nil)
+}
+
+type mockMetadataReport struct {
+       mock.Mock
+}
+
+func (m *mockMetadataReport) CreateMetadataReport(*common.URL) 
report.MetadataReport {
+       return m
+}
+
+func (m *mockMetadataReport) GetAppMetadata(string, string) 
(*info.MetadataInfo, error) {
+       args := m.Called()
+       return args.Get(0).(*info.MetadataInfo), args.Error(1)
+}
+
+func (m *mockMetadataReport) PublishAppMetadata(string, string, 
*info.MetadataInfo) error {
+       args := m.Called()
+       return args.Error(0)
+}
+
+func (m *mockMetadataReport) RegisterServiceAppMapping(string, string, string) 
error {
+       args := m.Called()
+       return args.Error(0)
+}
+
+func (m *mockMetadataReport) GetServiceAppMapping(string, string, 
mapping.MappingListener) (*gxset.HashSet, error) {
+       args := m.Called()
+       return args.Get(0).(*gxset.HashSet), args.Error(1)
+}
+
+func (m *mockMetadataReport) RemoveServiceAppMappingListener(string, string) 
error {
+       args := m.Called()
+       return args.Error(0)
+}
+
+type listener struct {
+}
+
+func (l *listener) OnEvent(e observer.Event) error {
+       return nil
+}
+
+func (l *listener) Stop() {
+}
diff --git a/options.go b/options.go
index b4bff1da2..5fd162db1 100644
--- a/options.go
+++ b/options.go
@@ -94,7 +94,6 @@ func (rc *InstanceOptions) init(opts ...InstanceOption) error 
{
                log.Infof("[Config Center] Config center doesn't start")
                log.Debugf("config center doesn't start because %s", err)
        } else {
-               compatInstanceOptions(rcCompat, rc)
                if err = rcCompat.Logger.Init(); err != nil { // init logger 
using config from config center again
                        return err
                }
@@ -110,15 +109,8 @@ func (rc *InstanceOptions) init(opts ...InstanceOption) 
error {
        }
 
        // init protocol
-       protocols := rcCompat.Protocols
-       if len(protocols) <= 0 {
-               protocol := &config.ProtocolConfig{}
-               protocols = make(map[string]*config.ProtocolConfig, 1)
-               protocols[constant.Dubbo] = protocol
-               rcCompat.Protocols = protocols
-       }
-       for _, protocol := range protocols {
-               if err := protocol.Init(); err != nil {
+       for _, protocolConfig := range rcCompat.Protocols {
+               if err := protocolConfig.Init(); err != nil {
                        return err
                }
        }
@@ -169,6 +161,7 @@ func (rc *InstanceOptions) init(opts ...InstanceOption) 
error {
                return err
        }
 
+       compatInstanceOptions(rcCompat, rc) // overrider options config because 
some config are changed after init
        return nil
 }
 
diff --git 
a/registry/servicediscovery/customizer/service_instance_host_port_customizer.go 
b/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
index f33023f45..bd918d6ac 100644
--- 
a/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
+++ 
b/registry/servicediscovery/customizer/service_instance_host_port_customizer.go
@@ -39,7 +39,7 @@ func (e *hostPortCustomizer) GetPriority() int {
 
 // Customize calculate the revision for exported urls and then put it into 
instance metadata
 func (e *hostPortCustomizer) Customize(instance registry.ServiceInstance) {
-       if instance.GetPort() > 0 {
+       if instance.GetPort() > 0 { // has set, avoid reset
                return
        }
        if instance.GetServiceMetadata() == nil || 
len(instance.GetServiceMetadata().GetExportedServiceURLs()) == 0 {

Reply via email to