From e080f53fb5755fe4702241cde87f85c4bca940d6 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Wed, 17 Jun 2026 19:15:45 -0400 Subject: [PATCH 1/8] fix(web): show OS update confirm when key state has an error The update confirmation body and footer were gated on `!stateDataError`, which suppressed the entire confirm UI whenever the server had any state error (e.g. an EGUID key/GUID mismatch). A standalone OS update on such a server rendered a blank "install update" modal with no confirm button, even though OS update eligibility is independent of key validity. Swap the guard to the existing `showPostInstallKeyError` computed, which is only true after a key install left the server in an error state. This preserves the original intent (prioritize the key error in the combined key-install + update flow) without blocking standalone updates. Add tests covering the standalone-update-with-state-error case and the combined key-install error case. --- .../components/CallbackFeedback.test.ts | 55 +++++++++++++++++++ .../UserProfile/CallbackFeedback.vue | 4 +- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/web/__test__/components/CallbackFeedback.test.ts b/web/__test__/components/CallbackFeedback.test.ts index abdb0e8a64..2b4c5f0be9 100644 --- a/web/__test__/components/CallbackFeedback.test.ts +++ b/web/__test__/components/CallbackFeedback.test.ts @@ -310,6 +310,61 @@ describe('CallbackFeedback.vue', () => { expect(wrapper.find('.modal').attributes('data-success')).toBe('false'); }); + describe('OS update confirmation', () => { + it('renders the update confirmation when a standalone update lands on a server with a state error', () => { + updateOsStatus.value = 'confirming'; + callbackUpdateRelease.value = { name: 'Unraid 7.3.1' }; + osVersion.value = '7.3.0'; + // Server is in an error state (e.g. EGUID GUID mismatch) but no key was installed + stateDataError.value = true; + keyInstallStatus.value = 'ready'; + + const wrapper = mountComponent(); + + expect(wrapper.find('h1').text()).toBe('Update Unraid OS confirmation required'); + expect(wrapper.text()).toContain('Current Version: Unraid 7.3.0'); + expect(wrapper.text()).toContain('New Version: Unraid 7.3.1'); + expect(wrapper.text()).toContain('Confirm and start update'); + }); + + it('confirms the update when the confirm button is clicked', async () => { + updateOsStatus.value = 'confirming'; + callbackUpdateRelease.value = { name: 'Unraid 7.3.1' }; + stateDataError.value = true; + keyInstallStatus.value = 'ready'; + + const wrapper = mountComponent(); + + const confirmButton = wrapper + .findAll('button') + .find((button) => button.text() === 'Confirm and start update'); + expect(confirmButton).toBeDefined(); + await confirmButton!.trigger('click'); + + expect(mockInstallOsUpdate).toHaveBeenCalledTimes(1); + expect(mockSetCallbackStatus).toHaveBeenCalledWith('ready'); + }); + + it('suppresses the update confirmation when a key install left the server in an error state', () => { + updateOsStatus.value = 'confirming'; + callbackUpdateRelease.value = { name: 'Unraid 7.3.1' }; + osVersion.value = '7.3.0'; + // Combined key-install + update flow where the install errored + callbackStatus.value = 'success'; + keyActionType.value = 'purchase'; + keyInstallStatus.value = 'success'; + keyType.value = 'Pro'; + stateDataError.value = true; + callbackCallsCompleted.value = true; + + const wrapper = mountComponent(); + + expect(wrapper.text()).not.toContain('New Version: Unraid 7.3.1'); + expect(wrapper.text()).not.toContain('Confirm and start update'); + expect(wrapper.text()).toContain('Post Install License Key Error'); + }); + }); + it('reloads the page when the modal is dismissed after a callback action', async () => { const mockReload = vi.fn(); diff --git a/web/src/components/UserProfile/CallbackFeedback.vue b/web/src/components/UserProfile/CallbackFeedback.vue index 47d3430866..0239ed2335 100644 --- a/web/src/components/UserProfile/CallbackFeedback.vue +++ b/web/src/components/UserProfile/CallbackFeedback.vue @@ -327,7 +327,7 @@ const showPostInstallKeyError = computed(() => /> - -