From b9d0bbf490f1c2098f367a016b772b98df12ff80 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2026 18:18:17 +0100 Subject: [PATCH] test(alerts): pin diagnosis refresh failure and disposal guards Protect the existing stale-response repair when a newer periodic read fails and when the overview is disposed. Both added tests fail with the response guards removed; 49 focused tests and TypeScript pass with runtime unchanged. This is regression coverage, not recipient-delivery or combined-candidate qualification. Change-source: pulse-maintainer --- .../__tests__/useAlertOverviewState.test.tsx | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx b/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx index 2b3a96ddc..8ee5e8e9f 100644 --- a/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx +++ b/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx @@ -85,6 +85,61 @@ describe('useAlertOverviewState', () => { expect(result.deliveryDiagnoses()).toEqual({}); }); + it('does not accept an older success when a newer periodic refresh fails', async () => { + let finishOlder!: (value: AlertDeliveryDiagnosis[]) => void; + const retained = { + alertIdentifier: 'a1', + reason: 'notifications_disabled', + } as AlertDeliveryDiagnosis; + vi.mocked(AlertsAPI.getDeliveryDiagnoses) + .mockResolvedValueOnce([retained]) + .mockReturnValueOnce( + new Promise((resolve) => { + finishOlder = resolve; + }), + ) + .mockRejectedValueOnce(new Error('refresh unavailable')); + const { result } = renderHook(() => + useAlertOverviewState({ + activeAlerts: () => ({ a1: makeAlert('a1', new Date().toISOString()) }), + overrides: () => [], + showAcknowledged: () => true, + updateAlert: vi.fn(), + }), + ); + await Promise.resolve(); + expect(result.deliveryDiagnoses()).toEqual({ a1: retained }); + await vi.advanceTimersByTimeAsync(60_000); + await vi.advanceTimersByTimeAsync(60_000); + expect(AlertsAPI.getDeliveryDiagnoses).toHaveBeenCalledTimes(3); + finishOlder([{ alertIdentifier: 'a1', reason: 'ready' } as AlertDeliveryDiagnosis]); + await Promise.resolve(); + expect(result.deliveryDiagnoses()).toEqual({ a1: retained }); + }); + + it('ignores pending responses and stops periodic reads after disposal', async () => { + let finish!: (value: AlertDeliveryDiagnosis[]) => void; + vi.mocked(AlertsAPI.getDeliveryDiagnoses).mockReturnValueOnce( + new Promise((resolve) => { + finish = resolve; + }), + ); + const { result, cleanup } = renderHook(() => + useAlertOverviewState({ + activeAlerts: () => ({ a1: makeAlert('a1', new Date().toISOString()) }), + overrides: () => [], + showAcknowledged: () => true, + updateAlert: vi.fn(), + }), + ); + cleanup(); + finish([{ alertIdentifier: 'a1', reason: 'ready' } as AlertDeliveryDiagnosis]); + await Promise.resolve(); + await vi.advanceTimersByTimeAsync(120_000); + expect(result.deliveryDiagnoses()).toEqual({}); + expect(AlertsAPI.getDeliveryDiagnoses).toHaveBeenCalledOnce(); + }); + it('owns overview stats, filtering, and acknowledge flows outside the tab shell', async () => { const now = Date.now(); const [activeAlerts] = createSignal>({