xpeditis2.0/apps/backend/src/application/services/csv-booking.bank-transfer.spec.ts
2026-09-14 20:02:12 +02:00

105 lines
3.3 KiB
TypeScript

import { Logger } from '@nestjs/common';
import { CsvBookingService } from './csv-booking.service';
import { CsvBookingStatus } from '@domain/entities/csv-booking.entity';
/**
* Validation d'un virement par un administrateur : le booking passe en PENDING
* et la demande part chez le transporteur. Un echec de cet email etait avale
* sans que l'administrateur le sache.
*/
function pendingTransferBooking() {
const booking = {
id: 'b-1',
bookingNumber: 'XPD-2026-AAAAAA',
userId: 'u-1',
organizationId: 'org-1',
carrierName: 'SSC Consolidation',
carrierEmail: 'booking@ssc.test',
origin: { getValue: () => 'FRLEH' },
destination: { getValue: () => 'EGEDK' },
volumeCBM: 2.4,
weightKG: 850,
palletCount: 2,
priceUSD: 200,
priceEUR: 180,
primaryCurrency: 'EUR',
transitDays: 11,
containerType: 'LCL',
status: CsvBookingStatus.PENDING_BANK_TRANSFER as CsvBookingStatus,
documents: [],
confirmationToken: 'token',
requestedAt: new Date('2026-09-01T10:00:00Z'),
notes: undefined,
getRouteDescription: () => 'FRLEH → EGEDK',
isExpired: () => false,
getPriceInCurrency: () => 180,
options: {},
markBankTransferValidated() {
booking.status = CsvBookingStatus.PENDING;
},
};
return booking;
}
function buildService(options: { emailFails: boolean }) {
const booking = pendingTransferBooking();
const csvBookingRepository = {
findById: jest.fn(async () => booking),
update: jest.fn(async (b: unknown) => b),
repository: {
findOne: jest.fn(async () => ({ bookingNumber: booking.bookingNumber, passwordHash: null })),
save: jest.fn(async (b: unknown) => b),
},
};
const emailAdapter = {
sendCsvBookingRequest: jest.fn(async () => {
if (options.emailFails) throw new Error('550 sender not valid');
}),
};
const notificationRepository = { save: jest.fn(async () => undefined) };
const unused = {} as never;
const service = new CsvBookingService(
csvBookingRepository as never,
notificationRepository as never,
emailAdapter as never,
unused,
unused,
unused,
unused
);
return { service, booking, emailAdapter };
}
describe('CsvBookingService.validateBankTransfer', () => {
beforeEach(() => {
jest.spyOn(Logger.prototype, 'log').mockImplementation(() => undefined);
jest.spyOn(Logger.prototype, 'error').mockImplementation(() => undefined);
});
afterEach(() => jest.restoreAllMocks());
it('active le booking et envoie la demande au transporteur', async () => {
const { service, booking, emailAdapter } = buildService({ emailFails: false });
const result = await service.validateBankTransfer('b-1');
expect(booking.status).toBe(CsvBookingStatus.PENDING);
expect(emailAdapter.sendCsvBookingRequest).toHaveBeenCalledWith(
'booking@ssc.test',
expect.objectContaining({ bookingNumber: 'XPD-2026-AAAAAA', confirmationToken: 'token' })
);
expect(result.carrierEmailSent).toBe(true);
}, 20000);
it("signale l'echec de l'email au lieu de l'avaler, sans annuler la validation", async () => {
const { service, booking } = buildService({ emailFails: true });
const result = await service.validateBankTransfer('b-1');
expect(booking.status).toBe(CsvBookingStatus.PENDING);
expect(result.carrierEmailSent).toBe(false);
}, 20000);
});