xpeditis2.0/apps/backend/src/infrastructure/email/email.adapter.spec.ts
2026-09-14 20:02:12 +02:00

98 lines
3.4 KiB
TypeScript

import { Logger } from '@nestjs/common';
import { EmailAdapter } from './email.adapter';
/**
* Tous les emails doivent partir de l'adresse SMTP_FROM, la seule validee chez
* le relais SMTP (Brevo). Les invitations et les demandes aux transporteurs
* partaient d'adresses codees en dur et etaient refusees.
*/
function buildAdapter(smtpFrom = 'noreply@xpeditis.com') {
const settings: Record<string, string> = {
SMTP_FROM: smtpFrom,
APP_URL: 'https://app.preprod.xpeditis.com',
};
const config = { get: jest.fn((key: string, fallback?: unknown) => settings[key] ?? fallback) };
const templates = {
renderInvitationWithToken: jest.fn(async () => '<p>invitation</p>'),
renderCsvBookingRequest: jest.fn(async () => '<p>demande</p>'),
renderUserInvitation: jest.fn(async () => '<p>compte</p>'),
renderPasswordResetEmail: jest.fn(async () => '<p>reset</p>'),
};
const adapter = new EmailAdapter(config as never, templates as never);
// Parametre type : sans lui, Jest infere un appel sans argument et
// `mock.calls[0][0]` ne compile pas.
const sendMail = jest.fn(async (_mail: { from: string; to: string }) => ({
messageId: 'm-1',
accepted: ['x'],
rejected: [],
}));
(adapter as unknown as { transporter: { sendMail: typeof sendMail } }).transporter = { sendMail };
return { adapter, sendMail };
}
const sentFrom = (sendMail: jest.Mock) => (sendMail.mock.calls[0][0] as { from: string }).from;
describe('EmailAdapter — expediteur', () => {
beforeAll(() => {
jest.spyOn(Logger.prototype, 'log').mockImplementation(() => undefined);
});
it("envoie l'invitation depuis SMTP_FROM, avec le nom de l'equipe", async () => {
const { adapter, sendMail } = buildAdapter();
await adapter.sendInvitationWithToken(
'nouveau@client.test',
'Marie',
'Dupont',
'Acme',
'Paul Martin',
'https://app/register?token=t',
new Date('2026-09-21T10:00:00Z')
);
expect(sentFrom(sendMail)).toBe('"Équipe Xpeditis" <noreply@xpeditis.com>');
});
it('envoie la demande au transporteur depuis SMTP_FROM', async () => {
const { adapter, sendMail } = buildAdapter();
await adapter.sendCsvBookingRequest('booking@ssc.test', {
bookingId: 'b-1',
bookingNumber: 'XPD-2026-AAAAAA',
origin: 'FRLEH',
destination: 'EGEDK',
volumeCBM: 2.4,
weightKG: 850,
palletCount: 2,
priceUSD: 200,
priceEUR: 180,
primaryCurrency: 'EUR',
transitDays: 11,
containerType: 'LCL',
documents: [],
confirmationToken: 'token',
});
expect(sentFrom(sendMail)).toBe('"Xpeditis Bookings" <noreply@xpeditis.com>');
expect((sendMail.mock.calls[0][0] as { to: string }).to).toBe('booking@ssc.test');
});
it("suit l'adresse configuree, pour tous les types d'email", async () => {
const { adapter, sendMail } = buildAdapter('contact@mondomaine.fr');
await adapter.sendUserInvitation('a@b.test', 'Acme', 'Paul', 'Temp-1234');
await adapter.sendPasswordResetEmail('a@b.test', 'token');
await adapter.send({ to: 'a@b.test', subject: 'Test', html: '<p>t</p>' });
const froms = sendMail.mock.calls.map(call => (call[0] as { from: string }).from);
expect(froms).toEqual([
'"Équipe Xpeditis" <contact@mondomaine.fr>',
'"Xpeditis Sécurité" <contact@mondomaine.fr>',
'"Xpeditis" <contact@mondomaine.fr>',
]);
});
});