From 85f23dfdf478539a8740d4a92bedc93590c1c51b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 24 Sep 2026 19:33:15 +0200 Subject: [PATCH] test: check that session restore waits for the persisted stores together The audit listed the four hydration waits in `restoreSession()` as serial. They are already awaited together with `Promise.all`, and restore only reads the stores after all four have settled, so the order they finish in does not matter. The remaining queueing is inside AsyncStorage, whose native side runs reads one at a time. This test pins the concurrent wait: with all four stores stuck, restore gives up after one `HYDRATION_TIMEOUT_MS`, not four. --- src/stores/__tests__/auth-store.test.ts | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/stores/__tests__/auth-store.test.ts b/src/stores/__tests__/auth-store.test.ts index 61dfbd30..ae32bdda 100644 --- a/src/stores/__tests__/auth-store.test.ts +++ b/src/stores/__tests__/auth-store.test.ts @@ -39,6 +39,8 @@ import { jmapClient } from '../../api/jmap-client'; import { useAuthStore, HYDRATION_TIMEOUT_MS } from '../auth-store'; import { useAccountStore } from '../account-store'; import { useCalendarStore } from '../calendar-store'; +import { useContactsStore } from '../contacts-store'; +import { useEmailStore } from '../email-store'; const mockConnect = jmapClient.connect as ReturnType; const mockLogout = jmapClient.logout as ReturnType; @@ -181,6 +183,31 @@ describe('auth-store', () => { vi.useRealTimers(); } }); + + it('waits for the four persisted stores at the same time', async () => { + vi.useFakeTimers(); + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + const spies = [useAccountStore, useEmailStore, useCalendarStore, useContactsStore] + .map((store) => vi.spyOn(store.persist, 'hasHydrated').mockReturnValue(false)); + try { + let settled = false; + const restoring = useAuthStore.getState().restoreSession().finally(() => { settled = true; }); + + // One after another, four stuck stores would hold the session for + // four timeouts; waited on together, they cost one. + await vi.advanceTimersByTimeAsync(HYDRATION_TIMEOUT_MS - 1); + expect(settled).toBe(false); + await vi.advanceTimersByTimeAsync(1); + + expect(settled).toBe(true); + expect(await restoring).toBe(false); + expect(warn.mock.calls.filter(([m]) => String(m).includes('did not hydrate in time'))).toHaveLength(4); + } finally { + for (const spy of spies) spy.mockRestore(); + warn.mockRestore(); + vi.useRealTimers(); + } + }); }); describe('clearError', () => {