72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
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)
|
|
})
|
|
})
|