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 45dc74ed fix: avoid false success messages when proxy refresh fails
(#674)
45dc74ed is described below
commit 45dc74ed649b48d04ffd7b0db36f92d71e9ed8d6
Author: yx9o <[email protected]>
AuthorDate: Fri Jul 31 16:16:50 2026 +0800
fix: avoid false success messages when proxy refresh fails (#674)
---
web/src/pages/studio/Proxy.tsx | 82 ++++++++++++------------
web/src/pages/studio/__tests__/Proxy.test.tsx | 92 +++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 41 deletions(-)
diff --git a/web/src/pages/studio/Proxy.tsx b/web/src/pages/studio/Proxy.tsx
index bdbae56c..9a36b0e4 100644
--- a/web/src/pages/studio/Proxy.tsx
+++ b/web/src/pages/studio/Proxy.tsx
@@ -75,47 +75,46 @@ const ProxyPage: React.FC = () => {
loadProxyNodes();
}
- function loadProxyNodes() {
+ async function loadProxyNodes() {
setLoading(true);
- queryProxyHomePage()
- .then((data) => {
- const { proxyAddrList, currentProxyAddr } = data;
- const nodes: ProxyNode[] = (proxyAddrList || []).map((addr) => ({
- key: addr,
- address: addr,
- status: 'healthy' as const,
- version: '5.3.0',
- connections: Math.floor(Math.random() * 1000) + 100,
- tps: Math.floor(Math.random() * 5000) + 1000,
- memory: Math.floor(Math.random() * 60) + 20,
- cpu: Math.floor(Math.random() * 50) + 10,
- uptime: `${Math.floor(Math.random() * 30) + 1}d`,
- isSelected: addr === currentProxyAddr,
- }));
- setProxyNodes(nodes);
+ try {
+ const { proxyAddrList, currentProxyAddr } = await queryProxyHomePage();
+ const nodes: ProxyNode[] = (proxyAddrList || []).map((addr) => ({
+ key: addr,
+ address: addr,
+ status: 'healthy' as const,
+ version: '5.3.0',
+ connections: Math.floor(Math.random() * 1000) + 100,
+ tps: Math.floor(Math.random() * 5000) + 1000,
+ memory: Math.floor(Math.random() * 60) + 20,
+ cpu: Math.floor(Math.random() * 50) + 10,
+ uptime: `${Math.floor(Math.random() * 30) + 1}d`,
+ isSelected: addr === currentProxyAddr,
+ }));
+ setProxyNodes(nodes);
- const healthyCount = nodes.filter((n) => n.status ===
'healthy').length;
- const totalConn = nodes.reduce((sum, n) => sum + n.connections, 0);
- const totalTPS = nodes.reduce((sum, n) => sum + n.tps, 0);
- setClusterStats({
- totalNodes: nodes.length,
- healthyNodes: healthyCount,
- totalConnections: totalConn,
- totalTPS,
- });
-
- if (currentProxyAddr) {
- localStorage.setItem('proxyAddr', currentProxyAddr);
- } else if (proxyAddrList && proxyAddrList.length > 0) {
- localStorage.setItem('proxyAddr', proxyAddrList[0]);
- }
- })
- .catch(() => {
- message.error(t('proxy.fetchListFailed'));
- })
- .finally(() => {
- setLoading(false);
+ const healthyCount = nodes.filter((n) => n.status === 'healthy').length;
+ const totalConn = nodes.reduce((sum, n) => sum + n.connections, 0);
+ const totalTPS = nodes.reduce((sum, n) => sum + n.tps, 0);
+ setClusterStats({
+ totalNodes: nodes.length,
+ healthyNodes: healthyCount,
+ totalConnections: totalConn,
+ totalTPS,
});
+
+ if (currentProxyAddr) {
+ localStorage.setItem('proxyAddr', currentProxyAddr);
+ } else if (proxyAddrList && proxyAddrList.length > 0) {
+ localStorage.setItem('proxyAddr', proxyAddrList[0]);
+ }
+ return true;
+ } catch {
+ message.error(t('proxy.fetchListFailed'));
+ return false;
+ } finally {
+ setLoading(false);
+ }
}
const handleViewConfig = (node: ProxyNode) => {
@@ -175,9 +174,10 @@ const ProxyPage: React.FC = () => {
});
};
- const handleRefresh = () => {
- loadProxyNodes();
- message.success(t('common.refreshSuccess'));
+ const handleRefresh = async () => {
+ if (await loadProxyNodes()) {
+ message.success(t('common.refreshSuccess'));
+ }
};
const renderStatus = (status: string) => {
diff --git a/web/src/pages/studio/__tests__/Proxy.test.tsx
b/web/src/pages/studio/__tests__/Proxy.test.tsx
new file mode 100644
index 00000000..22d3aa41
--- /dev/null
+++ b/web/src/pages/studio/__tests__/Proxy.test.tsx
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { App } from 'antd';
+import { queryProxyHomePage } from '../../../api/proxy';
+import { LangProvider } from '../../../i18n/LangContext';
+import ProxyPage from '../Proxy';
+
+vi.mock('../../../api/proxy', () => ({
+ addProxyAddr: vi.fn(),
+ queryProxyHomePage: vi.fn(),
+ removeProxyAddr: vi.fn(),
+}));
+
+beforeAll(() => {
+ Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: vi.fn().mockImplementation((query: string) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: vi.fn(),
+ removeListener: vi.fn(),
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
+ })),
+ });
+});
+
+const proxyHome = {
+ proxyAddrList: ['127.0.0.1:8081'],
+ currentProxyAddr: '127.0.0.1:8081',
+};
+
+function renderPage() {
+ return render(
+ <App>
+ <LangProvider>
+ <ProxyPage />
+ </LangProvider>
+ </App>,
+ );
+}
+
+describe('ProxyPage', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.mocked(queryProxyHomePage).mockResolvedValue(proxyHome);
+ });
+
+ it('shows success after the proxy list refreshes', async () => {
+ const user = userEvent.setup();
+ renderPage();
+ await screen.findByText('127.0.0.1:8081');
+
+ await user.click(screen.getByRole('button', { name: '刷新' }));
+
+ expect(await screen.findByText('刷新成功')).toBeInTheDocument();
+ await waitFor(() => expect(queryProxyHomePage).toHaveBeenCalledTimes(2));
+ });
+
+ it('does not show success when the proxy list refresh fails', async () => {
+ const user = userEvent.setup();
+ renderPage();
+ await screen.findByText('127.0.0.1:8081');
+ vi.mocked(queryProxyHomePage).mockRejectedValueOnce(new Error('network
error'));
+
+ await user.click(screen.getByRole('button', { name: '刷新' }));
+
+ expect(await screen.findByText('获取代理列表失败')).toBeInTheDocument();
+ expect(screen.queryByText('刷新成功')).not.toBeInTheDocument();
+ await waitFor(() => expect(queryProxyHomePage).toHaveBeenCalledTimes(2));
+ });
+});