import { describe, it, expect } from 'vitest'; import { derivePublicStatus, toPublicBerth } from '@/lib/services/public-berths'; import type { Berth, BerthMapData } from '@/lib/db/schema/berths'; function makeBerth(overrides: Partial = {}): Berth { return { id: 'b1', portId: 'p1', mooringNumber: 'A1', area: 'A', status: 'available', lengthFt: '206.69', widthFt: '46.56', draftFt: '14.5', lengthM: '63', widthM: '14.19', draftM: '4.42', widthIsMinimum: false, nominalBoatSize: '200', nominalBoatSizeM: '60.96', waterDepth: '16.08', waterDepthM: '4.9', waterDepthIsMinimum: false, sidePontoon: 'Quay PT', powerCapacity: '330', voltage: '480', mooringType: 'Side Pier / Med Mooring', cleatType: 'A5', cleatCapacity: '20-24 ton break load', bollardType: 'Bull bollard type B', bollardCapacity: '40 ton break load', access: 'Car (3t) to Vessel', price: '3528000', priceCurrency: 'USD', weeklyRateHighUsd: null, weeklyRateLowUsd: null, dailyRateHighUsd: null, dailyRateLowUsd: null, pricingValidUntil: null, bowFacing: 'East', berthApproved: false, tenureType: 'permanent', tenureYears: null, tenureStartDate: null, tenureEndDate: null, statusLastChangedBy: null, statusLastChangedReason: null, statusLastModified: null, statusOverrideMode: null, lastImportedAt: null, currentPdfVersionId: null, createdAt: new Date(), updatedAt: new Date(), ...overrides, }; } function makeMapData(overrides: Partial = {}): BerthMapData { return { id: 'm1', berthId: 'b1', svgPath: 'M0 0', x: '922.819', y: '930.721', transform: '', fontSize: '32', extraData: {}, updatedAt: new Date(), ...overrides, }; } describe('derivePublicStatus', () => { it('"sold" wins over the specific-interest signal', () => { expect(derivePublicStatus('sold', true)).toBe('Sold'); expect(derivePublicStatus('sold', false)).toBe('Sold'); }); it('"under_offer" maps to "Under Offer"', () => { expect(derivePublicStatus('under_offer', false)).toBe('Under Offer'); }); it('a specific-interest link promotes "available" → "Under Offer"', () => { expect(derivePublicStatus('available', true)).toBe('Under Offer'); }); it('plain available stays available', () => { expect(derivePublicStatus('available', false)).toBe('Available'); }); }); describe('toPublicBerth', () => { it('maps every NocoDB-style field with numeric coercion', () => { const out = toPublicBerth(makeBerth(), makeMapData(), false); expect(out.Id).toBe('b1'); expect(out['Mooring Number']).toBe('A1'); expect(out.Length).toBe(206.69); expect(out.Width).toBe(46.56); expect(out.Draft).toBe(14.5); expect(out['Power Capacity']).toBe(330); expect(out.Voltage).toBe(480); expect(out['Nominal Boat Size']).toBe(200); expect(out.Status).toBe('Available'); expect(out['Mooring Type']).toBe('Side Pier / Med Mooring'); expect(out['Bow Facing']).toBe('East'); expect(out.Area).toBe('A'); expect(out.Access).toBe('Car (3t) to Vessel'); }); it('inlines map data when present', () => { const out = toPublicBerth(makeBerth(), makeMapData(), false); expect(out['Map Data']).toEqual({ path: 'M0 0', x: '922.819', y: '930.721', transform: '', fontSize: '32', }); }); it('omits map data entirely when missing', () => { const out = toPublicBerth(makeBerth(), null, false); expect(out['Map Data']).toBeUndefined(); }); it('numeric fields stored as strings round-trip cleanly', () => { const out = toPublicBerth(makeBerth({ lengthFt: '42' }), null, false); expect(out.Length).toBe(42); }); it('null numeric inputs surface as null (not NaN)', () => { const out = toPublicBerth( makeBerth({ lengthFt: null, widthFt: null, draftFt: null }), null, false, ); expect(out.Length).toBeNull(); expect(out.Width).toBeNull(); expect(out.Draft).toBeNull(); }); it('promotes status to "Under Offer" when a specific-interest link exists', () => { const out = toPublicBerth(makeBerth({ status: 'available' }), null, true); expect(out.Status).toBe('Under Offer'); }); it('preserves "Sold" even when a specific-interest link exists (defensive)', () => { const out = toPublicBerth(makeBerth({ status: 'sold' }), null, true); expect(out.Status).toBe('Sold'); }); });