fix(mail): retry SMTP transient + erreur API si email refus échoue

- Transporteur SMTP réutilisé (pool) au lieu d'une auth par email
- 3 tentatives avec backoff sur erreurs 454/timeout/coupure Dovecot
- refuseUser renvoie 503 si un email de refus échoue après retry
- Tests unitaires isTransientSmtpError

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-15 22:50:53 +02:00
co-authored by Cursor
parent f71943fa79
commit e1684676a7
3 changed files with 167 additions and 42 deletions
+15 -1
View File
@@ -1,7 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MailService, ValidationAccountEmailKind } from './mail.service';
import { MailService, ValidationAccountEmailKind, isTransientSmtpError } from './mail.service';
import { AppConfigService } from '../config/config.service';
describe('isTransientSmtpError', () => {
it('détecte les erreurs SMTP temporaires (454, coupure auth, timeout)', () => {
expect(isTransientSmtpError(new Error('Invalid login: 454 4.7.0 Temporary authentication failure'))).toBe(true);
expect(isTransientSmtpError(new Error('Connection lost to authentication server'))).toBe(true);
expect(isTransientSmtpError(Object.assign(new Error('timeout'), { code: 'ETIMEDOUT' }))).toBe(true);
expect(isTransientSmtpError(Object.assign(new Error('auth failed'), { responseCode: 454 }))).toBe(true);
});
it('ignore les erreurs permanentes (mauvaise adresse, refus définitif)', () => {
expect(isTransientSmtpError(new Error('Invalid login: 535 Authentication failed'))).toBe(false);
expect(isTransientSmtpError(new Error('Mailbox unavailable'))).toBe(false);
});
});
describe('MailService (ticket #28)', () => {
let service: MailService;
let sendEmailSpy: jest.SpyInstance;