You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2025/12/21 23:40:21 UTC

(dubbo-go) branch develop updated: test: add unit test for server/server (#3129)

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

alexstocks 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 1563aea3f test: add unit test for server/server (#3129)
1563aea3f is described below

commit 1563aea3f13591535c8ac36ad65de959e07d7839
Author: Akashisang <15...@users.noreply.github.com>
AuthorDate: Mon Dec 22 07:40:16 2025 +0800

    test: add unit test for server/server (#3129)
    
    * test: add unit test for server/server
---
 server/server_test.go | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 481 insertions(+)

diff --git a/server/server_test.go b/server/server_test.go
new file mode 100644
index 000000000..57555e343
--- /dev/null
+++ b/server/server_test.go
@@ -0,0 +1,481 @@
+/*
+ * 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 server
+
+import (
+	"strconv"
+	"sync"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common"
+	"dubbo.apache.org/dubbo-go/v3/global"
+)
+
+// Test NewServer creates a server successfully
+func TestNewServer(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+	assert.NotNil(t, srv)
+
+	// Verify server is properly initialized by using public API
+	// Try to register and retrieve a service to verify internal maps work
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "init-test-service"
+	srv.registerServiceOptions(svcOpts)
+
+	retrieved := srv.GetServiceOptions("init-test-service")
+	assert.NotNil(t, retrieved, "Server should have properly initialized service maps")
+}
+
+// Test NewServer with options
+func TestNewServerWithOptions(t *testing.T) {
+	appCfg := &global.ApplicationConfig{
+		Name: "test-app",
+	}
+	srv, err := NewServer(
+		SetServerApplication(appCfg),
+		WithServerGroup("test-group"),
+	)
+	assert.NoError(t, err)
+	assert.NotNil(t, srv)
+
+	// Verify configuration by registering a service and checking its options
+	handler := &mockServerRPCService{}
+	err = srv.Register(handler, nil)
+	assert.NoError(t, err)
+
+	svcOpts := srv.GetServiceOptions(handler.Reference())
+	assert.NotNil(t, svcOpts)
+	assert.Equal(t, "test-group", svcOpts.Service.Group)
+}
+
+// Test GetServiceOptions
+func TestGetServiceOptions(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	srv.registerServiceOptions(svcOpts)
+
+	retrieved := srv.GetServiceOptions("test-service")
+	assert.NotNil(t, retrieved)
+	assert.Equal(t, "test-service", retrieved.Id)
+}
+
+// Test GetServiceOptions returns nil for non-existent service
+func TestGetServiceOptionsNotFound(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	retrieved := srv.GetServiceOptions("non-existent")
+	assert.Nil(t, retrieved)
+}
+
+// Test GetServiceInfo
+func TestGetServiceInfo(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	svcOpts.info = &common.ServiceInfo{
+		InterfaceName: "com.example.TestService",
+	}
+	srv.registerServiceOptions(svcOpts)
+
+	info := srv.GetServiceInfo("test-service")
+	assert.NotNil(t, info)
+	assert.Equal(t, "com.example.TestService", info.InterfaceName)
+}
+
+// Test GetServiceInfo returns nil for non-existent service
+func TestGetServiceInfoNotFound(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	info := srv.GetServiceInfo("non-existent")
+	assert.Nil(t, info)
+}
+
+// Test GetRPCService
+func TestGetRPCService(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	mockService := &mockServerRPCService{}
+	svcOpts.rpcService = mockService
+	srv.registerServiceOptions(svcOpts)
+
+	rpcService := srv.GetRPCService("test-service")
+	assert.NotNil(t, rpcService)
+	assert.Equal(t, mockService, rpcService)
+}
+
+// Test GetRPCService returns nil for non-existent service
+func TestGetRPCServiceNotFound(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	rpcService := srv.GetRPCService("non-existent")
+	assert.Nil(t, rpcService)
+}
+
+// Test GetServiceOptionsByInterfaceName
+func TestGetServiceOptionsByInterfaceName(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	svcOpts.Service.Interface = "com.example.TestService"
+	srv.registerServiceOptions(svcOpts)
+
+	retrieved := srv.GetServiceOptionsByInterfaceName("com.example.TestService")
+	assert.NotNil(t, retrieved)
+	assert.Equal(t, "test-service", retrieved.Id)
+}
+
+// Test GetServiceOptionsByInterfaceName returns nil for non-existent interface
+func TestGetServiceOptionsByInterfaceNameNotFound(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	retrieved := srv.GetServiceOptionsByInterfaceName("non.existent.Service")
+	assert.Nil(t, retrieved)
+}
+
+// Test registerServiceOptions
+func TestRegisterServiceOptions(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	svcOpts.Service.Interface = "com.example.TestService"
+
+	srv.registerServiceOptions(svcOpts)
+
+	// Verify via public API methods
+	retrieved := srv.GetServiceOptions("test-service")
+	assert.NotNil(t, retrieved)
+	assert.Equal(t, svcOpts, retrieved)
+
+	retrievedByInterface := srv.GetServiceOptionsByInterfaceName("com.example.TestService")
+	assert.NotNil(t, retrievedByInterface)
+	assert.Equal(t, svcOpts, retrievedByInterface)
+}
+
+// Test registerServiceOptions with empty interface
+func TestRegisterServiceOptionsEmptyInterface(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	svcOpts := defaultServiceOptions()
+	svcOpts.Id = "test-service"
+	svcOpts.Service.Interface = ""
+
+	srv.registerServiceOptions(svcOpts)
+
+	// Should be retrievable by service ID but not by interface name
+	retrieved := srv.GetServiceOptions("test-service")
+	assert.NotNil(t, retrieved)
+	assert.Equal(t, svcOpts, retrieved)
+
+	// Empty interface should not be retrievable by interface name
+	retrievedByInterface := srv.GetServiceOptionsByInterfaceName("")
+	assert.Nil(t, retrievedByInterface)
+}
+
+// Test SetProviderServices
+func TestSetProviderServices(t *testing.T) {
+	// Lock and backup original state
+	internalProLock.Lock()
+	originalServices := internalProServices
+	internalProServices = make([]*InternalService, 0, 16)
+	internalProLock.Unlock()
+
+	// Register cleanup to restore original state
+	t.Cleanup(func() {
+		internalProLock.Lock()
+		defer internalProLock.Unlock()
+		internalProServices = originalServices
+	})
+
+	internalService := &InternalService{
+		Name:     "test-internal-service",
+		Priority: 0,
+		Init: func(options *ServiceOptions) (*ServiceDefinition, bool) {
+			return &ServiceDefinition{
+				Handler: &mockServerRPCService{},
+				Info:    nil,
+				Opts:    []ServiceOption{},
+			}, true
+		},
+	}
+
+	// This function will acquire the lock internally
+	SetProviderServices(internalService)
+
+	// Access the global variable safely with lock
+	internalProLock.Lock()
+	defer internalProLock.Unlock()
+	assert.Len(t, internalProServices, 1)
+	assert.Equal(t, "test-internal-service", internalProServices[0].Name)
+}
+
+// Test SetProviderServices with empty name
+func TestSetProviderServicesEmptyName(t *testing.T) {
+	// Lock and backup original state
+	internalProLock.Lock()
+	originalServices := internalProServices
+	internalProServices = make([]*InternalService, 0, 16)
+
+	// Unlock before calling SetProviderServices (which needs the lock)
+	internalProLock.Unlock()
+
+	// Register cleanup to restore original state
+	t.Cleanup(func() {
+		internalProLock.Lock()
+		defer internalProLock.Unlock()
+		internalProServices = originalServices
+	})
+
+	internalService := &InternalService{
+		Name: "",
+	}
+
+	// This function will acquire the lock internally
+	SetProviderServices(internalService)
+
+	// Access the global variable safely with lock
+	internalProLock.Lock()
+	defer internalProLock.Unlock()
+	assert.Len(t, internalProServices, 0)
+}
+
+// Test enhanceServiceInfo with nil info
+func TestEnhanceServiceInfoNil(t *testing.T) {
+	info := enhanceServiceInfo(nil)
+	assert.Nil(t, info)
+}
+
+// Test enhanceServiceInfo with methods
+func TestEnhanceServiceInfo(t *testing.T) {
+	info := &common.ServiceInfo{
+		InterfaceName: "com.example.Service",
+		Methods: []common.MethodInfo{
+			{
+				Name: "sayHello",
+			},
+		},
+	}
+
+	result := enhanceServiceInfo(info)
+	assert.NotNil(t, result)
+	// Should have doubled methods (original + case-swapped)
+	assert.Equal(t, 2, len(result.Methods))
+	assert.Equal(t, "sayHello", result.Methods[0].Name)
+	// The swapped version should have capitalized first letter
+	assert.Equal(t, "SayHello", result.Methods[1].Name)
+}
+
+// Test getMetadataPort with default protocol
+func TestGetMetadataPortWithDefaultProtocol(t *testing.T) {
+	opts := defaultServerOptions()
+	opts.Application.MetadataServicePort = ""
+	opts.Protocols = map[string]*global.ProtocolConfig{
+		"dubbo": {
+			Port: "20880",
+		},
+	}
+
+	port := getMetadataPort(opts)
+	assert.Equal(t, 20880, port)
+}
+
+// Test getMetadataPort with explicit port
+func TestGetMetadataPortExplicit(t *testing.T) {
+	opts := defaultServerOptions()
+	opts.Application.MetadataServicePort = "30880"
+
+	port := getMetadataPort(opts)
+	assert.Equal(t, 30880, port)
+}
+
+// Test getMetadataPort with no port
+func TestGetMetadataPortNoPort(t *testing.T) {
+	opts := defaultServerOptions()
+	opts.Application.MetadataServicePort = ""
+	opts.Protocols = map[string]*global.ProtocolConfig{}
+
+	port := getMetadataPort(opts)
+	assert.Equal(t, 0, port)
+}
+
+// Test getMetadataPort with invalid port
+func TestGetMetadataPortInvalid(t *testing.T) {
+	opts := defaultServerOptions()
+	opts.Application.MetadataServicePort = "invalid"
+
+	port := getMetadataPort(opts)
+	assert.Equal(t, 0, port)
+}
+
+// mockServerRPCService is a mock implementation for testing server_test.go only
+type mockServerRPCService struct{}
+
+func (m *mockServerRPCService) Invoke(methodName string, params []any, results []any) error {
+	return nil
+}
+
+func (m *mockServerRPCService) Reference() string {
+	return "com.example.MockService"
+}
+
+// Test concurrency: multiple goroutines registering services
+func TestConcurrentServiceRegistration(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	var wg sync.WaitGroup
+	for i := 0; i < 10; i++ {
+		wg.Add(1)
+		go func(idx int) {
+			defer wg.Done()
+			svcOpts := defaultServiceOptions()
+			svcOpts.Id = "service-" + strconv.Itoa(idx)
+			srv.registerServiceOptions(svcOpts)
+		}(i)
+	}
+
+	wg.Wait()
+
+	// Verify all services were registered using public API
+	for i := 0; i < 10; i++ {
+		svcID := "service-" + strconv.Itoa(i)
+		retrieved := srv.GetServiceOptions(svcID)
+		assert.NotNil(t, retrieved, "Service %s should be registered", svcID)
+		assert.Equal(t, svcID, retrieved.Id)
+	}
+}
+
+// Test Register with ServiceInfo
+func TestRegisterWithServiceInfo(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	handler := &mockServerRPCService{}
+	info := &common.ServiceInfo{
+		InterfaceName: "com.example.Service",
+		Methods: []common.MethodInfo{
+			{Name: "Method1"},
+		},
+	}
+
+	err = srv.Register(handler, info)
+	assert.NoError(t, err)
+
+	// Service should be registered
+	svcOpts := srv.GetServiceOptions(handler.Reference())
+	assert.NotNil(t, svcOpts)
+}
+
+// Test RegisterService without ServiceInfo
+func TestRegisterService(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	handler := &mockServerRPCService{}
+	err = srv.RegisterService(handler)
+	assert.NoError(t, err)
+
+	// Service should be registered with handler reference name
+	svcOpts := srv.GetServiceOptions(handler.Reference())
+	assert.NotNil(t, svcOpts)
+}
+
+// Test Register with handler that has method config
+func TestRegisterWithMethodConfig(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	handler := &mockServerRPCService{}
+	info := &common.ServiceInfo{
+		InterfaceName: "com.example.TestService",
+	}
+
+	err = srv.Register(
+		handler,
+		info,
+		WithInterface("com.example.TestService"),
+		WithGroup("test-group"),
+		WithVersion("1.0.0"),
+	)
+	assert.NoError(t, err)
+
+	svcOpts := srv.GetServiceOptions(handler.Reference())
+	assert.NotNil(t, svcOpts)
+	assert.Equal(t, "test-group", svcOpts.Service.Group)
+	assert.Equal(t, "1.0.0", svcOpts.Service.Version)
+}
+
+// Test genSvcOpts with missing server config
+func TestGenSvcOptsWithMissingServerConfig(t *testing.T) {
+	srv := &Server{
+		cfg: nil,
+	}
+
+	handler := &mockServerRPCService{}
+	_, err := srv.genSvcOpts(handler, nil)
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "has not been initialized")
+}
+
+// Test exportServices with empty service map
+func TestExportServicesEmpty(t *testing.T) {
+	srv, err := NewServer()
+	assert.NoError(t, err)
+
+	err = srv.exportServices()
+	assert.NoError(t, err)
+}
+
+// Test NewServer with custom group option
+func TestNewServerWithCustomGroup(t *testing.T) {
+	srv, err := NewServer(WithServerGroup("test"))
+	assert.NoError(t, err)
+	assert.NotNil(t, srv)
+
+	// Verify the group option by registering a service and checking its configuration
+	handler := &mockServerRPCService{}
+	err = srv.Register(handler, nil)
+	assert.NoError(t, err)
+
+	svcOpts := srv.GetServiceOptions(handler.Reference())
+	assert.NotNil(t, svcOpts)
+	assert.Equal(t, "test", svcOpts.Service.Group)
+}