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:52:55 UTC

(dubbo-go) branch develop updated: test: add test cases for `filter` (#3118)

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 95520ff25 test: add test cases for `filter` (#3118)
95520ff25 is described below

commit 95520ff257079bacc4ba443b06f88662d0810748
Author: Xuetao Li <m1...@163.com>
AuthorDate: Mon Dec 22 07:52:50 2025 +0800

    test: add test cases for `filter` (#3118)
    
    * test: add unit tests for ./filter/
---
 filter/access_key_test.go                        |  47 ++++++++
 filter/adaptivesvc/filter_test.go                | 130 +++++++++++++++++++++++
 filter/adaptivesvc/limiter/hill_climbing_test.go |  89 ++++++++++++++++
 filter/adaptivesvc/limiter/utils_test.go         |  44 ++++++++
 filter/adaptivesvc/limiter_mapper_test.go        |  71 +++++++++++++
 filter/generic/util_test.go                      |  92 ++++++++++++++++
 6 files changed, 473 insertions(+)

diff --git a/filter/access_key_test.go b/filter/access_key_test.go
new file mode 100644
index 000000000..01d47f471
--- /dev/null
+++ b/filter/access_key_test.go
@@ -0,0 +1,47 @@
+/*
+ * 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 filter
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+// Mock the AccessKeyStorage interface
+func TestAccessKeyPair(t *testing.T) {
+	// Test case 1: Test that AccessKeyPair can be properly instantiated and fields are correctly set
+	accessKeyPair := &AccessKeyPair{
+		AccessKey:    "test-access-key",
+		SecretKey:    "test-secret-key",
+		ConsumerSide: "consumer-side",
+		ProviderSide: "provider-side",
+		Creator:      "creator",
+		Options:      "options",
+	}
+
+	// Assert that the fields are correctly set
+	assert.Equal(t, "test-access-key", accessKeyPair.AccessKey)
+	assert.Equal(t, "test-secret-key", accessKeyPair.SecretKey)
+	assert.Equal(t, "consumer-side", accessKeyPair.ConsumerSide)
+	assert.Equal(t, "provider-side", accessKeyPair.ProviderSide)
+	assert.Equal(t, "creator", accessKeyPair.Creator)
+	assert.Equal(t, "options", accessKeyPair.Options)
+}
diff --git a/filter/adaptivesvc/filter_test.go b/filter/adaptivesvc/filter_test.go
new file mode 100644
index 000000000..c95f6dffd
--- /dev/null
+++ b/filter/adaptivesvc/filter_test.go
@@ -0,0 +1,130 @@
+/*
+ * 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 adaptivesvc
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+
+	"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/filter/adaptivesvc/limiter"
+	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
+	"dubbo.apache.org/dubbo-go/v3/protocol/mock"
+	"dubbo.apache.org/dubbo-go/v3/protocol/result"
+)
+
+type mockUpdater struct {
+	called bool
+}
+
+func (m *mockUpdater) DoUpdate() error {
+	m.called = true
+	return nil
+}
+
+func (m *mockUpdater) Report(_ uint64) {}
+
+func TestAdaptiveServiceProviderFilter_Invoke(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	u, _ := common.NewURL("dubbo://127.0.0.1:20000/com.test.Service")
+	methodName := "GetInfo"
+	filter := newAdaptiveServiceProviderFilter()
+
+	t.Run("AdaptiveDisabled", func(t *testing.T) {
+		invoc := invocation.NewRPCInvocation(methodName, nil, nil)
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(&result.RPCResult{Rest: "ok"})
+
+		res := filter.Invoke(context.Background(), invoker, invoc)
+		assert.Nil(t, res.Error())
+	})
+
+	t.Run("AdaptiveEnabled_AcquireSuccess", func(t *testing.T) {
+		invoc := invocation.NewRPCInvocation(methodName, nil, map[string]any{
+			constant.AdaptiveServiceEnabledKey: constant.AdaptiveServiceIsEnabled,
+		})
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().GetURL().Return(u).AnyTimes()
+		invoker.EXPECT().Invoke(gomock.Any(), gomock.Any()).Return(&result.RPCResult{Rest: "ok"})
+
+		res := filter.Invoke(context.Background(), invoker, invoc)
+		assert.Nil(t, res.Error())
+
+		updater, _ := invoc.GetAttribute(constant.AdaptiveServiceUpdaterKey)
+		assert.NotNil(t, updater)
+	})
+}
+
+func TestAdaptiveServiceProviderFilter_OnResponse(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	u, _ := common.NewURL("dubbo://127.0.0.1:20000/com.test.Service")
+	methodName := "GetInfo"
+	filter := newAdaptiveServiceProviderFilter()
+
+	t.Run("DisabledInResponse", func(t *testing.T) {
+		invoc := invocation.NewRPCInvocation(methodName, nil, nil)
+		res := &result.RPCResult{Rest: "ok"}
+		invoker := mock.NewMockInvoker(ctrl)
+
+		ret := filter.OnResponse(context.Background(), res, invoker, invoc)
+		assert.Equal(t, res, ret)
+	})
+
+	t.Run("InterruptedErrorShouldSkip", func(t *testing.T) {
+		invoc := invocation.NewRPCInvocation(methodName, nil, nil)
+		res := &result.RPCResult{Err: wrapErrAdaptiveSvcInterrupted("limit exceeded")}
+		invoker := mock.NewMockInvoker(ctrl)
+
+		ret := filter.OnResponse(context.Background(), res, invoker, invoc)
+		assert.True(t, isErrAdaptiveSvcInterrupted(ret.Error()))
+	})
+
+	t.Run("SuccessWithAttachments", func(t *testing.T) {
+		invoc := invocation.NewRPCInvocation(methodName, nil, nil)
+		updater := &mockUpdater{}
+		invoc.SetAttribute(constant.AdaptiveServiceUpdaterKey, updater)
+
+		res := &result.RPCResult{Rest: "ok"}
+		res.AddAttachment(constant.AdaptiveServiceEnabledKey, constant.AdaptiveServiceIsEnabled)
+
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().GetURL().Return(u).AnyTimes()
+
+		_, _ = limiterMapperSingleton.newAndSetMethodLimiter(u, methodName, limiter.HillClimbingLimiter)
+
+		ret := filter.OnResponse(context.Background(), res, invoker, invoc)
+
+		assert.Nil(t, ret.Error())
+		assert.NotEmpty(t, ret.Attachment(constant.AdaptiveServiceRemainingKey, ""))
+		assert.NotEmpty(t, ret.Attachment(constant.AdaptiveServiceInflightKey, ""))
+		assert.True(t, updater.called)
+	})
+}
diff --git a/filter/adaptivesvc/limiter/hill_climbing_test.go b/filter/adaptivesvc/limiter/hill_climbing_test.go
new file mode 100644
index 000000000..7ad75666c
--- /dev/null
+++ b/filter/adaptivesvc/limiter/hill_climbing_test.go
@@ -0,0 +1,89 @@
+/*
+ * 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 limiter
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+// Test for HillClimbing limiter's Acquire method
+func TestHillClimbing_Acquire(t *testing.T) {
+	limiter := NewHillClimbing().(*HillClimbing)
+
+	// Simulating that there is remaining capacity
+	limiter.limitation.Store(100)
+	limiter.inflight.Store(50)
+
+	updater, err := limiter.Acquire()
+	assert.NotNil(t, updater)
+	assert.NoError(t, err)
+
+	// Simulating no remaining capacity
+	limiter.limitation.Store(50)
+	limiter.inflight.Store(50)
+
+	updater, err = limiter.Acquire()
+	assert.Nil(t, updater)
+	assert.Error(t, err, ErrReachLimitation)
+}
+
+// Test the HillClimbingUpdater's DoUpdate method
+func TestHillClimbingUpdater_DoUpdate(t *testing.T) {
+	limiter := NewHillClimbing().(*HillClimbing)
+	updater := NewHillClimbingUpdater(limiter)
+
+	// Simulate the limiter update with arbitrary values for RTT and inflight
+	// Normally, this would adjust the limiter's limitation based on RTT and inflight metrics
+	err := updater.DoUpdate()
+	assert.NoError(t, err)
+}
+
+// Test adjustLimitation method with different options
+func TestHillClimbingUpdater_AdjustLimitation(t *testing.T) {
+	limiter := NewHillClimbing().(*HillClimbing)
+	updater := NewHillClimbingUpdater(limiter)
+
+	// Simulate a scenario where the limiter is set to extend its capacity
+	err := updater.adjustLimitation(HillClimbingOptionExtend)
+	assert.NoError(t, err)
+	assert.True(t, limiter.limitation.Load() > initialLimitation)
+
+	// Simulate a scenario where the limiter is set to shrink its capacity
+	err = updater.adjustLimitation(HillClimbingOptionShrink)
+	assert.NoError(t, err)
+	assert.True(t, limiter.limitation.Load() < initialLimitation)
+}
+
+// Test HillClimbing's Remaining capacity behavior
+func TestHillClimbing_Remaining(t *testing.T) {
+	limiter := NewHillClimbing().(*HillClimbing)
+	limiter.limitation.Store(100)
+	limiter.inflight.Store(30)
+
+	remaining := limiter.Remaining()
+	assert.Equal(t, uint64(70), remaining)
+
+	// Simulate that inflight requests exceed the limitation
+	limiter.inflight.Store(120)
+	remaining = limiter.Remaining()
+	assert.Equal(t, uint64(0), remaining)
+}
diff --git a/filter/adaptivesvc/limiter/utils_test.go b/filter/adaptivesvc/limiter/utils_test.go
new file mode 100644
index 000000000..483056487
--- /dev/null
+++ b/filter/adaptivesvc/limiter/utils_test.go
@@ -0,0 +1,44 @@
+/*
+ * 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 limiter
+
+import (
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMinDuration(t *testing.T) {
+	// Test when lhs is smaller than rhs
+	dur1 := 2 * time.Second
+	dur2 := 3 * time.Second
+	result := minDuration(dur1, dur2)
+	assert.Equal(t, dur1, result)
+
+	// Test when rhs is smaller than lhs
+	result = minDuration(dur2, dur1)
+	assert.Equal(t, dur1, result)
+
+	// Test when both durations are equal
+	dur3 := 2 * time.Second
+	result = minDuration(dur3, dur3)
+	assert.Equal(t, dur3, result)
+}
diff --git a/filter/adaptivesvc/limiter_mapper_test.go b/filter/adaptivesvc/limiter_mapper_test.go
new file mode 100644
index 000000000..13ef47f82
--- /dev/null
+++ b/filter/adaptivesvc/limiter_mapper_test.go
@@ -0,0 +1,71 @@
+/*
+ * 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 adaptivesvc
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"dubbo.apache.org/dubbo-go/v3/common"
+	"dubbo.apache.org/dubbo-go/v3/filter/adaptivesvc/limiter"
+)
+
+func TestLimiterMapper_newAndSetMethodLimiter(t *testing.T) {
+	// Initialize the limiterMapper and mock URL
+	mapper := newLimiterMapper()
+	url := &common.URL{Path: "/testService"}
+	methodName := "testMethod"
+
+	// Test creating a new limiter
+	l, err := mapper.newAndSetMethodLimiter(url, methodName, limiter.HillClimbingLimiter)
+	assert.NoError(t, err)
+	assert.NotNil(t, l)
+
+	// Test that the same limiter is returned if already created
+	l2, err := mapper.newAndSetMethodLimiter(url, methodName, limiter.HillClimbingLimiter)
+	assert.NoError(t, err)
+	assert.Same(t, l, l2)
+}
+
+func TestLimiterMapper_getMethodLimiter(t *testing.T) {
+	// Initialize the limiterMapper and mock URL
+	mapper := newLimiterMapper()
+	url := &common.URL{Path: "/testService"}
+	methodName := "testMethod"
+
+	// Add a limiter to the mapper
+	_, err := mapper.newAndSetMethodLimiter(url, methodName, limiter.HillClimbingLimiter)
+	assert.NoError(t, err)
+
+	// Test getting an existing limiter
+	l, err := mapper.getMethodLimiter(url, methodName)
+	assert.NoError(t, err)
+	assert.NotNil(t, l)
+
+	// Test getting a limiter that does not exist
+	url2 := &common.URL{Path: "/testService2"}
+	l, err = mapper.getMethodLimiter(url2, "nonExistentMethod")
+	assert.Error(t, err)
+	assert.Nil(t, l)
+	assert.Equal(t, ErrLimiterNotFoundOnMapper, err)
+}
diff --git a/filter/generic/util_test.go b/filter/generic/util_test.go
new file mode 100644
index 000000000..5fdc066c1
--- /dev/null
+++ b/filter/generic/util_test.go
@@ -0,0 +1,92 @@
+/*
+ * 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 generic
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+
+	"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/filter/generic/generalizer"
+	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
+	"dubbo.apache.org/dubbo-go/v3/protocol/mock"
+)
+
+func TestIsCallingToGenericService(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	u1, _ := common.NewURL("dubbo://127.0.0.1:20000/ComTest?generic=true")
+	invoker1 := mock.NewMockInvoker(ctrl)
+	invoker1.EXPECT().GetURL().Return(u1).AnyTimes()
+	invoc1 := invocation.NewRPCInvocation("GetUser", []any{"ID"}, nil)
+	assert.True(t, isCallingToGenericService(invoker1, invoc1))
+
+	u2, _ := common.NewURL("dubbo://127.0.0.1:20000/ComTest")
+	invoker2 := mock.NewMockInvoker(ctrl)
+	invoker2.EXPECT().GetURL().Return(u2).AnyTimes()
+	assert.False(t, isCallingToGenericService(invoker2, invoc1))
+
+	invoc3 := invocation.NewRPCInvocation(constant.Generic, []any{"m", "t", "a"}, nil)
+	assert.False(t, isCallingToGenericService(invoker1, invoc3))
+}
+
+func TestIsMakingAGenericCall(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	u, _ := common.NewURL("dubbo://127.0.0.1:20000/ComTest?generic=true")
+	invoker := mock.NewMockInvoker(ctrl)
+	invoker.EXPECT().GetURL().Return(u).AnyTimes()
+
+	invoc1 := invocation.NewRPCInvocation(constant.Generic, []any{"method", "types", "args"}, nil)
+	assert.True(t, isMakingAGenericCall(invoker, invoc1))
+
+	invoc2 := invocation.NewRPCInvocation(constant.Generic, []any{"method"}, nil)
+	assert.False(t, isMakingAGenericCall(invoker, invoc2))
+
+	invoc3 := invocation.NewRPCInvocation("GetUser", []any{"a", "b", "c"}, nil)
+	assert.False(t, isMakingAGenericCall(invoker, invoc3))
+}
+
+func TestIsGeneric(t *testing.T) {
+	assert.True(t, isGeneric("true"))
+	assert.True(t, isGeneric("True"))
+	assert.False(t, isGeneric("false"))
+	assert.False(t, isGeneric(""))
+	assert.False(t, isGeneric("bean")) // 目前代码逻辑仅匹配 "true"
+}
+
+func TestGetGeneralizer(t *testing.T) {
+	g1 := getGeneralizer(constant.GenericSerializationDefault)
+	assert.IsType(t, generalizer.GetMapGeneralizer(), g1)
+
+	g2 := getGeneralizer(constant.GenericSerializationGson)
+	assert.IsType(t, generalizer.GetGsonGeneralizer(), g2)
+
+	g3 := getGeneralizer("unsupported_type")
+	assert.IsType(t, generalizer.GetMapGeneralizer(), g3)
+}