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

Reply via email to