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);
}