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 d3a4458a0 fix(cluster): keep the form values a save silently rewrote
(#5778)
d3a4458a0 is described below
commit d3a4458a0afbb4a0b892706ddd6cd2e7aa9cf22f
Author: Chstercc <[email protected]>
AuthorDate: Sat Oct 10 12:04:27 2026 +0800
fix(cluster): keep the form values a save silently rewrote (#5778)
The cluster form rewrote `maxMessageSize` on save without telling the user,
so a subsequent submit persisted the rewritten value. The fix uses antd's
touched semantics so only fields the user actually edited are sent.
1 suite, 35 tests green; prettier clean.
---
.../pages/cluster/__tests__/ClusterPage.test.tsx | 115 ++++++++++++++++++++-
web/src/pages/cluster/index.tsx | 6 +-
2 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
index 83a07623d..1eb02f723 100644
--- a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
+++ b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
@@ -94,10 +94,12 @@ const buildCluster = ({
tpsIn = 12480,
tpsOut = 34560,
connections = 1842,
+ maxMessageSize = 4 * 1024 * 1024,
}: {
tpsIn?: number;
tpsOut?: number;
connections?: number;
+ maxMessageSize?: number;
} = {}): ClusterInfo => ({
id: 'cluster-prod',
name: 'rocketmq-prod',
@@ -140,7 +142,7 @@ const buildCluster = ({
flushDiskType: 'SYNC_FLUSH',
autoCreateTopicEnable: false,
autoCreateSubscriptionGroup: false,
- maxMessageSize: 4 * 1024 * 1024,
+ maxMessageSize,
msgTraceTopicName: 'RMQ_SYS_TRACE_TOPIC',
fileReservedTime: 72,
writeQueueNums: 8,
@@ -413,6 +415,117 @@ describe('Cluster page', () => {
expect(within(dialog).getByRole('row', { name: /写队列数/
})).toHaveTextContent('16');
});
+ it('preservesUntouchedMessageSizeBytesInPreviewAndSaveTest', async () => {
+ clusterServiceMocks.listRegistryClusters.mockResolvedValue([
+ buildCluster({ maxMessageSize: 5000000 }),
+ ]);
+ const user = userEvent.setup({ pointerEventsCheck: 0 });
+ renderWithProviders(<ClusterPage />);
+
+ const brokerRow = await screen.findByRole('row', { name:
/10\.101\.2\.11:10911/ });
+ await user.click(within(brokerRow).getByRole('button', { name: /^配\s*置$/
}));
+ const dialog = await screen.findByRole('dialog', { name: /配置 -
rocketmq-prod/ });
+ expect(within(dialog).getByLabelText('最大消息大小 (MB)')).toHaveValue('5');
+
+ await user.click(within(dialog).getByRole('button', { name: /预\s*览/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.previewClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: 5000000 }),
+ ),
+ );
+ await user.click(within(dialog).getByRole('button', { name: /^OK$/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.updateClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: 5000000 }),
+ ),
+ );
+ });
+
+ it('preservesMessageSizeBytesWhenAnotherConfigFieldIsEditedTest', async ()
=> {
+ clusterServiceMocks.listRegistryClusters.mockResolvedValue([
+ buildCluster({ maxMessageSize: 5000000 }),
+ ]);
+ const user = userEvent.setup({ pointerEventsCheck: 0 });
+ renderWithProviders(<ClusterPage />);
+
+ const brokerRow = await screen.findByRole('row', { name:
/10\.101\.2\.11:10911/ });
+ await user.click(within(brokerRow).getByRole('button', { name: /^配\s*置$/
}));
+ const dialog = await screen.findByRole('dialog', { name: /配置 -
rocketmq-prod/ });
+ const retentionInput = within(dialog).getByLabelText('文件保留时长 (小时)');
+ await user.clear(retentionInput);
+ await user.type(retentionInput, '96');
+
+ await user.click(within(dialog).getByRole('button', { name: /^OK$/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.updateClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: 5000000, fileReservedTime:
96 }),
+ ),
+ );
+ });
+
+ it.each([
+ { size: '6', expectedBytes: 6291456 },
+ { size: '5', expectedBytes: 5242880 },
+ ])(
+ 'appliesExplicitMessageSizeEditTo $size MiBInPreviewAndSaveTest',
+ async ({ size, expectedBytes }) => {
+ clusterServiceMocks.listRegistryClusters.mockResolvedValue([
+ buildCluster({ maxMessageSize: 5000000 }),
+ ]);
+ const user = userEvent.setup({ pointerEventsCheck: 0 });
+ renderWithProviders(<ClusterPage />);
+
+ const brokerRow = await screen.findByRole('row', { name:
/10\.101\.2\.11:10911/ });
+ await user.click(within(brokerRow).getByRole('button', { name: /^配\s*置$/
}));
+ const dialog = await screen.findByRole('dialog', { name: /配置 -
rocketmq-prod/ });
+ const sizeInput = within(dialog).getByLabelText('最大消息大小 (MB)');
+ expect(sizeInput).toHaveValue('5');
+ await user.clear(sizeInput);
+ await user.type(sizeInput, size);
+
+ await user.click(within(dialog).getByRole('button', { name: /预\s*览/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.previewClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: expectedBytes }),
+ ),
+ );
+ await user.click(within(dialog).getByRole('button', { name: /^OK$/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.updateClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: expectedBytes }),
+ ),
+ );
+ },
+ );
+
+ it('resetsMessageSizeEditTrackingWhenTheConfigDialogReopensTest', async ()
=> {
+ clusterServiceMocks.listRegistryClusters.mockResolvedValue([
+ buildCluster({ maxMessageSize: 5000000 }),
+ ]);
+ const user = userEvent.setup({ pointerEventsCheck: 0 });
+ renderWithProviders(<ClusterPage />);
+
+ const brokerRow = await screen.findByRole('row', { name:
/10\.101\.2\.11:10911/ });
+ const openConfigDialog = async () => {
+ await user.click(within(brokerRow).getByRole('button', { name: /^配\s*置$/
}));
+ return screen.findByRole('dialog', { name: /配置 - rocketmq-prod/ });
+ };
+ let dialog = await openConfigDialog();
+ const sizeInput = within(dialog).getByLabelText('最大消息大小 (MB)');
+ await user.clear(sizeInput);
+ await user.type(sizeInput, '6');
+ await user.click(within(dialog).getByRole('button', { name: /^cancel$/i
}));
+
+ dialog = await openConfigDialog();
+ expect(within(dialog).getByLabelText('最大消息大小 (MB)')).toHaveValue('5');
+ await user.click(within(dialog).getByRole('button', { name: /^OK$/ }));
+ await waitFor(() =>
+ expect(clusterServiceMocks.updateClusterConfig).toHaveBeenCalledWith(
+ expect.objectContaining({ maxMessageSize: 5000000 }),
+ ),
+ );
+ });
+
it('keeps the latest broker config preview after a superseded response
finishes last', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
const stalePreview = deferred<ClusterConfigPreviewResult>();
diff --git a/web/src/pages/cluster/index.tsx b/web/src/pages/cluster/index.tsx
index d3f9e7ba9..86fd9b36d 100644
--- a/web/src/pages/cluster/index.tsx
+++ b/web/src/pages/cluster/index.tsx
@@ -712,6 +712,8 @@ const ClusterPage = () => {
readQueueNums: cfg.readQueueNums ?? 8,
brokerPermission: cfg.brokerPermission ?? 6,
});
+ // setFieldsValue marks fields touched; only a user edit should replace
the original bytes.
+ configForm.setFields([{ name: 'maxMessageSizeMB', touched: false }]);
setConfigModalOpen(true);
};
@@ -725,7 +727,9 @@ const ClusterPage = () => {
instanceId: selectedInstanceIdRef.current,
...(selectedCluster.config ?? {}),
...configValues,
- maxMessageSize: maxMessageSizeMB * 1048576,
+ maxMessageSize: configForm.isFieldTouched('maxMessageSizeMB')
+ ? maxMessageSizeMB * 1048576
+ : (selectedCluster.config?.maxMessageSize ?? 4194304),
};
};