d3ro-voice/apps/mobile-rn/__tests__/account-exit.test.ts
2026-08-29 18:33:45 +09:00

84 lines
3.1 KiB
TypeScript

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()
})
})