7ff2dbb139
Build & Deploy / Build & Push Docker Image (push) Successful in 2m55s
Test / Type Check (all packages) (push) Successful in 54s
Build & Deploy / Deploy to VPS (push) Successful in 4s
Test / API Unit Tests (push) Failing after 48s
Test / Homepage Unit Tests (push) Successful in 43s
Test / Storefront Unit Tests (push) Failing after 40s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Failing after 42s
Test / API Integration Tests (push) Successful in 1m0s
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
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()
|
|
})
|
|
})
|