diff --git a/client/src/components/Admin/storage/BackendForm.test.tsx b/client/src/components/Admin/storage/BackendForm.test.tsx index ea5df7472..1ae59f505 100644 --- a/client/src/components/Admin/storage/BackendForm.test.tsx +++ b/client/src/components/Admin/storage/BackendForm.test.tsx @@ -120,4 +120,15 @@ describe('BackendForm', () => { }); expect(committed.options).not.toHaveProperty('region'); }); + + it('FE-ADMIN-STORF-009: renaming onto another existing backend warns and blocks Apply', () => { + renderForm({ initial: S3_INITIAL }); + expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled(); + fireEvent.change(screen.getByLabelText(/Name/), { target: { value: 'uploads-local' } }); + expect(screen.getByText(/A backend named uploads-local already exists/)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled(); + // Keeping the original name stays allowed. + fireEvent.change(screen.getByLabelText(/Name/), { target: { value: 'off-box' } }); + expect(screen.getByRole('button', { name: 'Apply' })).toBeEnabled(); + }); }); diff --git a/client/src/components/Admin/storage/BackendForm.tsx b/client/src/components/Admin/storage/BackendForm.tsx index 5574c7c21..54b20e156 100644 --- a/client/src/components/Admin/storage/BackendForm.tsx +++ b/client/src/components/Admin/storage/BackendForm.tsx @@ -62,7 +62,7 @@ export default function BackendForm({ if (field.kind === 'backend-ref-list') return Array.isArray(value) && value.length > 0 return typeof value === 'string' && value.trim() !== '' } - const duplicate = initial === null && backendNames.includes(name.trim()) + const duplicate = name.trim() !== (initial?.name ?? '') && backendNames.includes(name.trim()) const blocked = hasPlaintextSecret({ type, options: values }) && !encryptionReady const canApply = name.trim() !== '' && !duplicate && fields.every((f) => !f.required || filled(f)) diff --git a/client/src/mobile/screens/admin/MAdminStoragePanel.tsx b/client/src/mobile/screens/admin/MAdminStoragePanel.tsx index c8ad9a9f2..7d8c42ad5 100644 --- a/client/src/mobile/screens/admin/MAdminStoragePanel.tsx +++ b/client/src/mobile/screens/admin/MAdminStoragePanel.tsx @@ -68,7 +68,7 @@ function MBackendForm({ if (field.kind === 'backend-ref-list') return Array.isArray(value) && value.length > 0 return typeof value === 'string' && value.trim() !== '' } - const duplicate = initial === null && backendNames.includes(name.trim()) + const duplicate = name.trim() !== (initial?.name ?? '') && backendNames.includes(name.trim()) const blocked = hasPlaintextSecret({ type, options: values }) && !encryptionReady const canApply = name.trim() !== '' && !duplicate && fields.every((f) => !f.required || filled(f)) diff --git a/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx b/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx index ee354f3dd..11c3f789e 100644 --- a/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx +++ b/client/tests/unit/mobile/admin/MAdminStoragePanel.test.tsx @@ -90,4 +90,13 @@ describe('MAdminStoragePanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); expect(await screen.findByText('registry says no')).toBeInTheDocument(); }); + + it('FE-MOB-MSTOR-005: renaming onto another existing backend warns and blocks Apply', async () => { + await renderPanel(); + fireEvent.click(within(screen.getByTestId('m-storage-backend-off-box')).getByRole('button', { name: 'Edit' })); + const nameInput = screen.getByDisplayValue('off-box'); + fireEvent.change(nameInput, { target: { value: 'uploads-local' } }); + expect(screen.getByText(/A backend named uploads-local already exists/)).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Apply' })).toBeDisabled(); + }); });