feat(quick-260914-m97): Fehlermeldungen per E-Mail — Empfaenger in SmtpConfig (Migration), MailService-Anhaenge, Modul bug-reports mit Drossel, PNG-Pruefung und Mandant aus der Sitzung
- SmtpConfig.bugReportRecipient (nullable, additive Migration 20260914170000), DTO @IsOptional @IsEmail, SAFE_SELECT, getBugReportRecipient gebunden - MailService: Versandkern deliver (wirft, Anhaenge), sendViaTenantTransport bleibt verschluckender Mantel (T-02-12), sendBugReport laesst Fehler durch - POST /bug-reports: Multipart 4 MiB je Route, alle angemeldeten Rollen, Drossel 5/10 min -> 429, PNG-Signatur -> 400, kein Empfaenger -> 409, Versandfehler -> 502, eine Protokollzeile - Falsifizierungen (a)-(d) als Specs; @Expose() im DTO, damit errors auch bei fehlendem Feld zu [] wird - Doku-Zeile fuer rls-access-inventory, TESSERA_BUGREPORT_TO in docker-compose.prod.yml Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
@@ -18,7 +18,14 @@ import { MailService } from './mail.service';
|
||||
* protokolliert, `close()` trotzdem gerufen.
|
||||
*
|
||||
* `nodemailer` wird per `vi.mock` ersetzt (wie in settings.service.spec.ts)
|
||||
* — kein echter Transport, lokal gibt es keinen `mailhog`.
|
||||
* — kein echter Transport, der lokale `mailhog` aus docker-compose.dev.yml
|
||||
* ist nur fuer den Browser-Check gedacht.
|
||||
*
|
||||
* Erweitert in quick-260914-m97 (Fehler-melden-Knopf): Tests 5 und 6 pinnen
|
||||
* `sendBugReport` — Anhaenge werden 1:1 an `sendMail` durchgereicht, und
|
||||
* Fehler gehen bewusst NACH AUSSEN (der Anwender soll wissen, ob sein
|
||||
* Bericht ankam), waehrend `sendPasswordResetEmail` weiterhin verschluckt
|
||||
* (T-02-12 unveraendert, Gegenprobe im selben Test).
|
||||
*/
|
||||
|
||||
let mockSendMail = vi.fn(async (_mail: unknown) => ({}));
|
||||
@@ -196,4 +203,48 @@ describe('MailService — Transport je Versand nach Mandant des Empfaengers (260
|
||||
expect(JSON.stringify(errorSpy.mock.calls[0])).not.toContain('geheim-a');
|
||||
expect(mockClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('Test 5 (260914-m97): sendBugReport reicht to/subject/text und den PNG-Anhang unveraendert an sendMail durch, from = fromAddress des Mandanten, close() gerufen', async () => {
|
||||
const settings = makeFakeSettings({ t1: configA });
|
||||
const service = new MailService(settings as any, makeFakeConfig({}) as any);
|
||||
const png = Buffer.from([1, 2, 3]);
|
||||
|
||||
await service.sendBugReport('t1', 'fehler@a.example.invalid', {
|
||||
subject: 'S',
|
||||
text: 'T',
|
||||
attachments: [{ filename: 'x.png', content: png, contentType: 'image/png' }],
|
||||
});
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledTimes(1);
|
||||
const sent = mockSendMail.mock.calls[0][0] as any;
|
||||
expect(sent.from).toBe('noreply@a.example.invalid');
|
||||
expect(sent.to).toBe('fehler@a.example.invalid');
|
||||
expect(sent.subject).toBe('S');
|
||||
expect(sent.text).toBe('T');
|
||||
expect(sent.attachments).toHaveLength(1);
|
||||
expect(sent.attachments[0].filename).toBe('x.png');
|
||||
expect(sent.attachments[0].contentType).toBe('image/png');
|
||||
expect(Buffer.isBuffer(sent.attachments[0].content)).toBe(true);
|
||||
expect((sent.attachments[0].content as Buffer).equals(png)).toBe(true);
|
||||
expect(mockClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Test 6 (260914-m97): sendBugReport laesst Transportfehler DURCH (rejects), close() trotzdem; Gegenprobe: sendPasswordResetEmail verschluckt denselben Fehler weiterhin (T-02-12)', async () => {
|
||||
mockSendMail = vi.fn(async () => {
|
||||
throw new Error('ECONNREFUSED smtp-a.example.invalid');
|
||||
});
|
||||
const settings = makeFakeSettings({ t1: configA });
|
||||
const service = new MailService(settings as any, makeFakeConfig({}) as any);
|
||||
const errorSpy = vi.spyOn((service as any).logger, 'error').mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
service.sendBugReport('t1', 'fehler@a.example.invalid', { subject: 'S', text: 'T', attachments: [] }),
|
||||
).rejects.toThrow('ECONNREFUSED');
|
||||
expect(mockClose).toHaveBeenCalledTimes(1);
|
||||
|
||||
await expect(
|
||||
service.sendPasswordResetEmail('alice@a.example.invalid', 'tok-1', 't1'),
|
||||
).resolves.toBeUndefined();
|
||||
expect(mockClose).toHaveBeenCalledTimes(2);
|
||||
expect(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user