This is an automated email from the ASF dual-hosted git repository.
xuetaoli pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
The following commit(s) were added to refs/heads/develop by this push:
new 7d7b84d7a test: add unit tests for service discovery registry (#3085)
7d7b84d7a is described below
commit 7d7b84d7a52af6f661703db25e26681e717f5358
Author: yxrxy <[email protected]>
AuthorDate: Mon Dec 1 11:23:38 2025 +0800
test: add unit tests for service discovery registry (#3085)
* test: add unit tests for service discovery registry
---
.../servicediscovery/service_discovery_registry.go | 4 +-
.../service_discovery_registry_test.go | 458 ++++++++++++---------
2 files changed, 265 insertions(+), 197 deletions(-)
diff --git a/registry/servicediscovery/service_discovery_registry.go
b/registry/servicediscovery/service_discovery_registry.go
index 3e5c50ecd..2fe06d293 100644
--- a/registry/servicediscovery/service_discovery_registry.go
+++ b/registry/servicediscovery/service_discovery_registry.go
@@ -154,7 +154,9 @@ func (s *serviceDiscoveryRegistry) UnSubscribe(url
*common.URL, listener registr
// FIXME ServiceNames.String() is not good
serviceNamesKey := services.String()
l := s.serviceListeners[serviceNamesKey]
- l.RemoveListener(url.ServiceKey())
+ if l != nil {
+ l.RemoveListener(url.ServiceKey())
+ }
s.stopListen(url)
err := s.serviceNameMapping.Remove(url)
if err != nil {
diff --git a/registry/servicediscovery/service_discovery_registry_test.go
b/registry/servicediscovery/service_discovery_registry_test.go
index 29b0e6f98..c466c0429 100644
--- a/registry/servicediscovery/service_discovery_registry_test.go
+++ b/registry/servicediscovery/service_discovery_registry_test.go
@@ -17,199 +17,265 @@
package servicediscovery
-//import (
-// "testing"
-//)
-//
-//import (
-// gxset "github.com/dubbogo/gost/container/set"
-// "github.com/dubbogo/gost/hash/page"
-// "github.com/stretchr/testify/assert"
-//)
-//
-//import (
-// "dubbo.apache.org/dubbo-go/v3/common"
-// "dubbo.apache.org/dubbo-go/v3/common/extension"
-// "dubbo.apache.org/dubbo-go/v3/config"
-// "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
-// "dubbo.apache.org/dubbo-go/v3/metadata/service"
-// "dubbo.apache.org/dubbo-go/v3/registry"
-//)
-//
-//var (
-// serviceInterface = "org.apache.dubbo.metadata.MetadataService"
-// group = "dubbo-provider"
-// version = "1.0.0"
-//)
-//
-//func TestServiceDiscoveryRegistry_Register(t *testing.T) {
-// config.GetApplicationConfig().MetadataType = "mock"
-// extension.SetLocalMetadataService("mock", func() (service
service.MetadataService, err error) {
-// service = &mockMetadataService{}
-// return
-// })
-//
-// extension.SetServiceDiscovery("mock", func(name string) (discovery
registry.ServiceDiscovery, err error) {
-// return &mockServiceDiscovery{}, nil
-// })
-//
-// extension.SetGlobalServiceNameMapping(func() mapping.ServiceNameMapping
{
-// return mapping.NewMockServiceNameMapping()
-// })
-//
-// config.GetBaseConfig().ServiceDiscoveries["mock"] =
&config.ServiceDiscoveryConfig{
-// Protocol: "mock",
-// }
-// registryURL, _ := common.NewURL("service-discovery://localhost:12345",
-// common.WithParamsValue("service_discovery", "mock"),
-// common.WithParamsValue("subscribed-services", "a, b , c,d,e ,"))
-// url, _ := common.NewURL("dubbo://192.168.0.102:20880/" +
serviceInterface +
-// "?&application=" + group +
-// "&interface=" + serviceInterface +
-// "&group=" + group +
-// "&version=" + version +
-// "&service_discovery=mock" +
-//
"&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs"
+
-// "&side=provider")
-// registry, err := newServiceDiscoveryRegistry(registryURL)
-// assert.Nil(t, err)
-// assert.NotNil(t, registry)
-// err = registry.Register(url)
-// assert.NoError(t, err)
-//}
-//
-//type mockServiceDiscovery struct{}
-//
-//func (m *mockServiceDiscovery) String() string {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) Destroy() error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) Register(registry.ServiceInstance) error {
-// return nil
-//}
-//
-//func (m *mockServiceDiscovery) Update(registry.ServiceInstance) error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) Unregister(registry.ServiceInstance) error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetDefaultPageSize() int {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetServices() *gxset.HashSet {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetInstances(string)
[]registry.ServiceInstance {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int)
gxpage.Pager {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int,
bool) gxpage.Pager {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int)
map[string]gxpage.Pager {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery)
AddListener(registry.ServiceInstancesChangedListener) error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) DispatchEventByServiceName(string) error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery) DispatchEventForInstances(string,
[]registry.ServiceInstance) error {
-// panic("implement me")
-//}
-//
-//func (m *mockServiceDiscovery)
DispatchEvent(*registry.ServiceInstancesChangedEvent) error {
-// panic("implement me")
-//}
-//
-//type mockMetadataService struct{}
-//
-//func (m *mockMetadataService) GetExportedURLs(string, string, string,
string) ([]*common.URL, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetMetadataInfo(revision string)
(*common.MetadataInfo, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetExportedServiceURLs() []*common.URL {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetMetadataServiceURL() *common.URL {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) SetMetadataServiceURL(url *common.URL) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) Reference() string {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) ServiceName() (string, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) ExportURL(*common.URL) (bool, error) {
-// return true, nil
-//}
-//
-//func (m *mockMetadataService) UnexportURL(*common.URL) error {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) UnsubscribeURL(*common.URL) error {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error {
-// return nil
-//}
-//
-//func (m *mockMetadataService) MethodMapper() map[string]string {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetServiceDefinition(string, string, string)
(string, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string)
(string, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) {
-// panic("implement me")
-//}
-//
-//func (m *mockMetadataService) Version() (string, error) {
-// panic("implement me")
-//}
+import (
+ "context"
+ "fmt"
+ "sync"
+ "testing"
+ "time"
+)
+
+import (
+ gxset "github.com/dubbogo/gost/container/set"
+ gxpage "github.com/dubbogo/gost/hash/page"
+
+ "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/global"
+ "dubbo.apache.org/dubbo-go/v3/metadata"
+ "dubbo.apache.org/dubbo-go/v3/metadata/mapping"
+ "dubbo.apache.org/dubbo-go/v3/protocol"
+ "dubbo.apache.org/dubbo-go/v3/protocol/base"
+ "dubbo.apache.org/dubbo-go/v3/proxy"
+ "dubbo.apache.org/dubbo-go/v3/registry"
+)
+
+const (
+ testInterface = "org.apache.dubbo.test.TestService"
+ testGroup = "test-group"
+ testApp = "test-app"
+ testRegistryURL = "service-discovery://localhost:12345"
+)
+
+// TestServiceDiscoveryRegistryRegister verifies the registration process.
+func TestServiceDiscoveryRegistryRegister(t *testing.T) {
+ mockSD, mockMapping := setupEnvironment(t)
+ regID := fmt.Sprintf("mock-reg-%d", time.Now().UnixNano())
+
+ registryURL, err := common.NewURL(testRegistryURL,
+ common.WithParamsValue(constant.RegistryKey, "mock"),
+ common.WithParamsValue(constant.RegistryIdKey, regID))
+ assert.NoError(t, err)
+
+ reg, err := newServiceDiscoveryRegistry(registryURL)
+ assert.NoError(t, err)
+
+ providerURL, _ := common.NewURL("dubbo://127.0.0.1:20880/",
+ common.WithParamsValue(constant.ApplicationKey, testApp),
+ common.WithInterface(testInterface),
+ common.WithParamsValue(constant.SideKey, constant.SideProvider),
+ )
+
+ // Interface Mapping
+ err = reg.Register(providerURL)
+ assert.NoError(t, err)
+ assert.True(t, mockMapping.mapCalled, "ServiceNameMapping.Map should be
called")
+
+ // Instance Registration
+ sdReg, ok := reg.(*serviceDiscoveryRegistry)
+ assert.True(t, ok)
+
+ err = sdReg.RegisterService()
+ assert.NoError(t, err)
+
+ assert.True(t, mockSD.registerCalled, "ServiceDiscovery.Register should
be called")
+
+ if mockSD.capturedInstance != nil {
+ assert.Equal(t, testApp,
mockSD.capturedInstance.GetServiceName())
+ assert.Equal(t, "127.0.0.1", mockSD.capturedInstance.GetHost())
+ assert.Equal(t, 20880, mockSD.capturedInstance.GetPort())
+ assert.Equal(t, "mock",
mockSD.capturedInstance.GetMetadata()[constant.MetadataStorageTypePropertyName])
+ }
+}
+
+// TestServiceDiscoveryRegistrySubscribe verifies the subscription flow.
+func TestServiceDiscoveryRegistrySubscribe(t *testing.T) {
+ mockSD, mockMapping := setupEnvironment(t)
+ mockMapping.data[testInterface] = gxset.NewSet(testApp)
+
+ registryURL, _ := common.NewURL(testRegistryURL,
+ common.WithParamsValue(constant.RegistryKey, "mock"))
+
+ reg, err := newServiceDiscoveryRegistry(registryURL)
+ assert.NoError(t, err)
+
+ consumerURL, _ := common.NewURL("dubbo://127.0.0.1:20000/",
+ common.WithInterface(testInterface),
+ common.WithParamsValue(constant.GroupKey, testGroup),
+ common.WithParamsValue(constant.SideKey, constant.SideConsumer),
+ )
+
+ mockSD.wg.Add(1)
+ err = reg.Subscribe(consumerURL, &mockNotifyListener{})
+ assert.NoError(t, err)
+
+ assert.True(t, mockMapping.getCalled)
+ assert.Equal(t, testApp, mockSD.capturedAppName)
+
+ mockSD.wg.Wait()
+ assert.True(t, mockSD.listenerAdded)
+}
+
+// TestServiceDiscoveryRegistryUnSubscribe verifies the unsubscription logic.
+func TestServiceDiscoveryRegistryUnSubscribe(t *testing.T) {
+ mockSD, mockMapping := setupEnvironment(t)
+ mockMapping.data[testInterface] = gxset.NewSet(testApp)
+
+ registryURL, _ := common.NewURL(testRegistryURL,
+ common.WithParamsValue(constant.RegistryKey, "mock"))
+
+ reg, err := newServiceDiscoveryRegistry(registryURL)
+ assert.NoError(t, err)
+
+ consumerURL, _ := common.NewURL("dubbo://127.0.0.1:20000/",
+ common.WithInterface(testInterface),
+ common.WithParamsValue(constant.SideKey, constant.SideConsumer),
+ )
+
+ mockSD.wg.Add(1)
+ _ = reg.Subscribe(consumerURL, &mockNotifyListener{})
+ mockSD.wg.Wait()
+
+ err = reg.UnSubscribe(consumerURL, &mockNotifyListener{})
+ assert.NoError(t, err)
+ assert.True(t, mockMapping.removeCalled)
+}
+
+// setupEnvironment initializes the test environment.
+func setupEnvironment(t *testing.T) (*mockServiceDiscovery,
*mockServiceNameMapping) {
+ appConfig := global.DefaultApplicationConfig()
+ appConfig.Name = testApp
+
+ mockSD := &mockServiceDiscovery{}
+ mockMapping := &mockServiceNameMapping{data:
make(map[string]*gxset.HashSet)}
+
+ extension.SetProtocol("dubbo", func() protocol.Protocol { return
&mockProtocol{} })
+ extension.SetProxyFactory("default", func(options ...proxy.Option)
proxy.ProxyFactory { return &mockProxyFactory{} })
+ extension.SetRegistry("service-discovery", newServiceDiscoveryRegistry)
+ extension.SetServiceDiscovery("mock", func(url *common.URL)
(registry.ServiceDiscovery, error) {
+ return mockSD, nil
+ })
+ extension.SetGlobalServiceNameMapping(func() mapping.ServiceNameMapping
{
+ return mockMapping
+ })
+
+ opts := metadata.NewOptions(metadata.WithMetadataType("mock"))
+ _ = opts.Init()
+ return mockSD, mockMapping
+}
+
+type mockServiceDiscovery struct {
+ wg sync.WaitGroup
+ registerCalled bool
+ listenerAdded bool
+ capturedAppName string
+ capturedInstance registry.ServiceInstance
+}
+
+func (m *mockServiceDiscovery) String() string { return "mock" }
+func (m *mockServiceDiscovery) Destroy() error { return nil }
+func (m *mockServiceDiscovery) Register(inst registry.ServiceInstance) error {
+ m.registerCalled = true
+ m.capturedInstance = inst
+ return nil
+}
+func (m *mockServiceDiscovery) Update(inst registry.ServiceInstance) error
{ return nil }
+func (m *mockServiceDiscovery) Unregister(inst registry.ServiceInstance) error
{ return nil }
+func (m *mockServiceDiscovery) GetDefaultPageSize() int
{ return 10 }
+func (m *mockServiceDiscovery) GetServices() *gxset.HashSet
{ return gxset.NewSet("mock-service") }
+func (m *mockServiceDiscovery) GetInstances(name string)
[]registry.ServiceInstance {
+ m.capturedAppName = name
+ return []registry.ServiceInstance{}
+}
+func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int)
gxpage.Pager { return nil }
+func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int,
bool) gxpage.Pager {
+ return nil
+}
+func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int)
map[string]gxpage.Pager {
+ return nil
+}
+func (m *mockServiceDiscovery)
AddListener(registry.ServiceInstancesChangedListener) error {
+ defer m.wg.Done()
+ m.listenerAdded = true
+ return nil
+}
+
+type mockServiceNameMapping struct {
+ data map[string]*gxset.HashSet
+ mapCalled bool
+ getCalled bool
+ removeCalled bool
+ capturedGroup string
+}
+
+func (m *mockServiceNameMapping) Map(url *common.URL) error {
+ m.mapCalled = true
+ serviceInterface := url.GetParam(constant.InterfaceKey, "")
+ appName := url.GetParam(constant.ApplicationKey, "")
+ m.data[serviceInterface] = gxset.NewSet(appName)
+ return nil
+}
+func (m *mockServiceNameMapping) Get(url *common.URL, _
mapping.MappingListener) (*gxset.HashSet, error) {
+ m.getCalled = true
+ m.capturedGroup = url.GetParam(constant.GroupKey, "")
+ serviceInterface := url.GetParam(constant.InterfaceKey, "")
+ if s, ok := m.data[serviceInterface]; ok {
+ return s, nil
+ }
+ return gxset.NewSet(), nil
+}
+func (m *mockServiceNameMapping) Remove(url *common.URL) error {
m.removeCalled = true; return nil }
+
+type mockNotifyListener struct{}
+
+func (m *mockNotifyListener) Notify(*registry.ServiceEvent) {
+ // for mocking
+}
+func (m *mockNotifyListener) NotifyAll([]*registry.ServiceEvent, func()) {
+ // for mocking
+}
+
+type mockProtocol struct{}
+
+func (m *mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
return &mockExporter{} }
+func (m *mockProtocol) Refer(url *common.URL) protocol.Invoker {
return &mockInvoker{} }
+func (m *mockProtocol) Destroy() {
+ // for mocking
+}
+
+type mockExporter struct{}
+
+func (m *mockExporter) UnExport() {
+ // for mocking
+}
+
+func (m *mockExporter) Unexport() {
+ // for mocking
+}
+func (m *mockExporter) GetInvoker() protocol.Invoker { return &mockInvoker{} }
+
+type mockProxyFactory struct{}
+
+func (m *mockProxyFactory) GetProxy(invoker base.Invoker, url *common.URL)
*proxy.Proxy {
+ return &proxy.Proxy{}
+}
+
+func (m *mockProxyFactory) GetAsyncProxy(invoker base.Invoker, callBack any,
url *common.URL) *proxy.Proxy {
+ return &proxy.Proxy{}
+}
+
+func (m *mockProxyFactory) GetInvoker(url *common.URL) protocol.Invoker {
return &mockInvoker{} }
+
+type mockInvoker struct{}
+
+func (m *mockInvoker) GetURL() *common.URL { return nil }
+func (m *mockInvoker) IsAvailable() bool { return true }
+func (m *mockInvoker) Destroy() {
+ // for mocking
+}
+func (m *mockInvoker) Invoke(context.Context, protocol.Invocation)
protocol.Result { return nil }