Files
tessera-ctl/apps/api/src/bug-reports/bug-reports.controller.spec.ts
T
schalli 716947228e feat(bug-reports): Herkunft der Fehlermeldung im Betreff-Kuerzel und als Zeile Herkunft ausweisen
WebView2 (Windows) sieht im User-Agent aus wie Edge, WebKitGTK (Linux) wie
Safari — im Postfach war eine Client-Meldung von einer Browser-Meldung
nicht zu unterscheiden. Neuer reiner Helfer origin.ts leitet aus vier
optionalen DTO-Feldern (Desktop-App) bzw. dem User-Agent (Browser) ein
Betreff-Kuerzel und eine Zeile "Herkunft: ..." ab; rein informativ,
laengenbegrenzt, nichts wird gespeichert (T-GZA-01). Browser-Pfad ist
damit Ende-zu-Ende fertig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh
2026-09-18 12:32:29 +02:00

94 lines
4.1 KiB
TypeScript

import 'reflect-metadata';
import { BadRequestException, ValidationPipe } from '@nestjs/common';
import { describe, expect, it } from 'vitest';
import { ROLES_KEY } from '../auth/decorators/roles.decorator';
import { BugReportsController } from './bug-reports.controller';
import { BugReportDto } from './dto/bug-report.dto';
/**
* BugReportsController.spec — NEU (quick-260914-m97, Fehler-melden-Knopf).
*
* Vier Tests an der Grenze Browser -> API:
* 1. die globale Pipe (`whitelist: true, transform: true`, wie in
* `main.ts`) entfernt Fremdfelder wie `tenantId` (T-M97-06) und
* normalisiert `errors` (multer/append-field liefert EIN Feld als
* String, mehrere als Array, keins als undefined);
* 2. die DTO-Grenzen greifen (31 Eintraege, 4001 Zeichen -> 400);
* 3. die Route steht JEDEM angemeldeten Benutzer offen — kein
* `@Roles`-Metadatum, Pfad `bug-reports`.
* 4. (quick-260918-gza) die vier neuen Client-Felder bleiben optional
* und laengenbegrenzt erhalten.
*/
const pipe = new ValidationPipe({ whitelist: true, transform: true });
const meta = { type: 'body' as const, metatype: BugReportDto };
const baseBody = {
page: '/x',
webVersion: 'v1',
webChannel: 'beta',
webCommit: '',
userAgent: 'UA',
viewport: '1x1',
clientTime: 't',
};
describe('BugReportsController (quick-260914-m97)', () => {
it('Test 1: whitelist entfernt tenantId; errors wird aus String/undefined/Array normalisiert', async () => {
const single = (await pipe.transform({ ...baseBody, errors: 'einzeln', tenantId: 'fremd' }, meta)) as any;
expect(Object.prototype.hasOwnProperty.call(single, 'tenantId')).toBe(false);
expect(single.errors).toEqual(['einzeln']);
const none = (await pipe.transform({ ...baseBody }, meta)) as any;
expect(none.errors).toEqual([]);
const many = (await pipe.transform({ ...baseBody, errors: ['a', 'b'] }, meta)) as any;
expect(many.errors).toEqual(['a', 'b']);
});
it('Test 2: Grenzen — 31 Eintraege oder 4001 Zeichen -> BadRequestException; 30 Eintraege und 4000 Zeichen gelingen', async () => {
const thirtyOne = Array.from({ length: 31 }, (_, i) => `e${i}`);
await expect(pipe.transform({ ...baseBody, errors: thirtyOne }, meta)).rejects.toThrow(BadRequestException);
await expect(
pipe.transform({ ...baseBody, description: 'x'.repeat(4001) }, meta),
).rejects.toThrow(BadRequestException);
const ok = (await pipe.transform(
{ ...baseBody, errors: thirtyOne.slice(0, 30), description: 'x'.repeat(4000) },
meta,
)) as any;
expect(ok.errors).toHaveLength(30);
expect(ok.description).toHaveLength(4000);
});
it('Test 3: nur angemeldet — kein @Roles-Metadatum auf submit, Controller-Pfad bug-reports', () => {
expect(Reflect.getMetadata(ROLES_KEY, BugReportsController.prototype.submit)).toBeUndefined();
expect(Reflect.getMetadata('path', BugReportsController)).toBe('bug-reports');
});
it('Test 4 (quick-260918-gza): die vier Client-Felder bleiben optional und laengenbegrenzt erhalten', async () => {
const withDesktop = (await pipe.transform(
{ ...baseBody, clientKind: 'desktop', clientOs: 'windows', clientVersion: '1.2.0', clientCommit: '' },
meta,
)) as any;
expect(withDesktop.clientKind).toBe('desktop');
expect(withDesktop.clientOs).toBe('windows');
expect(withDesktop.clientVersion).toBe('1.2.0');
expect(withDesktop.clientCommit).toBe('');
const withoutClientFields = (await pipe.transform({ ...baseBody }, meta)) as any;
expect(withoutClientFields.clientKind).toBeUndefined();
await expect(pipe.transform({ ...baseBody, clientKind: 'tablet' }, meta)).rejects.toThrow(BadRequestException);
await expect(pipe.transform({ ...baseBody, clientOs: 'x'.repeat(21) }, meta)).rejects.toThrow(
BadRequestException,
);
await expect(pipe.transform({ ...baseBody, clientVersion: 'x'.repeat(41) }, meta)).rejects.toThrow(
BadRequestException,
);
await expect(pipe.transform({ ...baseBody, clientCommit: 'x'.repeat(41) }, meta)).rejects.toThrow(
BadRequestException,
);
});
});