import { ValidationPipe } from '@nestjs/common'; import { AdminAuditQueryDto, MAX_LIMIT } from './audit-query.dto'; /** * Spec exercises the DTO through Nest's `ValidationPipe` with the * same options the BFF wires in `main.ts` (`whitelist`, * `forbidNonWhitelisted`, `transform`) — the test is therefore a * close stand-in for how the controller will see the parsed query * at runtime. */ const pipe = new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }); async function transform(input: unknown): Promise { return (await pipe.transform(input, { type: 'query', metatype: AdminAuditQueryDto, })) as AdminAuditQueryDto; } describe('AdminAuditQueryDto', () => { it('accepts an empty query (every filter is optional)', async () => { await expect(transform({})).resolves.toEqual({}); }); it('coerces numeric strings (URL query format) into numbers', async () => { const out = await transform({ limit: '25', offset: '100' }); expect(out.limit).toBe(25); expect(out.offset).toBe(100); }); it('rejects a limit above MAX_LIMIT', async () => { await expect(transform({ limit: String(MAX_LIMIT + 1) })).rejects.toThrow(); }); it('rejects a negative offset', async () => { await expect(transform({ offset: '-1' })).rejects.toThrow(); }); it('rejects a non-integer limit', async () => { await expect(transform({ limit: '12.5' })).rejects.toThrow(); }); it('accepts each valid audience enum value', async () => { await expect(transform({ audience: 'workforce' })).resolves.toMatchObject({ audience: 'workforce', }); await expect(transform({ audience: 'customer' })).resolves.toMatchObject({ audience: 'customer', }); }); it('rejects an unknown audience', async () => { await expect(transform({ audience: 'visitor' })).rejects.toThrow(); }); it('accepts each valid outcome enum value', async () => { for (const outcome of ['success', 'failure', 'denied']) { await expect(transform({ outcome })).resolves.toMatchObject({ outcome }); } }); it('rejects an ISO-8601 createdAtFrom that is not well-formed', async () => { await expect(transform({ createdAtFrom: 'not-a-date' })).rejects.toThrow(); }); it('rejects an unknown query key (forbidNonWhitelisted)', async () => { await expect(transform({ password: 'leak' })).rejects.toThrow(); }); it('rejects an over-long eventType (MaxLength guard)', async () => { await expect(transform({ eventType: 'a'.repeat(129) })).rejects.toThrow(); }); });