Spaces:
Sleeping
Sleeping
File size: 5,392 Bytes
1f5ea39 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | import { describe, it, expect } from 'vitest';
import { canonicalHash, mapFlightToReservation, normalizeFlight } from '../../../src/services/airtrail/airtrailMapper';
import type { AirtrailFlightRaw } from '../../../src/services/airtrail/airtrailClient';
function airport(over: Partial<AirtrailFlightRaw['from']> = {}): NonNullable<AirtrailFlightRaw['from']> {
return {
id: 1,
icao: 'KJFK',
iata: 'JFK',
name: 'John F. Kennedy Intl.',
lat: 40.6413,
lon: -73.7781,
tz: 'America/New_York',
country: 'US',
...over,
};
}
function flight(over: Partial<AirtrailFlightRaw> = {}): AirtrailFlightRaw {
return {
id: 42,
from: airport(),
to: airport({ id: 2, icao: 'EGLL', iata: 'LHR', name: 'London Heathrow', lat: 51.4706, lon: -0.4619, tz: 'Europe/London' }),
date: '2021-09-01',
datePrecision: 'day',
departure: '2021-09-01T23:00:00.000+00:00', // 19:00 local at JFK (EDT, UTC-4)
arrival: '2021-09-02T07:00:00.000+00:00', // 08:00 local at LHR (BST, UTC+1)
airline: { id: 1, icao: 'BAW', iata: 'BA', name: 'British Airways' },
flightNumber: 'BA178',
aircraft: { id: 1, icao: 'B772', name: 'Boeing 777' },
aircraftReg: 'G-VIIL',
flightReason: 'leisure',
note: 'window seat',
seats: [{ userId: 'u1', guestName: null, seat: 'window', seatNumber: '12A', seatClass: 'economy' }],
...over,
};
}
describe('airtrailMapper.normalizeFlight', () => {
it('prefers IATA codes and exposes the picker fields', () => {
const n = normalizeFlight(flight());
expect(n).toMatchObject({
id: '42',
fromCode: 'JFK',
toCode: 'LHR',
date: '2021-09-01',
airline: 'BAW',
flightNumber: 'BA178',
seatClass: 'economy',
});
});
it('falls back to ICAO when IATA is missing and tolerates null airports', () => {
const n = normalizeFlight(flight({ from: airport({ iata: null }), to: null }));
expect(n.fromCode).toBe('KJFK');
expect(n.toCode).toBeNull();
expect(n.toName).toBeNull();
});
});
describe('airtrailMapper.mapFlightToReservation', () => {
it('composes airport-local times from the instant + airport tz', () => {
const m = mapFlightToReservation(flight());
// 23:00 UTC at JFK in September is 19:00 EDT; date stays the AirTrail local date.
expect(m.reservation_time).toBe('2021-09-01T19:00');
// 07:00 UTC at LHR in September is 08:00 BST.
expect(m.reservation_end_time).toBe('2021-09-02T08:00');
});
it('builds two endpoints with codes, coords and timezones', () => {
const m = mapFlightToReservation(flight());
expect(m.endpoints).toHaveLength(2);
expect(m.endpoints[0]).toMatchObject({ role: 'from', code: 'JFK', lat: 40.6413, timezone: 'America/New_York', local_date: '2021-09-01', local_time: '19:00' });
expect(m.endpoints[1]).toMatchObject({ role: 'to', code: 'LHR', timezone: 'Europe/London', local_time: '08:00' });
expect(m.needs_review).toBe(0);
});
it('titles from the flight number, else the route', () => {
expect(mapFlightToReservation(flight()).title).toBe('BA178');
expect(mapFlightToReservation(flight({ airline: null, flightNumber: null })).title).toBe('JFK → LHR');
});
it('carries flight metadata', () => {
const m = mapFlightToReservation(flight());
expect(m.metadata).toMatchObject({ airline: 'BAW', flight_number: 'BA178', aircraft: 'B772', aircraft_reg: 'G-VIIL', flight_reason: 'leisure', seat: '12A' });
expect(m.type).toBe('flight');
expect(m.status).toBe('confirmed');
expect(m.notes).toBe('window seat');
});
it('flags needs_review for a non-day date precision', () => {
expect(mapFlightToReservation(flight({ datePrecision: 'month' })).needs_review).toBe(1);
});
it('flags needs_review and drops the endpoint when an airport has no coordinates', () => {
const m = mapFlightToReservation(flight({ from: airport({ lat: null, lon: null }) }));
expect(m.needs_review).toBe(1);
expect(m.endpoints.find(e => e.role === 'from')).toBeUndefined();
expect(m.endpoints.find(e => e.role === 'to')).toBeDefined();
});
it('leaves the end time null for a partial flight with no arrival', () => {
const m = mapFlightToReservation(flight({ arrival: null }));
expect(m.reservation_end_time).toBeNull();
expect(m.reservation_time).toBe('2021-09-01T19:00');
});
});
describe('airtrailMapper.canonicalHash', () => {
it('is stable for the same flight', () => {
expect(canonicalHash(flight())).toBe(canonicalHash(flight()));
});
it('changes when a meaningful field changes', () => {
expect(canonicalHash(flight())).not.toBe(canonicalHash(flight({ flightNumber: 'BA179' })));
expect(canonicalHash(flight())).not.toBe(canonicalHash(flight({ note: 'aisle seat' })));
});
it('is independent of seat ordering', () => {
const a = flight({
seats: [
{ userId: 'u1', guestName: null, seat: null, seatNumber: '1A', seatClass: 'economy' },
{ userId: 'u2', guestName: null, seat: null, seatNumber: '1B', seatClass: 'economy' },
],
});
const b = flight({
seats: [
{ userId: 'u2', guestName: null, seat: null, seatNumber: '1B', seatClass: 'economy' },
{ userId: 'u1', guestName: null, seat: null, seatNumber: '1A', seatClass: 'economy' },
],
});
expect(canonicalHash(a)).toBe(canonicalHash(b));
});
});
|