feat(backend): #28 templates email validation compte (Handlebars)

- Templates .hbs parent principal, co-parent, AM + lien create-password
- app_name / app_url / expéditeur via ConfigService (sendEmail)
- validateUser: renouvelle token création MDP + envoi email si sans password
- Dist: assets *.hbs (nest-cli)
- Tests unitaires MailService (mock sendEmail)
- Doc liste tickets #28 terminé

Made-with: Cursor
This commit is contained in:
2026-04-17 18:34:04 +02:00
parent 68efa91c39
commit 94d9428c43
11 changed files with 439 additions and 32 deletions
@@ -0,0 +1,72 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MailService, ValidationAccountEmailKind } from './mail.service';
import { AppConfigService } from '../config/config.service';
describe('MailService (ticket #28)', () => {
let service: MailService;
let sendEmailSpy: jest.SpyInstance;
const mockConfigGet = jest.fn((key: string, defaultValue?: unknown) => {
const values: Record<string, unknown> = {
app_name: 'TestApp',
app_url: 'https://app.test/',
smtp_host: '127.0.0.1',
smtp_port: 1025,
smtp_secure: false,
smtp_auth_required: false,
smtp_user: '',
smtp_password: '',
email_from_name: 'Test',
email_from_address: 'noreply@test',
};
if (key in values) return values[key];
return defaultValue;
});
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
providers: [
MailService,
{
provide: AppConfigService,
useValue: { get: mockConfigGet },
},
],
}).compile();
service = module.get<MailService>(MailService);
sendEmailSpy = jest.spyOn(service, 'sendEmail').mockResolvedValue(undefined);
});
afterEach(() => {
sendEmailSpy.mockRestore();
});
it.each<[ValidationAccountEmailKind, string]>([
['parent_primary', 'Compte validé'],
['parent_coparent', 'Co-parent'],
['am', 'Inscription validée'],
])('sendValidatedAccountPasswordSetupEmail (%s) utilise Handlebars + lien token', async (kind, subjectPart) => {
const token = '11111111-2222-3333-4444-555555555555';
await service.sendValidatedAccountPasswordSetupEmail(
{
email: 'user@test.fr',
prenom: 'Jean',
nom: 'Dupont',
token,
numeroDossier: '2025-000001',
},
kind,
);
expect(sendEmailSpy).toHaveBeenCalledTimes(1);
const [, subject, html] = sendEmailSpy.mock.calls[0];
expect(subject).toContain('TestApp');
expect(subject).toContain(subjectPart);
expect(html).toContain('Jean');
expect(html).toContain('Dupont');
expect(html).toContain('2025-000001');
expect(html).toContain(`https://app.test/create-password?token=${encodeURIComponent(token)}`);
});
});