import { UnauthorizedException } from '@nestjs/common'; import { describe, expect, it, vi } from 'vitest'; import { forTenant } from '../../prisma/prisma-tenant.extension'; import { JwtStrategy } from './jwt.strategy'; vi.mock('../../prisma/prisma-tenant.extension', () => ({ forTenant: vi.fn((p: unknown) => p), })); /** * JwtStrategy.validate — seit quick-260930 kommen Rolle, Aktiv-Status und * Kennwort-Pflicht bei jeder Anfrage aus der Datenbank, nicht aus dem Token * (Rollenaenderung/Deaktivierung wirkt sofort). Direkte Konstruktion ohne * Nest-Testmodul, Muster aus `../../tenant/tenant.guard.spec.ts`. */ function makeConfigService() { return { get: () => 'test-secret' } as any; } type Row = { id: string; username: string; role: string; tenantId: string; isActive: boolean; mustChangePassword: boolean; } | null; function makePrisma(row: Row) { return { user: { findUnique: vi.fn(async () => row) } } as any; } const payload = { sub: 'u1', username: 'kschaller', role: 'SUPER_ADMIN' as const, tenantId: 't1', mustChangePassword: false, }; const dbRow = { id: 'u1', username: 'kschaller', role: 'ADMIN', tenantId: 't1', isActive: true, mustChangePassword: false, }; describe('JwtStrategy.validate', () => { it('Rolle kommt aus der Datenbank, nicht aus dem Token (herabgestufter Super-Admin ist sofort Admin)', async () => { const prisma = makePrisma(dbRow); const strategy = new JwtStrategy(makeConfigService(), prisma); const result = await strategy.validate(payload); expect(result).toEqual({ id: 'u1', username: 'kschaller', role: 'ADMIN', tenantId: 't1', mustChangePassword: false, }); expect(forTenant).toHaveBeenCalledWith(prisma, 't1'); expect(prisma.user.findUnique).toHaveBeenCalledWith( expect.objectContaining({ where: { id: 'u1' } }), ); }); it('deaktiviertes Konto: 401, auch mit gueltigem Token', async () => { const strategy = new JwtStrategy(makeConfigService(), makePrisma({ ...dbRow, isActive: false })); await expect(strategy.validate(payload)).rejects.toBeInstanceOf(UnauthorizedException); }); it('geloeschtes Konto: 401', async () => { const strategy = new JwtStrategy(makeConfigService(), makePrisma(null)); await expect(strategy.validate(payload)).rejects.toBeInstanceOf(UnauthorizedException); }); it('Konto gehoert nicht (mehr) zum Mandanten aus dem Token: 401', async () => { const strategy = new JwtStrategy(makeConfigService(), makePrisma({ ...dbRow, tenantId: 't2' })); await expect(strategy.validate(payload)).rejects.toBeInstanceOf(UnauthorizedException); }); it('Kennwort-Pflicht kommt aus der Datenbank (vom Administrator nachtraeglich gesetzt)', async () => { const strategy = new JwtStrategy( makeConfigService(), makePrisma({ ...dbRow, mustChangePassword: true }), ); const result = await strategy.validate(payload); expect(result.mustChangePassword).toBe(true); }); });