From 2ba7520dd443c90224ecf826122e516c489b595c Mon Sep 17 00:00:00 2001 From: ashim-hq Date: Mon, 20 Apr 2026 21:49:22 +0800 Subject: [PATCH] fix: handle non-ok HTTP responses in connection store checkHealth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkHealth previously ignored non-ok responses (e.g. 503), silently doing nothing when the server reported unhealthy. Add else branch to transition connected → disconnected on non-ok status. Add tests for connected → disconnected on fetch failure and offline → reconnected on health check success. --- apps/web/src/stores/connection-store.ts | 4 ++++ tests/unit/web/connection-store.test.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/apps/web/src/stores/connection-store.ts b/apps/web/src/stores/connection-store.ts index cb9159e9..3dff6e46 100644 --- a/apps/web/src/stores/connection-store.ts +++ b/apps/web/src/stores/connection-store.ts @@ -51,6 +51,10 @@ export const useConnectionStore = create((set, get) => ({ } else { set({ lastHealthCheck: Date.now() }); } + } else { + if (get().status === "connected") { + get().setDisconnected(); + } } } catch { if (get().status === "connected") { diff --git a/tests/unit/web/connection-store.test.ts b/tests/unit/web/connection-store.test.ts index 601d435a..4527e894 100644 --- a/tests/unit/web/connection-store.test.ts +++ b/tests/unit/web/connection-store.test.ts @@ -81,6 +81,24 @@ describe("connection-store", () => { expect(fetchMock).toHaveBeenCalledTimes(1); }); + it("checkHealth transitions connected → disconnected on fetch failure", async () => { + fetchMock.mockImplementation(failHealth); + expect(useConnectionStore.getState().status).toBe("connected"); + await useConnectionStore.getState().checkHealth(); + expect(useConnectionStore.getState().status).toBe("disconnected"); + expect(useConnectionStore.getState().failedSince).toBeTypeOf("number"); + }); + + it("checkHealth transitions offline → reconnected on success", async () => { + fetchMock.mockImplementation(okHealth); + useConnectionStore.getState().setOffline(); + expect(useConnectionStore.getState().status).toBe("offline"); + await useConnectionStore.getState().checkHealth(); + expect(useConnectionStore.getState().status).toBe("reconnected"); + expect(useConnectionStore.getState().failedSince).toBeNull(); + expect(useConnectionStore.getState().lastHealthCheck).toBeTypeOf("number"); + }); + it("startPolling is idempotent", () => { useConnectionStore.getState().setDisconnected(); useConnectionStore.getState().startPolling();