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

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new d24f58b82 fix(tencent): reject incomplete catalog pages (#4529)
d24f58b82 is described below

commit d24f58b82667c22277720c7afe2e5cbf69c089d5
Author: zmuxuny <[email protected]>
AuthorDate: Mon Sep 21 17:30:00 2026 +0800

    fix(tencent): reject incomplete catalog pages (#4529)
    
    TencentCatalogService.listCloudInstances treated a null/empty or short page 
as the end of the catalog without consulting TotalCount, so a truncated page 
was reported as a complete instance list. A page that contradicts a known 
non-negative TotalCount now raises BusinessException(502); genuinely empty 
catalogs and unknown totals keep the existing behaviour.
    
    Fixes #4528
---
 .../provider/tencent/TencentCatalogService.java    | 23 +++++++++---
 .../tencent/TencentCatalogServiceTest.java         | 42 ++++++++++++++++++++++
 2 files changed, 61 insertions(+), 4 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
index 744980ed7..f06be61e2 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
@@ -89,7 +89,13 @@ public class TencentCatalogService implements 
CloudCatalogProvider {
             DescribeInstanceListResponse response = 
clientFactory.call(credentialId, regionId,
                     client -> client.DescribeInstanceList(request));
             InstanceItem[] data = response == null ? null : response.getData();
-            if (data == null || data.length == 0) {
+            Long totalCount = response == null ? null : 
response.getTotalCount();
+            int returned = data == null ? 0 : data.length;
+            if (isIncompletePage(offset, returned, totalCount)) {
+                throw new BusinessException(502,
+                        "Tencent Cloud RocketMQ instance catalog returned an 
incomplete page");
+            }
+            if (returned == 0) {
                 break;
             }
             for (InstanceItem item : data) {
@@ -101,15 +107,24 @@ public class TencentCatalogService implements 
CloudCatalogProvider {
                     instances.add(option);
                 }
             }
-            if (data.length < PAGE_SIZE || hasFetchedAll(offset, 
response.getTotalCount())) {
+            if (hasFetchedAll(offset, returned, totalCount)
+                    || isUnknownTotalCount(totalCount) && returned < 
PAGE_SIZE) {
                 break;
             }
         }
         return instances;
     }
 
-    private static boolean hasFetchedAll(long offset, Long totalCount) {
-        return totalCount != null && totalCount >= 0L && offset + PAGE_SIZE >= 
totalCount;
+    private static boolean isIncompletePage(long offset, int returned, Long 
totalCount) {
+        return totalCount != null && totalCount >= 0L && offset + returned < 
totalCount && returned < PAGE_SIZE;
+    }
+
+    private static boolean hasFetchedAll(long offset, int returned, Long 
totalCount) {
+        return totalCount != null && totalCount >= 0L && offset + returned >= 
totalCount;
+    }
+
+    private static boolean isUnknownTotalCount(Long totalCount) {
+        return totalCount == null || totalCount < 0L;
     }
 
     @Override
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
index 03d15b621..3ed018119 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
@@ -22,6 +22,7 @@ import 
com.tencentcloudapi.trocket.v20230308.models.DescribeInstanceResponse;
 import com.tencentcloudapi.trocket.v20230308.models.Endpoint;
 import com.tencentcloudapi.trocket.v20230308.models.InstanceItem;
 import com.tencentcloudapi.trocket.v20230308.TrocketClient;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.provider.CloudInstanceDetailVO;
 import org.apache.rocketmq.studio.provider.CloudInstanceOptionVO;
 import org.apache.rocketmq.studio.provider.CloudRegionVO;
@@ -34,6 +35,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.lenient;
@@ -133,6 +135,46 @@ class TencentCatalogServiceTest {
                 .isEqualTo("rmq-valid");
     }
 
+    @Test
+    void 
listCloudInstancesShouldRejectIncompleteEmptyPageWhenTotalCountRequiresDataTest()
 {
+        DescribeInstanceListResponse response = new 
DescribeInstanceListResponse();
+        response.setTotalCount(1L);
+        response.setData(null);
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), 
any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID, 
REGION, null))
+                .isInstanceOf(BusinessException.class)
+                .satisfies(error -> assertThat(
+                        ((BusinessException) error).getCode())
+                        .isEqualTo(502));
+    }
+
+    @Test
+    void 
listCloudInstancesShouldRejectIncompleteShortPageWhenTotalCountRequiresMoreTest()
 {
+        InstanceItem item = new InstanceItem();
+        item.setInstanceId("rmq-only-row");
+        DescribeInstanceListResponse response = new 
DescribeInstanceListResponse();
+        response.setTotalCount(2L);
+        response.setData(new InstanceItem[]{item});
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), 
any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID, 
REGION, null))
+                .isInstanceOf(BusinessException.class)
+                .satisfies(error -> assertThat(
+                        ((BusinessException) error).getCode())
+                        .isEqualTo(502));
+    }
+
+    @Test
+    void listCloudInstancesShouldKeepGenuineEmptyCatalogTest() {
+        DescribeInstanceListResponse response = new 
DescribeInstanceListResponse();
+        response.setTotalCount(0L);
+        response.setData(null);
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), 
any())).thenReturn(response);
+
+        assertThat(service.listCloudInstances(CREDENTIAL_ID, REGION, 
null)).isEmpty();
+    }
+
     @Test
     void 
listCloudInstancesShouldContinuePastTenThousandRecordsWhenTotalCountRequiresItTest()
 throws Exception {
         InstanceItem item = new InstanceItem();

Reply via email to