import { UnauthorizedException } from '@nestjs/common'; import * as bcrypt from 'bcryptjs'; import { ReauthService } from './reauth.service'; describe('ReauthService', () => { const baseUser = { id: 'u1', email: 'a@b.de', passwordHash: bcrypt.hashSync('richtig', 4), mfaEnabled: false, mfaSecret: null, mfaLastTimeStep: null, authLockedUntil: null, isActive: true, }; const accountGuard = { isLocked: jest.fn(), recordFailure: jest.fn(), recordSuccess: jest.fn(), consumeTotp: jest.fn(), }; const prisma = { user: { findUnique: jest.fn() } }; const service = new ReauthService(prisma as never, accountGuard as never); beforeEach(() => { jest.resetAllMocks(); prisma.user.findUnique.mockResolvedValue(baseUser); accountGuard.isLocked.mockReturnValue(false); }); it('sagt bei falschem Passwort, dass es nicht stimmt', async () => { await expect(service.verify('u1', { password: 'falsch' })).rejects.toThrow( new UnauthorizedException('Das Passwort stimmt nicht.'), ); expect(accountGuard.recordFailure).toHaveBeenCalledWith('u1'); }); it('sagt bei falschem Code, dass er nicht stimmt', async () => { prisma.user.findUnique.mockResolvedValue({ ...baseUser, mfaEnabled: true }); accountGuard.consumeTotp.mockResolvedValue(false); await expect( service.verify('u1', { password: 'richtig', mfaCode: '000000' }), ).rejects.toThrow(new UnauthorizedException('Der Code stimmt nicht.')); }); it('nennt bei gesperrtem Konto die Sperre', async () => { accountGuard.isLocked.mockReturnValue(true); await expect(service.verify('u1', { password: 'richtig' })).rejects.toThrow( new UnauthorizedException( 'Zu viele Fehlversuche. Bestätigen ist für 15 Minuten gesperrt.', ), ); }); it('lässt richtige Angaben durch', async () => { await expect( service.verify('u1', { password: 'richtig' }), ).resolves.toBeUndefined(); expect(accountGuard.recordSuccess).toHaveBeenCalledWith('u1'); }); });