jest.mock('react-native-keychain', () => ({ ACCESSIBLE: { AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 'device-only' }, SECURITY_LEVEL: { SECURE_SOFTWARE: 1 }, STORAGE_TYPE: { AES_GCM_NO_AUTH: 'aes-gcm' }, getGenericPassword: jest.fn().mockResolvedValue(false), setGenericPassword: jest.fn(), resetGenericPassword: jest.fn(), })) import * as Keychain from 'react-native-keychain' import { loadPendingInviteToken, savePendingInviteToken, } from '../src/features/teams/pending-invite' const TOKEN = 'Abcdefghijklmnopqrstuvwxyz_12345' const PENDING_SERVICE = 'com.d3ro.voice.pending-team-invite.v1' describe('pending invitation secure storage', () => { beforeEach(() => { jest.clearAllMocks() ;(Keychain.getGenericPassword as jest.Mock).mockReset().mockResolvedValue(false) }) test('stores only a validated token in device-bound secure storage', async () => { ;(Keychain.setGenericPassword as jest.Mock).mockResolvedValue({ service: 'stored' }) await savePendingInviteToken(TOKEN) expect(Keychain.setGenericPassword).toHaveBeenCalledWith( 'pending-invite', TOKEN, expect.objectContaining({ service: 'com.d3ro.voice.pending-team-invite.v1', accessible: 'device-only', }), ) }) test('rejects malformed tokens before touching secure storage', async () => { await expect(savePendingInviteToken('short')).rejects.toMatchObject({ code: 'invalid-token', }) expect(Keychain.setGenericPassword).not.toHaveBeenCalled() }) test('clears corrupt or foreign secure-storage records', async () => { const pendingResponses = [ { username: 'other', password: TOKEN }, { username: 'pending-invite', password: 'short' }, ] ;(Keychain.getGenericPassword as jest.Mock).mockImplementation( async ({ service }: { service?: string } = {}) => ( service === PENDING_SERVICE ? pendingResponses.shift() ?? false : false ), ) ;(Keychain.resetGenericPassword as jest.Mock).mockResolvedValue(true) await expect(loadPendingInviteToken()).resolves.toBeNull() await expect(loadPendingInviteToken()).resolves.toBeNull() expect(Keychain.resetGenericPassword).toHaveBeenCalledTimes(2) }) test('returns a valid stored invitation token', async () => { ;(Keychain.getGenericPassword as jest.Mock).mockImplementation( async ({ service }: { service?: string } = {}) => service === PENDING_SERVICE ? { username: 'pending-invite', password: TOKEN } : false, ) await expect(loadPendingInviteToken()).resolves.toBe(TOKEN) }) })