AlexStocks commented on code in PR #3313:
URL: https://github.com/apache/dubbo-go/pull/3313#discussion_r3610132824


##########
cluster/router/polaris/router_test.go:
##########
@@ -110,3 +260,173 @@ func 
TestNewPolarisRouterInvalidRegistriesTypeShouldFallback(t *testing.T) {
        require.NotNil(t, r.Registries)
        require.Empty(t, r.Registries)
 }
+
+// --- Route() regression tests for issue #3303 ---
+
+func TestRouteOpenRouteDisabledReturnsOriginalInvokers(t *testing.T) {
+       r := &polarisRouter{
+               openRoute:  false,
+               Registries: map[string]*global.RegistryConfig{},
+       }
+       url := mustNewURL(t, baseServiceURL+"?interface=com.xxx.Service")
+       invokers := []base.Invoker{newMockInvokerWithInstanceID(t, "inst-1")}
+
+       got := r.Route(invokers, url, &mockInvocation{})
+       assert.Equal(t, invokers, got, "should return original invokers when 
openRoute is false")
+}
+
+func TestRouteEmptyInvokersReturnsEmpty(t *testing.T) {
+       r := &polarisRouter{
+               openRoute:  true,
+               Registries: map[string]*global.RegistryConfig{},
+       }
+       url := mustNewURL(t, baseServiceURL+"?interface=com.xxx.Service")
+
+       got := r.Route(nil, url, &mockInvocation{})
+       assert.Empty(t, got, "should return empty when input invokers is nil")
+
+       got = r.Route([]base.Invoker{}, url, &mockInvocation{})
+       assert.Empty(t, got, "should return empty when input invokers is empty")
+}
+
+func TestRouteGetAllInstancesErrorReturnsOriginalInvokers(t *testing.T) {
+       consumerAPI := &mockConsumerAPI{
+               allInstancesResp: nil,
+               allInstancesErr:  errors.New("polaris unavailable"),
+       }
+       routerAPI := &mockRouterAPI{}
+       r := newTestRouter(t, routerAPI, consumerAPI)
+       url := mustNewURL(t, baseServiceURL+"?interface=com.xxx.Service")
+       invokers := []base.Invoker{newMockInvokerWithInstanceID(t, "inst-1")}
+
+       got := r.Route(invokers, url, &mockInvocation{})
+       assert.Equal(t, invokers, got, "should return original invokers when 
GetAllInstances fails")
+}
+
+func TestRouteProcessRoutersErrorReturnsOriginalInvokers(t *testing.T) {
+       inst := &mockInstance{id: "inst-1"}
+       consumerAPI := &mockConsumerAPI{
+               allInstancesResp: &model.InstancesResponse{
+                       Instances: []model.Instance{inst},
+               },
+       }
+       engine := &mockEngine{
+               routeRule: &model.ServiceRuleResponse{
+                       Value: &v1.Routing{},
+               },
+       }
+       sdkCtx := &mockSDKContext{engine: engine}
+       routerAPI := &mockRouterAPI{
+               sdkCtx:     sdkCtx,
+               processErr: errors.New("route processing failed"),
+       }
+       r := newTestRouter(t, routerAPI, consumerAPI)
+       url := mustNewURL(t, baseServiceURL+"?interface=com.xxx.Service")
+       invokers := []base.Invoker{newMockInvokerWithInstanceID(t, "inst-1")}
+
+       got := r.Route(invokers, url, &mockInvocation{})
+       assert.Equal(t, invokers, got, "should return original invokers when 
ProcessRouters fails")
+}
+
+// TestRouteRuleNotMatchFallbackToOriginalInvokers is the key regression test 
for issue #3303.
+// When ProcessRouters returns instances that don't match any known invoker 
(route rule not match),
+// Route() should return the original invokers instead of an empty slice to 
prevent nil pointer panic.
+func TestRouteRuleNotMatchFallbackToOriginalInvokers(t *testing.T) {
+       inst := &mockInstance{id: "inst-1"}
+       consumerAPI := &mockConsumerAPI{
+               allInstancesResp: &model.InstancesResponse{
+                       Instances: []model.Instance{inst},
+               },
+       }
+       engine := &mockEngine{
+               routeRule: &model.ServiceRuleResponse{
+                       Value: &v1.Routing{},
+               },
+       }
+       sdkCtx := &mockSDKContext{engine: engine}
+       // ProcessRouters returns instances with IDs that don't exist in 
invokersMap
+       nonMatchingInst := &mockInstance{id: "unknown-inst-999"}
+       routerAPI := &mockRouterAPI{
+               sdkCtx: sdkCtx,
+               processResp: &model.InstancesResponse{
+                       Instances: []model.Instance{nonMatchingInst},
+               },
+       }
+       r := newTestRouter(t, routerAPI, consumerAPI)
+       url := mustNewURL(t, baseServiceURL+"?interface=com.xxx.Service")
+       invokers := []base.Invoker{newMockInvokerWithInstanceID(t, "inst-1")}
+
+       var got []base.Invoker
+       require.NotPanics(t, func() {

Review Comment:
   [P1] 这个测试没有复现 Issue 描述的 panic 调用链
   `polarisRouter.Route` 本身只构造并返回切片,当前测试直接调用它再包一层 `NotPanics`,即使删除本 PR 的 5 
行生产修改也不会在这里触发 Issue 所称的 cluster/load-balance 空切片 panic;它只能证明作者设定的 fallback 
返回值。仓库的 `BaseClusterInvoker.CheckInvokers` 已对空列表返回明确错误,因此必须先用真实 RouterChain + 
具体 cluster invoker 复现 #3303 的栈,再在正确层修复。建议补一个从 Directory.List/RouterChain 进入 
failfast/failover 等调用路径的回归测试,断言不 panic 且返回 `no provider`/route-not-match 
错误,而不是把原始 providers 放回去。否则当前测试既未证明根因,也把绕过路由规则的行为固化成期望。



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to