jest.mock('../src/features/devices/device-service', () => ({ unregisterCurrentDevice: jest.fn(), })) jest.mock('../src/features/notifications/notification-service', () => ({ detachPushRegistrationForLogout: jest.fn(), })) jest.mock('../src/lib/native-config', () => ({ getMobileRuntimeConfig: () => ({ installationId: 'installation' }), })) jest.mock('../src/lib/logout', () => ({ signOutWithLocalFallback: jest.fn(), })) import { performAccountLogout, type AccountLogoutOperations, } from '../src/lib/account-exit' const USER_ID = '11111111-1111-4111-8111-111111111111' const DEVICE_ID = '22222222-2222-4222-8222-222222222222' describe('explicit account logout ordering', () => { it('detaches push and unregisters the device before auth/session purge', async () => { const order: string[] = [] const purgeLocalSession = jest.fn(async () => { order.push('purge') }) const operations: AccountLogoutOperations = { detachPush: jest.fn(async () => { order.push('detach-push') }), unregisterDevice: jest.fn(async () => { order.push('unregister-device') }), installationId: () => 'installation-id', signOut: jest.fn(async (purge) => { order.push('sign-out') await purge() return { remoteRevocationConfirmed: true } }), } await expect(performAccountLogout({ userId: USER_ID, deviceId: DEVICE_ID, purgeLocalSession, }, operations)).resolves.toEqual({ remoteRevocationConfirmed: true }) expect(order).toEqual(['detach-push', 'unregister-device', 'sign-out', 'purge']) expect(operations.detachPush).toHaveBeenCalledWith(USER_ID, DEVICE_ID) expect(operations.unregisterDevice).toHaveBeenCalledWith(USER_ID, 'installation-id') }) it('continues to local privacy cleanup when server device unregister is offline', async () => { const purgeLocalSession = jest.fn(async () => undefined) const operations: AccountLogoutOperations = { detachPush: jest.fn(async () => undefined), unregisterDevice: jest.fn(async () => { throw new Error('private network detail') }), installationId: () => 'installation-id', signOut: jest.fn(async (purge) => { await purge() return { remoteRevocationConfirmed: false } }), } await expect(performAccountLogout({ userId: USER_ID, deviceId: null, purgeLocalSession, }, operations)).resolves.toEqual({ remoteRevocationConfirmed: false }) expect(purgeLocalSession).toHaveBeenCalledTimes(1) }) it('does not attempt auth logout before required push detach succeeds', async () => { const operations: AccountLogoutOperations = { detachPush: jest.fn(async () => { throw new Error('detach failed') }), unregisterDevice: jest.fn(), installationId: () => 'installation-id', signOut: jest.fn(), } await expect(performAccountLogout({ userId: USER_ID, deviceId: DEVICE_ID, purgeLocalSession: jest.fn(), }, operations)).rejects.toThrow('detach failed') expect(operations.unregisterDevice).not.toHaveBeenCalled() expect(operations.signOut).not.toHaveBeenCalled() }) })