import { describe, expect, it, vi } from 'vitest' vi.mock('../../lib/storage', () => ({ getProtectedCustomerLicenseImageUrl: vi.fn((customerId: string) => `/api/v1/customers/${customerId}/license-image`), })) import { buildReservationWorkflow, normalizeOptionalString, parseReservationExtras, serializeReservationForDashboard, } from './reservation.presenter' describe('reservation.presenter boundary behavior', () => { it('treats missing, array and primitive extras as empty objects', () => { expect(parseReservationExtras(null)).toEqual({}) expect(parseReservationExtras(['paymentMode'])).toEqual({}) expect(parseReservationExtras('cash')).toEqual({}) expect(parseReservationExtras({ paymentMode: 'CASH' })).toEqual({ paymentMode: 'CASH' }) }) it('normalizes optional strings without preserving whitespace-only values', () => { expect(normalizeOptionalString(' hello ')).toBe('hello') expect(normalizeOptionalString(' ')).toBeNull() expect(normalizeOptionalString(null)).toBeNull() expect(normalizeOptionalString(undefined)).toBeNull() }) it('locks core edits after contract generation even when reservation is still confirmed', () => { expect(buildReservationWorkflow({ status: 'CONFIRMED', contractNumber: 'CTR-1', invoiceNumber: null, extras: {}, })).toMatchObject({ contractGenerated: true, closed: false, coreEditable: false, checkInInspectionEditable: true, }) }) it('marks closed reservations as immutable and exposes close metadata from extras only when strings', () => { expect(buildReservationWorkflow({ status: 'COMPLETED', contractNumber: null, invoiceNumber: null, extras: { reservationClosedAt: '2026-06-01T12:00:00.000Z', reservationClosedBy: 123 }, })).toMatchObject({ closed: true, closedAt: '2026-06-01T12:00:00.000Z', closedBy: null, coreEditable: false, returnEditable: false, checkOutInspectionEditable: false, }) }) it('serializes dashboard reservations with protected license URLs and payment mode extraction', () => { const result = serializeReservationForDashboard({ id: 'reservation_1', status: 'DRAFT', source: 'CARPLACE', contractNumber: null, invoiceNumber: null, paymentStatus: 'UNPAID', extras: { paymentMode: 'CARD' }, customer: { id: 'customer_1', driverLicense: null, dateOfBirth: null, address: {}, licenseImageUrl: '/storage/raw-license.jpg', licenseValidationStatus: 'PENDING', }, }) expect(result.paymentMode).toBe('CARD') expect(result.customer?.licenseImageUrl).toBe('/api/v1/customers/customer_1/license-image') expect(result.workflow.coreEditable).toBe(true) }) it('does not invent protected license URLs when customer has no stored license image', () => { const result = serializeReservationForDashboard({ id: 'reservation_1', status: 'DRAFT', source: 'CARPLACE', contractNumber: null, invoiceNumber: null, paymentStatus: 'UNPAID', extras: { paymentMode: 42 }, customer: { id: 'customer_1', driverLicense: null, dateOfBirth: null, address: {}, licenseImageUrl: null, licenseValidationStatus: 'PENDING', }, }) expect(result.paymentMode).toBeNull() expect(result.customer?.licenseImageUrl).toBeNull() }) })