From 33fab95a730f76263c912317f5a4a1efdf67bbd8 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Thu, 15 Oct 2026 14:49:51 +0100 Subject: [PATCH] Fix a database's label staying at '[Disconnecting...]' when disconnecting it fails. #9624 (#10590) --- .../servers/databases/static/js/database.js | 2 + .../browser/node_connect_menus_spec.js | 58 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js index 111e2aaf8..87b422332 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js +++ b/web/pgadmin/browser/server_groups/servers/databases/static/js/database.js @@ -559,10 +559,12 @@ define('pgadmin.node.database', [ } catch (e) { console.warn(e.stack || e); } + t.setLabel(i, {label}); t.unload(i); } }).catch(function(error) { pgAdmin.Browser.notifier.pgRespErrorNotify(error); + t.setLabel(i, {label}); t.unload(i); }); }; diff --git a/web/regression/javascript/browser/node_connect_menus_spec.js b/web/regression/javascript/browser/node_connect_menus_spec.js index f50609903..a756fd822 100644 --- a/web/regression/javascript/browser/node_connect_menus_spec.js +++ b/web/regression/javascript/browser/node_connect_menus_spec.js @@ -8,12 +8,13 @@ ////////////////////////////////////////////////////////////// const mockPost = jest.fn(); +const mockDelete = jest.fn(); jest.mock('../../../pgadmin/static/js/api_instance', () => ({ __esModule: true, default: () => ({ post: mockPost, get: jest.fn(() => Promise.resolve({data: {}})), - delete: jest.fn(), + delete: mockDelete, }), parseApiError: (e) => e, })); @@ -30,7 +31,11 @@ const pgBrowser = { Collection: {extend: (props) => props}, Events: {trigger: jest.fn(), on: jest.fn()}, enable_disable_menus: jest.fn(), - notifier: {success: jest.fn(), info: jest.fn(), warning: jest.fn()}, + notifier: { + success: jest.fn(), info: jest.fn(), warning: jest.fn(), + errorText: jest.fn(), pgRespErrorNotify: jest.fn(), + confirm: jest.fn((_title, _msg, onOk) => onOk()), + }, stdW: {md: 650}, }; const pgAdmin = {Browser: pgBrowser}; @@ -106,6 +111,55 @@ describe('database node connection', () => { }); }); +describe('database node disconnection', () => { + let tree, dbItem, dbData; + + beforeEach(() => { + dbItem = {id: 'db'}; + dbData = {_type: 'database', connected: true, label: 'my db'}; + tree = { + selected: jest.fn(() => dbItem), + itemData: jest.fn(() => dbData), + setLabel: jest.fn(), + close: jest.fn(), + unload: jest.fn(), + }; + pgBrowser.tree = tree; + pgBrowser.notifier.errorText.mockClear(); + pgBrowser.notifier.pgRespErrorNotify.mockClear(); + mockDelete.mockReset(); + }); + + const disconnect = async () => { + const node = pgBrowser.Nodes['database']; + node.callbacks.disconnect_database.call(node, {item: dbItem}); + await flushPromises(); + }; + + it('restores the label when the disconnect request fails', async () => { + mockDelete.mockRejectedValue(new Error('404 Not Found')); + + await disconnect(); + + expect(tree.setLabel).toHaveBeenCalledWith( + dbItem, {label: '[Disconnecting...] my db', className: 'text-muted'}); + expect(tree.setLabel).toHaveBeenLastCalledWith(dbItem, {label: 'my db'}); + expect(pgBrowser.notifier.pgRespErrorNotify).toHaveBeenCalled(); + }); + + it('restores the label when the server refuses to disconnect', async () => { + mockDelete.mockResolvedValue({data: { + success: 0, errormsg: 'Could not disconnect.', + }}); + + await disconnect(); + + expect(tree.setLabel).toHaveBeenLastCalledWith(dbItem, {label: 'my db'}); + expect(pgBrowser.notifier.errorText).toHaveBeenCalledWith( + 'Could not disconnect.'); + }); +}); + describe('server node connection', () => { let tree, otherItem, serverItem, serverData;