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 cd88faf15 fix(common): consolidate backend numeric and input 
sanitization fixes (#2656)
cd88faf15 is described below

commit cd88faf1531b53d4a15c8068d76b790b18e6ff8a
Author: yyqdbngt <[email protected]>
AuthorDate: Mon Aug 31 19:26:14 2026 +0800

    fix(common): consolidate backend numeric and input sanitization fixes 
(#2656)
    
    * fix(dashboard): ignore invalid broker TPS
    
    * fix(cluster): normalize broker disk ratio percentage
    
    * fix(ai): bound Claude CLI stderr buffering
    
    * fix(acl): normalize user search with root locale
    
    * fix(dlq): reject blank selected message IDs
    
    * fix(credentials): trim paged search terms
    
    * fix(common): isolate page result items
    
    ---------
    
    Co-authored-by: Yue Wang <[email protected]>
---
 .../rocketmq/studio/common/domain/PageResult.java   |  5 ++++-
 .../instance/acl/MybatisPlusAclRepository.java      |  3 ++-
 .../instance/dlq/DLQResendSelectedRequestDTO.java   |  2 +-
 .../studio/ops/ai/ClaudeCodeAgentProvider.java      |  8 +++++++-
 .../provider/apache/RocketMQClusterProvider.java    |  4 +++-
 .../provider/apache/RocketMQDashboardProvider.java  | 12 ++++++++++--
 .../MybatisPlusCloudCredentialRepository.java       | 14 +++++++++++---
 .../studio/common/domain/PageResultTest.java        | 16 ++++++++++++++++
 .../instance/acl/MybatisPlusAclRepositoryTest.java  | 21 +++++++++++++++++++++
 .../studio/instance/dlq/DLQControllerTest.java      | 18 ++++++++++++++++++
 .../studio/ops/ai/ClaudeCodeAgentProviderTest.java  | 12 ++++++++++++
 .../apache/RocketMQClusterProviderTest.java         | 16 ++++++++++++++++
 .../apache/RocketMQDashboardProviderTest.java       | 20 ++++++++++++++++++++
 .../MybatisPlusCloudCredentialRepositoryTest.java   | 20 ++++++++++++++++++++
 14 files changed, 161 insertions(+), 10 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/common/domain/PageResult.java 
b/server/src/main/java/org/apache/rocketmq/studio/common/domain/PageResult.java
index 34fa1f578..cba427495 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/common/domain/PageResult.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/common/domain/PageResult.java
@@ -17,6 +17,7 @@
 package org.apache.rocketmq.studio.common.domain;
 
 import lombok.Getter;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -31,7 +32,9 @@ public class PageResult<T> {
 
     public static <T> PageResult<T> of(List<T> items, long total, int page, 
int size) {
         PageResult<T> result = new PageResult<>();
-        result.items = items == null ? Collections.emptyList() : items;
+        result.items = items == null
+                ? Collections.emptyList()
+                : Collections.unmodifiableList(new ArrayList<>(items));
         result.total = total;
         result.page = page;
         result.size = size;
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
index 5f463c9ed..442fa7b7c 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
@@ -37,6 +37,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -105,7 +106,7 @@ public class MybatisPlusAclRepository implements 
AclRepository {
 
     @Override
     public PageResult<AclUserVO> findUserPage(String keyword, int page, int 
pageSize) {
-        String search = StringUtils.hasText(keyword) ? 
keyword.trim().toLowerCase() : null;
+        String search = StringUtils.hasText(keyword) ? 
keyword.trim().toLowerCase(Locale.ROOT) : null;
         QueryWrapper<RmqAclUser> query = new QueryWrapper<RmqAclUser>()
                 .and(search != null, w -> w
                         .like("username", search)
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQResendSelectedRequestDTO.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQResendSelectedRequestDTO.java
index 73c20c266..1ee31b5a9 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQResendSelectedRequestDTO.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQResendSelectedRequestDTO.java
@@ -39,7 +39,7 @@ public class DLQResendSelectedRequestDTO {
 
     @NotEmpty(message = "At least one msgId is required")
     @Size(max = 100, message = "At most 100 msgIds are allowed per resend")
-    private List<String> msgIds;
+    private List<@NotBlank(message = "msgId must not be blank") String> msgIds;
 
     private String targetTopic;
 }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProvider.java
index c7038dd80..aeb0c639c 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProvider.java
@@ -52,6 +52,8 @@ public class ClaudeCodeAgentProvider extends CliAgentProvider 
{
     private static final String ANTHROPIC_APP_SUFFIX = "/apps/anthropic";
     private static final long STREAM_TIMEOUT_SECONDS = 300;
     private static final long OUTPUT_DRAIN_TIMEOUT_SECONDS = 10;
+    private static final int MAX_STDERR_BYTES = 64 * 1024;
+    private static final String STDERR_TRUNCATED_SUFFIX = "\n...[stderr 
truncated]";
 
     private final LlmProperties llmProperties;
     private final ObjectMapper objectMapper = new ObjectMapper();
@@ -179,7 +181,11 @@ public class ClaudeCodeAgentProvider extends 
CliAgentProvider {
         CompletableFuture<String> result = new CompletableFuture<>();
         Thread.ofVirtual().start(() -> {
             try (stream) {
-                result.complete(new String(stream.readAllBytes(), 
StandardCharsets.UTF_8));
+                byte[] bytes = stream.readNBytes(MAX_STDERR_BYTES + 1);
+                boolean truncated = bytes.length > MAX_STDERR_BYTES;
+                int length = Math.min(bytes.length, MAX_STDERR_BYTES);
+                String output = new String(bytes, 0, length, 
StandardCharsets.UTF_8);
+                result.complete(truncated ? output + STDERR_TRUNCATED_SUFFIX : 
output);
             } catch (Exception exception) {
                 result.completeExceptionally(exception);
             }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
index 8cd304741..8deca28eb 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
@@ -276,7 +276,9 @@ public class RocketMQClusterProvider implements 
ClusterProvider {
                 try {
                     double parsedRatio = Double.parseDouble(diskRatio);
                     if (Double.isFinite(parsedRatio) && parsedRatio >= 0) {
-                        builder.diskUsage(parsedRatio);
+                        // RocketMQ exposes commitLogDiskRatio as a fraction 
in [0, 1],
+                        // while BrokerVO and the web progress bars use 
percentage points.
+                        builder.diskUsage(parsedRatio * 100D);
                     }
                 } catch (NumberFormatException ignored) {
                     // keep default
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
index 6fbe18459..7299fb0d0 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProvider.java
@@ -489,9 +489,9 @@ public class RocketMQDashboardProvider implements 
DashboardProvider {
         try {
             String[] parts = tpsStr.trim().split("\\s+");
             if (parts.length >= 2) {
-                return (long) Double.parseDouble(parts[1]);
+                return parseTpsValue(parts[1]);
             } else if (parts.length == 1) {
-                return (long) Double.parseDouble(parts[0]);
+                return parseTpsValue(parts[0]);
             }
         } catch (NumberFormatException e) {
             log.debug("Failed to parse TPS value: {}", tpsStr);
@@ -499,6 +499,14 @@ public class RocketMQDashboardProvider implements 
DashboardProvider {
         return 0;
     }
 
+    private long parseTpsValue(String value) {
+        double parsed = Double.parseDouble(value);
+        if (!Double.isFinite(parsed) || parsed < 0 || parsed > Long.MAX_VALUE) 
{
+            return 0;
+        }
+        return (long) parsed;
+    }
+
     private long parseMessagesToday(Map<String, String> runtimeStats) {
         String morningValue = runtimeStats.get("msgPutTotalTodayMorning");
         String currentValue = runtimeStats.get("msgPutTotalTodayNow");
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepository.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepository.java
index f60e42251..f9676ffc8 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepository.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepository.java
@@ -49,10 +49,18 @@ public class MybatisPlusCloudCredentialRepository 
implements CloudCredentialRepo
                 .map(MybatisPlusCloudCredentialRepository::toVO)
                 .collect(Collectors.toList());
     }
-    @Override public PageResult<CloudCredentialVO> findPage(InstanceVendor 
vendor, String search, int page, int pageSize) {
-        QueryWrapper<RmqCloudCredential> q = new 
QueryWrapper<RmqCloudCredential>().eq(vendor != null, "vendor", vendor == null 
? null : vendor.name()).like(search != null && !search.isBlank(), "name", 
search).orderByDesc("gmt_modified", "id");
+    @Override
+    public PageResult<CloudCredentialVO> findPage(InstanceVendor vendor, 
String search, int page, int pageSize) {
+        String normalizedSearch = search == null || search.isBlank() ? null : 
search.trim();
+        QueryWrapper<RmqCloudCredential> q = new 
QueryWrapper<RmqCloudCredential>()
+                .eq(vendor != null, "vendor", vendor == null ? null : 
vendor.name())
+                .like(normalizedSearch != null, "name", normalizedSearch)
+                .orderByDesc("gmt_modified", "id");
         Page<RmqCloudCredential> result = credentialMapper.selectPage(new 
Page<>(page, pageSize), q);
-        return 
PageResult.of(result.getRecords().stream().map(MybatisPlusCloudCredentialRepository::toVO).toList(),
 result.getTotal(), page, pageSize);
+        return PageResult.of(result.getRecords().stream()
+                        .map(MybatisPlusCloudCredentialRepository::toVO)
+                        .toList(),
+                result.getTotal(), page, pageSize);
     }
 
     @Override
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/common/domain/PageResultTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/common/domain/PageResultTest.java
index 7bcb3dadb..586aac76f 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/common/domain/PageResultTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/common/domain/PageResultTest.java
@@ -18,7 +18,11 @@ package org.apache.rocketmq.studio.common.domain;
 
 import org.junit.jupiter.api.Test;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class PageResultTest {
 
@@ -28,4 +32,16 @@ class PageResultTest {
 
         assertThat(result.getItems()).isNotNull().isEmpty();
     }
+
+    @Test
+    void ofShouldIsolateItemsFromTheSourceList() {
+        List<String> source = new ArrayList<>(List.of("first"));
+
+        PageResult<String> result = PageResult.of(source, 1, 1, 20);
+        source.add("later");
+
+        assertThat(result.getItems()).containsExactly("first");
+        assertThatThrownBy(() -> result.getItems().add("mutated"))
+                .isInstanceOf(UnsupportedOperationException.class);
+    }
 }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
index 0e7e075f3..a8604ba4e 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
@@ -40,6 +40,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -152,6 +153,26 @@ class MybatisPlusAclRepositoryTest {
         verify(userMapper, never()).selectList(any(QueryWrapper.class));
     }
 
+    @Test
+    void findUserPageShouldNormalizeSearchWithRootLocale() {
+        Page<RmqAclUser> mapperPage = new Page<RmqAclUser>(1, 20)
+                .setRecords(List.of())
+                .setTotal(0);
+        when(userMapper.selectPage(any(IPage.class), 
any(Wrapper.class))).thenReturn(mapperPage);
+        Locale previous = Locale.getDefault();
+        try {
+            Locale.setDefault(Locale.forLanguageTag("tr-TR"));
+            repository.findUserPage(" INSTANCE ", 1, 20);
+        } finally {
+            Locale.setDefault(previous);
+        }
+        ArgumentCaptor<Wrapper<RmqAclUser>> queryCaptor = 
ArgumentCaptor.forClass(Wrapper.class);
+        verify(userMapper).selectPage(any(IPage.class), queryCaptor.capture());
+        QueryWrapper<RmqAclUser> query = (QueryWrapper<RmqAclUser>) 
queryCaptor.getValue();
+        assertThat(query.getSqlSegment()).contains("username", "access_key");
+        assertThat(query.getParamNameValuePairs()).containsValue("%instance%");
+    }
+
     @Test
     void replaceRuleShouldReturnEmptyWhenConcurrentDeleteWins() {
         RmqAclRule existing = new RmqAclRule();
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
index 748913621..e2f54c15d 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
@@ -426,6 +426,24 @@ class DLQControllerTest {
         verifyNoInteractions(dlqService);
     }
 
+    @Test
+    void resendSelectedMessagesShouldRejectBlankMsgIdsTest() throws Exception {
+        Map<String, Object> body = Map.of(
+                "instanceId", "instance-1",
+                "groupName", "test-group",
+                "msgIds", List.of("msg-1", " ")
+        );
+
+        mockMvc.perform(post("/api/dlq/resend-selected")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(body)))
+                .andExpect(status().isBadRequest())
+                .andExpect(jsonPath("$.code").value(400))
+                .andExpect(jsonPath("$.message").value("msgId must not be 
blank"));
+
+        verifyNoInteractions(dlqService);
+    }
+
     @Test
     void resendSelectedMessagesShouldRejectMoreThanHundredMsgIdsTest() throws 
Exception {
         List<String> msgIds = new java.util.ArrayList<>();
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProviderTest.java
index 7786839ce..f9093a867 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/ClaudeCodeAgentProviderTest.java
@@ -50,6 +50,18 @@ class ClaudeCodeAgentProviderTest {
         assertThat(tokens).containsExactly("done");
     }
 
+    @Test
+    void streamShouldBoundStderrIncludedInFailuresTest() {
+        TestClaudeCodeAgentProvider provider = new TestClaudeCodeAgentProvider(
+                List.of("sh", "-c", "yes failure | head -c 131072 >&2; exit 
1"), 5);
+
+        assertThatThrownBy(() -> provider.stream(
+                LlmConfigVO.builder().build(), "prompt", null, ignored -> { }))
+                .isInstanceOf(LlmGatewayException.class)
+                .hasMessageContaining("[stderr truncated]")
+                .satisfies(exception -> 
assertThat(exception.getMessage().length()).isLessThan(70_000));
+    }
+
     @Test
     void streamShouldEnforceTimeoutBeforeWaitingForStdoutTest() {
         TestClaudeCodeAgentProvider provider = new TestClaudeCodeAgentProvider(
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
index 5ba2fb094..89ea41124 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
@@ -73,6 +73,22 @@ class RocketMQClusterProviderTest {
         
assertThat(clusters.get(0).getStatus()).isEqualTo(ClusterStatus.healthy);
     }
 
+    @Test
+    void discoverClustersShouldConvertDiskRatioToPercentage() throws Exception 
{
+        DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = newProvider(adminExt);
+        when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo());
+        KVTable runtime = runtimeStats();
+        runtime.getTable().put("commitLogDiskRatio", "0.625");
+        
when(adminExt.fetchBrokerRuntimeStats("10.0.0.11:10911")).thenReturn(runtime);
+
+        ClusterVO cluster = provider.discoverClusters().get(0);
+
+        assertThat(cluster.getBrokers()).singleElement()
+                .extracting(broker -> broker.getDiskUsage())
+                .isEqualTo(62.5D);
+    }
+
     @Test
     void discoverClustersShouldPopulateSafeDefaults() throws Exception {
         DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
index 46172fa7b..04c625f01 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQDashboardProviderTest.java
@@ -484,6 +484,26 @@ class RocketMQDashboardProviderTest {
         });
     }
 
+    @Test
+    void dashboardShouldIgnoreNonFiniteNegativeAndOverflowingTps() throws 
Exception {
+        DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
+        
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithTwoMasters());
+        when(adminExt.fetchAllTopicList()).thenReturn(topicList());
+        when(adminExt.fetchBrokerRuntimeStats("10.0.0.11:10911"))
+                .thenReturn(runtimeStats("NaN", "Infinity"));
+        when(adminExt.fetchBrokerRuntimeStats("10.0.0.12:10911"))
+                .thenReturn(runtimeStats("-1", "1e300"));
+
+        DashboardDataVO dashboard = newProvider(adminExt).getDashboardData();
+
+        assertThat(dashboard.getStats().getTpsIn()).isZero();
+        assertThat(dashboard.getStats().getTpsOut()).isZero();
+        assertThat(dashboard.getClusters()).allSatisfy(cluster -> {
+            assertThat(cluster.getTpsIn()).isZero();
+            assertThat(cluster.getTpsOut()).isZero();
+        });
+    }
+
     @Test
     void dashboardShouldReportMessagesProducedSinceTodayMorning() throws 
Exception {
         DefaultMQAdminExt adminExt = mock(DefaultMQAdminExt.class);
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepositoryTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepositoryTest.java
index d31cd29a2..7ee62d058 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepositoryTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/credential/MybatisPlusCloudCredentialRepositoryTest.java
@@ -17,12 +17,17 @@
 
 package org.apache.rocketmq.studio.provider.credential;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.rocketmq.studio.common.domain.enums.InstanceVendor;
 import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.persistence.entity.RmqCloudCredential;
 import org.apache.rocketmq.studio.persistence.mapper.RmqCloudCredentialMapper;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -32,6 +37,7 @@ import java.util.Optional;
 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.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
@@ -77,6 +83,20 @@ class MybatisPlusCloudCredentialRepositoryTest {
                 .hasMessageContaining("3");
     }
 
+    @Test
+    void findPageShouldTrimTheSearchTerm() {
+        when(credentialMapper.selectPage(any(IPage.class), any(Wrapper.class)))
+                .thenReturn(new Page<RmqCloudCredential>(1, 20));
+
+        repository.findPage(null, "  credential  ", 1, 20);
+
+        ArgumentCaptor<Wrapper<RmqCloudCredential>> queryCaptor = 
ArgumentCaptor.forClass(Wrapper.class);
+        verify(credentialMapper).selectPage(any(IPage.class), 
queryCaptor.capture());
+        QueryWrapper<RmqCloudCredential> query = 
(QueryWrapper<RmqCloudCredential>) queryCaptor.getValue();
+        query.getCustomSqlSegment();
+        
assertThat(query.getParamNameValuePairs()).containsValue("%credential%");
+    }
+
     private RmqCloudCredential entity(Long id, String name, String vendor) {
         RmqCloudCredential entity = new RmqCloudCredential();
         entity.setId(id);

Reply via email to