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 ea35491c3 fix(web): make the DLQ and consumer-group mocks honour the 
pagination contract (#5593)
ea35491c3 is described below

commit ea35491c32812052ce15911d018a6c1840a98ae9
Author: Zhao Jianing <[email protected]>
AuthorDate: Fri Oct 9 18:45:56 2026 +0800

    fix(web): make the DLQ and consumer-group mocks honour the pagination 
contract (#5593)
    
    The frontend mocks for `listDLQGroups` / `listDLQMessages` and 
`listConsumerGroupPage` sliced the
    fixture list without the guards the real backend applies, so a mock-backed 
page could ask for page 0
    or a negative page and get rows from the end of the list instead of an 
error - the opposite of what
    the backend does. Both mocks now reject `page < 1` and clamp `pageSize` to 
1..100 exactly like the
    server, and the CSV export path (which pages at `EXPORT_PAGE_SIZE = 100`) 
is covered so the clamp
    does not clip it.
    
    `messageService` + `consumerService` tests: 32 green. Removing the three 
guards reddens exactly the
    nine cases that pin them.
    
    Folded in #5595.
---
 web/src/services/consumerService.test.ts | 26 +++++++++++++++++++++
 web/src/services/consumerService.ts      |  9 ++++++++
 web/src/services/messageService.test.ts  | 39 ++++++++++++++++++++++++++++++++
 web/src/services/messageService.ts       | 16 ++++++++++++-
 4 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/web/src/services/consumerService.test.ts 
b/web/src/services/consumerService.test.ts
index 6052735da..f4d6c0177 100644
--- a/web/src/services/consumerService.test.ts
+++ b/web/src/services/consumerService.test.ts
@@ -379,3 +379,29 @@ describe('consumer group CSV export', () => {
     }
   });
 });
+
+describe('consumer group page mock pagination contract', () => {
+  it('rejects page 0 like the backend', async () => {
+    await expect(listConsumerGroupPage({ page: 0, pageSize: 20 
})).rejects.toThrow(
+      'page must be greater than zero',
+    );
+  });
+
+  it('rejects negative pages that would slice from the end', async () => {
+    await expect(listConsumerGroupPage({ page: -1, pageSize: 1 
})).rejects.toThrow(
+      'page must be greater than zero',
+    );
+  });
+
+  it('rejects pageSize above the backend cap of 100', async () => {
+    await expect(listConsumerGroupPage({ page: 1, pageSize: 101 
})).rejects.toThrow(
+      'pageSize must be between 1 and 100',
+    );
+  });
+
+  it('rejects pageSize 0', async () => {
+    await expect(listConsumerGroupPage({ page: 1, pageSize: 0 
})).rejects.toThrow(
+      'pageSize must be between 1 and 100',
+    );
+  });
+});
diff --git a/web/src/services/consumerService.ts 
b/web/src/services/consumerService.ts
index 1163e4211..9133e9614 100644
--- a/web/src/services/consumerService.ts
+++ b/web/src/services/consumerService.ts
@@ -121,6 +121,15 @@ export async function listConsumerGroupPage(
   if (isMockMode()) {
     const page = params.page ?? 1;
     const pageSize = params.pageSize ?? 20;
+    // Mirror the backend pagination contract (MetadataService
+    // validatePagination: page >= 1, pageSize 1..100 -> 400): without this
+    // the mock accepts out-of-range values and a negative page slices from
+    // the end of the list.
+    if (page < 1 || pageSize < 1 || pageSize > 100) {
+      return Promise.reject(
+        new Error('page must be greater than zero and pageSize must be between 
1 and 100'),
+      );
+    }
     const groups = filterConsumerGroups(params);
     const from = Math.min((page - 1) * pageSize, groups.length);
     return {
diff --git a/web/src/services/messageService.test.ts 
b/web/src/services/messageService.test.ts
index 716392f7f..c44bb965d 100644
--- a/web/src/services/messageService.test.ts
+++ b/web/src/services/messageService.test.ts
@@ -20,6 +20,7 @@ import {
   consumeMessageDirectly,
   getMessageTrace,
   listDLQGroups,
+  listDLQMessages,
   queryMessagePage,
   queryMessages,
 } from './messageService';
@@ -118,3 +119,41 @@ describe('message service mock data', () => {
     expect(all.total).toBeGreaterThanOrEqual(1);
   });
 });
+
+describe('DLQ mock pagination contract', () => {
+  it('rejects page 0 like the backend', async () => {
+    await expect(listDLQGroups('instance-1', undefined, 0, 
20)).rejects.toThrow(
+      'Invalid page or pageSize',
+    );
+  });
+
+  it('rejects negative pages that would slice from the end', async () => {
+    await expect(listDLQGroups('instance-1', undefined, -1, 
1)).rejects.toThrow(
+      'Invalid page or pageSize',
+    );
+  });
+
+  it('rejects pageSize above the backend cap of 100', async () => {
+    await expect(listDLQGroups('instance-1', undefined, 1, 
101)).rejects.toThrow(
+      'Invalid page or pageSize',
+    );
+    await expect(
+      listDLQMessages({ instanceId: 'instance-1', groupName: 'g', page: 1, 
pageSize: 101 }),
+    ).rejects.toThrow('Invalid page or pageSize');
+  });
+
+  it('rejects pageSize 0 for both listing functions', async () => {
+    await expect(listDLQGroups('instance-1', undefined, 1, 0)).rejects.toThrow(
+      'Invalid page or pageSize',
+    );
+    await expect(
+      listDLQMessages({ instanceId: 'instance-1', groupName: 'g', page: 1, 
pageSize: 0 }),
+    ).rejects.toThrow('Invalid page or pageSize');
+  });
+
+  it('rejects page 0 for message listing', async () => {
+    await expect(
+      listDLQMessages({ instanceId: 'instance-1', groupName: 'g', page: 0, 
pageSize: 20 }),
+    ).rejects.toThrow('Invalid page or pageSize');
+  });
+});
diff --git a/web/src/services/messageService.ts 
b/web/src/services/messageService.ts
index ebfba8b54..4bb4c39ea 100644
--- a/web/src/services/messageService.ts
+++ b/web/src/services/messageService.ts
@@ -123,6 +123,13 @@ export async function listDLQGroups(
   pageSize = 20,
 ): Promise<DLQGroupPage> {
   if (isMockMode()) {
+    // Mirror the backend pagination contract (DLQService: page >= 1,
+    // pageSize 1..100 -> 400 "Invalid page or pageSize"): without this the
+    // mock accepts out-of-range values and a negative page slices from the
+    // end of the list.
+    if (page < 1 || pageSize < 1 || pageSize > 100) {
+      return Promise.reject(new Error('Invalid page or pageSize'));
+    }
     const groups = (mockDLQGroups as unknown as DLQGroup[]).filter(
       (group) => !search || group.groupName.includes(search) || 
group.dlqTopic.includes(search),
     );
@@ -173,7 +180,14 @@ export async function listDLQMessages(params: {
   pageSize?: number;
 }): Promise<DLQMessagePage> {
   if (isMockMode()) {
-    return { items: [], total: 0, page: params.page ?? 1, size: 
params.pageSize ?? 20 };
+    // Mirror the backend contract (DLQController @Min(1) page/pageSize,
+    // @Max(100) pageSize -> 400).
+    const page = params.page ?? 1;
+    const pageSize = params.pageSize ?? 20;
+    if (page < 1 || pageSize < 1 || pageSize > 100) {
+      return Promise.reject(new Error('Invalid page or pageSize'));
+    }
+    return { items: [], total: 0, page, size: pageSize };
   }
   return messageApi.listDLQMessages(params);
 }

Reply via email to