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 1141fe7cc test: Add unit tests for graceful_shutdown package (#3128)
1141fe7cc is described below
commit 1141fe7ccb0446e8e89bd02af912450f73b2da3d
Author: Oxidaner <[email protected]>
AuthorDate: Mon Dec 22 07:41:01 2025 +0800
test: Add unit tests for graceful_shutdown package (#3128)
* Add unit tests for graceful_shutdown package
---
graceful_shutdown/options_test.go | 102 ++++++++++++++++++++
graceful_shutdown/shutdown_test.go | 192 +++++++++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+)
diff --git a/graceful_shutdown/options_test.go
b/graceful_shutdown/options_test.go
new file mode 100644
index 000000000..f5a262cec
--- /dev/null
+++ b/graceful_shutdown/options_test.go
@@ -0,0 +1,102 @@
+/*
+ * 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 graceful_shutdown
+
+import (
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/global"
+)
+
+func TestDefaultOptions(t *testing.T) {
+ opts := defaultOptions()
+ assert.NotNil(t, opts)
+ assert.NotNil(t, opts.Shutdown)
+ assert.Equal(t, "60s", opts.Shutdown.Timeout)
+ assert.Equal(t, "3s", opts.Shutdown.StepTimeout)
+ assert.Equal(t, "3s", opts.Shutdown.ConsumerUpdateWaitTime)
+ assert.Equal(t, "", opts.Shutdown.OfflineRequestWindowTimeout) // No
default value
+ assert.True(t, *opts.Shutdown.InternalSignal)
+}
+
+func TestNewOptions(t *testing.T) {
+ // Test with default options
+ opts := NewOptions()
+ assert.NotNil(t, opts)
+ assert.NotNil(t, opts.Shutdown)
+
+ // Test with custom options
+ customTimeout := 120 * time.Second
+ customStepTimeout := 10 * time.Second
+ customConsumerUpdateWaitTime := 5 * time.Second
+ customOfflineRequestWindowTimeout := 2 * time.Second
+
+ opts = NewOptions(
+ WithTimeout(customTimeout),
+ WithStepTimeout(customStepTimeout),
+ WithConsumerUpdateWaitTime(customConsumerUpdateWaitTime),
+
WithOfflineRequestWindowTimeout(customOfflineRequestWindowTimeout),
+ WithoutInternalSignal(),
+ )
+
+ assert.Equal(t, customTimeout.String(), opts.Shutdown.Timeout)
+ assert.Equal(t, customStepTimeout.String(), opts.Shutdown.StepTimeout)
+ assert.Equal(t, customConsumerUpdateWaitTime.String(),
opts.Shutdown.ConsumerUpdateWaitTime)
+ assert.Equal(t, customOfflineRequestWindowTimeout.String(),
opts.Shutdown.OfflineRequestWindowTimeout)
+ assert.False(t, *opts.Shutdown.InternalSignal)
+}
+
+func TestOptionFunctions(t *testing.T) {
+ // Test WithTimeout
+ opts := defaultOptions()
+ WithTimeout(120 * time.Second)(opts)
+ assert.Equal(t, "2m0s", opts.Shutdown.Timeout)
+
+ // Test WithStepTimeout
+ WithStepTimeout(5 * time.Second)(opts)
+ assert.Equal(t, "5s", opts.Shutdown.StepTimeout)
+
+ // Test WithConsumerUpdateWaitTime
+ WithConsumerUpdateWaitTime(10 * time.Second)(opts)
+ assert.Equal(t, "10s", opts.Shutdown.ConsumerUpdateWaitTime)
+
+ // Test WithOfflineRequestWindowTimeout
+ WithOfflineRequestWindowTimeout(2 * time.Second)(opts)
+ assert.Equal(t, "2s", opts.Shutdown.OfflineRequestWindowTimeout)
+
+ // Test WithoutInternalSignal
+ WithoutInternalSignal()(opts)
+ assert.False(t, *opts.Shutdown.InternalSignal)
+
+ // Test WithRejectRequest
+ WithRejectRequest()(opts)
+ assert.True(t, opts.Shutdown.RejectRequest.Load())
+
+ // Test SetShutdownConfig
+ customConfig := global.DefaultShutdownConfig()
+ customConfig.Timeout = "300s"
+ SetShutdownConfig(customConfig)(opts)
+ assert.Equal(t, customConfig, opts.Shutdown)
+}
diff --git a/graceful_shutdown/shutdown_test.go
b/graceful_shutdown/shutdown_test.go
new file mode 100644
index 000000000..8aa2e665e
--- /dev/null
+++ b/graceful_shutdown/shutdown_test.go
@@ -0,0 +1,192 @@
+/*
+ * 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 graceful_shutdown
+
+import (
+ "context"
+ "sync"
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+)
+
+import (
+ "dubbo.apache.org/dubbo-go/v3/common/constant"
+ "dubbo.apache.org/dubbo-go/v3/common/extension"
+ "dubbo.apache.org/dubbo-go/v3/filter"
+ "dubbo.apache.org/dubbo-go/v3/global"
+ "dubbo.apache.org/dubbo-go/v3/protocol/base"
+ "dubbo.apache.org/dubbo-go/v3/protocol/result"
+)
+
+// MockFilter implements filter.Filter and config.Setter for testing
+type MockFilter struct {
+ mock.Mock
+}
+
+func (m *MockFilter) Set(key string, value any) {
+ m.Called(key, value)
+}
+
+func (m *MockFilter) Invoke(ctx context.Context, invoker base.Invoker,
invocation base.Invocation) result.Result {
+ return nil
+}
+
+func (m *MockFilter) OnResponse(ctx context.Context, result result.Result,
invoker base.Invoker, invocation base.Invocation) result.Result {
+ return nil
+}
+
+func TestInit(t *testing.T) {
+ // Reset initOnce and protocols for testing
+ initOnce = sync.Once{}
+ protocols = nil
+ proMu = sync.Mutex{}
+
+ // Register mock filters
+ mockConsumerFilter := &MockFilter{}
+ mockProviderFilter := &MockFilter{}
+
+ // Expect Set method calls
+ mockConsumerFilter.On("Set", mock.Anything, mock.Anything).Return()
+ mockProviderFilter.On("Set", mock.Anything, mock.Anything).Return()
+
+ // Register mock filters
+ extension.SetFilter(constant.GracefulShutdownConsumerFilterKey, func()
filter.Filter {
+ return mockConsumerFilter
+ })
+ extension.SetFilter(constant.GracefulShutdownProviderFilterKey, func()
filter.Filter {
+ return mockProviderFilter
+ })
+
+ // Test with default options
+ Init()
+
+ // Test with custom options
+ customTimeout := 120 * time.Second
+ Init(WithTimeout(customTimeout))
+
+ // Remove mock filters
+ extension.UnregisterFilter(constant.GracefulShutdownConsumerFilterKey)
+ extension.UnregisterFilter(constant.GracefulShutdownProviderFilterKey)
+}
+
+func TestRegisterProtocol(t *testing.T) {
+ // Reset protocols for testing
+ protocols = make(map[string]struct{})
+ proMu = sync.Mutex{}
+
+ // Register some protocols
+ RegisterProtocol("dubbo")
+ RegisterProtocol("rest")
+ RegisterProtocol("tri")
+
+ // Check if protocols are registered correctly
+ proMu.Lock()
+ defer proMu.Unlock()
+
+ assert.Contains(t, protocols, "dubbo")
+ assert.Contains(t, protocols, "rest")
+ assert.Contains(t, protocols, "tri")
+ assert.Len(t, protocols, 3)
+}
+
+func TestTotalTimeout(t *testing.T) {
+ // Test with default timeout
+ config := global.DefaultShutdownConfig()
+ timeout := totalTimeout(config)
+ assert.Equal(t, defaultTimeout, timeout)
+
+ // Test with custom timeout
+ config.Timeout = "120s"
+ timeout = totalTimeout(config)
+ assert.Equal(t, 120*time.Second, timeout)
+
+ // Test with invalid timeout
+ config.Timeout = "invalid"
+ timeout = totalTimeout(config)
+ assert.Equal(t, defaultTimeout, timeout)
+
+ // Test with timeout less than default
+ config.Timeout = "30s"
+ timeout = totalTimeout(config)
+ assert.Equal(t, defaultTimeout, timeout) // Should use default if less
than default
+}
+
+func TestParseDuration(t *testing.T) {
+ // Test with valid duration
+ res := parseDuration("10s", "test", 5*time.Second)
+ assert.Equal(t, 10*time.Second, res)
+
+ // Test with invalid duration
+ res = parseDuration("invalid", "test", 5*time.Second)
+ assert.Equal(t, 5*time.Second, res)
+
+ // Test with empty string
+ res = parseDuration("", "test", 5*time.Second)
+ assert.Equal(t, 5*time.Second, res)
+}
+
+func TestWaitAndAcceptNewRequests(t *testing.T) {
+ // Test with positive step timeout
+ config := global.DefaultShutdownConfig()
+ config.StepTimeout = "100ms"
+ config.ProviderActiveCount.Store(0)
+
+ start := time.Now()
+ waitAndAcceptNewRequests(config)
+ elapsed := time.Since(start)
+
+ // Should wait for ConsumerUpdateWaitTime (default 3s) plus a little
extra for processing
+ assert.GreaterOrEqual(t, elapsed, 3*time.Second)
+
+ // Test with negative step timeout (should skip waiting)
+ config.StepTimeout = "-1s"
+ start = time.Now()
+ waitAndAcceptNewRequests(config)
+ elapsed = time.Since(start)
+
+ // Should only wait for ConsumerUpdateWaitTime
+ assert.Less(t, elapsed, 3*time.Second+100*time.Millisecond)
+}
+
+func TestWaitForSendingAndReceivingRequests(t *testing.T) {
+ // Test with active consumer requests
+ config := global.DefaultShutdownConfig()
+ config.StepTimeout = "100ms"
+ config.ConsumerActiveCount.Store(1)
+
+ start := time.Now()
+ waitForSendingAndReceivingRequests(config)
+ elapsed := time.Since(start)
+
+ // Should wait for step timeout
+ assert.GreaterOrEqual(t, elapsed, 100*time.Millisecond)
+
+ // Test with no active consumer requests
+ config.ConsumerActiveCount.Store(0)
+ start = time.Now()
+ waitForSendingAndReceivingRequests(config)
+ elapsed = time.Since(start)
+
+ // Should return immediately
+ assert.Less(t, elapsed, 50*time.Millisecond)
+}