Files
gods-eye-view/src/tools/queries/aviation.test.mjs
T
Rudy Mizrahi Celekli fa8e3e989f fix(tools): exclude unknown altitude from requested aircraft bands (#1115)
aircraft_in_area compared a nullable altitude, so a contact with neither barometric nor geometric altitude matched max_altitude_m as though it were at zero. Require a reported finite altitude only when a band is requested; unfiltered queries keep the contact.
2026-10-16 14:53:50 -07:00

450 lines
13 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { composeCatalog, coreTools } from '../index.js';
import { LiveSourceError } from '../../sources/live/contract.js';
const record = (id, latitude, longitude, extra = {}) => ({
id,
reference: id,
latitude,
longitude,
callsign: null,
originCountry: null,
positionTimeMs: Date.UTC(2026, 0, 1),
contactTimeMs: Date.UTC(2026, 0, 1, 0, 0, 5),
baroAltitudeM: 10000,
ellipsoidAltitudeM: null,
onGround: false,
speedMps: 230.04,
courseDeg: 90.4,
verticalRateMps: 0,
category: null,
typeCode: null,
registration: null,
operator: null,
...extra,
});
const snapshot = (records, extra = {}) => ({
records,
complete: true,
source: 'Test feed',
coverage: 'test coverage',
observedAtMs: Date.UTC(2026, 0, 1),
freshness: 'current',
...extra,
});
const feed = (records, { track = [], enrichment = {}, ...extra } = {}) => {
const calls = [];
return {
calls,
async getSnapshot(query) {
calls.push(query);
return snapshot(records, extra);
},
async getTrack(reference) {
calls.push(reference);
return { records: track, complete: false };
},
async getEnrichment({ kind, id }) {
calls.push(`${kind}:${id}`);
return enrichment[`${kind}:${id}`] ?? { found: false };
},
};
};
const civil = [
record('aaa111', 37.7, -122.4, {
callsign: 'UAL123',
originCountry: 'United States',
}),
record('bbb222', 37.58, -122.28, { onGround: true, baroAltitudeM: 0 }),
record('ccc333', 51.5, -0.1, { callsign: 'BAW1' }),
];
const military = [
record('ae0001', 37.6, -122.3, {
callsign: 'RCH123',
registration: '07-7175',
typeCode: 'C17',
}),
];
const bay = { lat: 37.6, lon: -122.3, radius_km: 60 };
test('aircraft in an area are filtered, nearest first, with source details', async () => {
const aircraft = feed(civil);
const catalog = composeCatalog({ tools: coreTools, services: { aircraft } });
const result = await catalog.call('aircraft_in_area', { area: bay });
assert.equal(result.summary, '2 aircraft in 60 km around 37.600, -122.300.');
assert.deepEqual(
result.data.rows.map((row) => row.id),
['bbb222', 'aaa111'],
);
assert.deepEqual(aircraft.calls[0], { latitude: 37.6, longitude: -122.3 });
assert.deepEqual(result.data.rows[1], {
id: 'aaa111',
callsign: 'UAL123',
lat: 37.7,
lon: -122.4,
altitude_m: 10000,
on_ground: false,
speed_mps: 230,
course_deg: 90,
vertical_rate_mps: 0,
type_code: null,
registration: null,
operator: null,
origin_country: 'United States',
last_contact: '2026-01-01T00:00:05.000Z',
distance_km: 14.2,
});
assert.equal(result.data.source, 'Test feed');
assert.equal(result.data.observed_at, '2026-01-01T00:00:00.000Z');
const airborne = await catalog.call('aircraft_in_area', {
area: bay,
airborne_only: true,
});
assert.deepEqual(
airborne.data.rows.map((row) => row.id),
['aaa111'],
);
const low = await catalog.call('aircraft_in_area', {
area: bay,
max_altitude_m: 100,
});
assert.deepEqual(
low.data.rows.map((row) => row.id),
['bbb222'],
);
});
test('the military feed is used on request and reported when missing', async () => {
const catalog = composeCatalog({
tools: coreTools,
services: {
aircraft: feed(civil),
military: feed(military, { freshness: 'stale' }),
},
});
const result = await catalog.call('aircraft_in_area', {
area: bay,
military: true,
});
assert.equal(
result.summary,
'1 military aircraft in 60 km around 37.600, -122.300 (data may be stale).',
);
assert.equal(result.data.rows[0].registration, '07-7175');
const civilOnly = composeCatalog({
tools: coreTools,
services: { aircraft: feed(civil) },
});
await assert.rejects(
civilOnly.call('aircraft_in_area', { area: bay, military: true }),
(error) => error.code === 'unsupported',
);
});
test('aircraft are found by exactly one identifier across feeds', async () => {
const catalog = composeCatalog({
tools: coreTools,
services: { aircraft: feed(civil), military: feed(military) },
});
const byCallsign = await catalog.call('find_aircraft', {
callsign: 'ual123',
});
assert.equal(byCallsign.summary, 'Found 1 aircraft with callsign UAL123.');
assert.deepEqual(
byCallsign.data.rows.map((row) => row.id),
['aaa111'],
);
assert.equal(byCallsign.data.rows[0].distance_km, undefined);
const byRegistration = await catalog.call('find_aircraft', {
registration: '07-7175',
});
assert.deepEqual(
byRegistration.data.rows.map((row) => row.id),
['ae0001'],
);
const none = await catalog.call('find_aircraft', { icao24: 'ABCDEF' });
assert.equal(
none.summary,
'No aircraft with icao24 ABCDEF is currently reported.',
);
await assert.rejects(catalog.call('find_aircraft', {}), /exactly one/);
await assert.rejects(
catalog.call('find_aircraft', { callsign: 'UAL123', icao24: 'aaa111' }),
/exactly one/,
);
await assert.rejects(
catalog.call('find_aircraft', { icao24: 'xyz' }),
/must match/,
);
});
test('tracks are ordered and thinned to 200 points', async () => {
const start = Date.UTC(2026, 0, 1);
const track = Array.from({ length: 450 }, (_, index) => ({
latitude: 37 + index / 1000,
longitude: -122,
observedAtMs: start + (449 - index) * 1000,
baroAltitudeM: 1000,
onGround: false,
}));
const aircraft = feed(civil, { track });
const catalog = composeCatalog({ tools: coreTools, services: { aircraft } });
const result = await catalog.call('get_aircraft_track', { icao24: 'AAA111' });
assert.equal(aircraft.calls[0], 'aaa111');
assert.equal(
result.summary,
'450 positions for aaa111 from 2026-01-01T00:00:00.000Z to 2026-01-01T00:07:29.000Z.',
);
assert.deepEqual(
{
total: result.data.total,
returned: result.data.returned,
thinned: result.data.thinned,
},
{ total: 450, returned: 200, thinned: true },
);
assert.equal(result.data.points[0].time, '2026-01-01T00:00:00.000Z');
assert.equal(result.data.points.at(-1).time, '2026-01-01T00:07:29.000Z');
const empty = composeCatalog({
tools: coreTools,
services: { aircraft: feed(civil) },
});
assert.equal(
(await empty.call('get_aircraft_track', { icao24: 'aaa111' })).summary,
'No recent track is available for aaa111.',
);
const failing = (status) => ({
getSnapshot: async () => snapshot([]),
getTrack: async () => {
throw new LiveSourceError('unavailable', `OpenSky HTTP ${status}`, {
status,
});
},
});
const unknown = composeCatalog({
tools: coreTools,
services: { aircraft: failing(404) },
});
assert.equal(
(await unknown.call('get_aircraft_track', { icao24: 'abcdef' })).summary,
'No recent track is available for abcdef.',
);
const down = composeCatalog({
tools: coreTools,
services: { aircraft: failing(502) },
});
await assert.rejects(
down.call('get_aircraft_track', { icao24: 'abcdef' }),
(error) => error.code === 'unavailable',
);
});
test('aircraft info combines type and route lookups', async () => {
const aircraft = feed(civil, {
enrichment: {
'type:aaa111': {
found: true,
typeCode: 'B738',
typeName: 'Boeing 737-800',
registration: 'N12345',
},
'route:UAL123': {
found: true,
airline: 'United Airlines',
origin: { code: 'SFO', name: 'San Francisco', lat: 37.6, lon: -122.4 },
destination: { code: 'EWR', name: 'Newark', lat: 40.7, lon: -74.2 },
},
},
});
const catalog = composeCatalog({ tools: coreTools, services: { aircraft } });
const both = await catalog.call('get_aircraft_info', {
icao24: 'AAA111',
callsign: 'ual123',
});
assert.equal(
both.summary,
'Aircraft aaa111 is Boeing 737-800 (N12345); flight UAL123 flies SFO to EWR (United Airlines).',
);
assert.equal(both.data.route.destination.name, 'Newark');
const unknown = await catalog.call('get_aircraft_info', { callsign: 'XYZ9' });
assert.equal(unknown.summary, 'No route is known for XYZ9.');
assert.deepEqual(unknown.data, { aircraft: null, route: null });
await assert.rejects(
catalog.call('get_aircraft_info', {}),
/icao24, callsign or both/,
);
});
test('live source failures become tool errors with retry guidance', async () => {
const limited = {
getSnapshot: async () => {
throw new LiveSourceError('limited', 'OpenSky rate limited', {
retryAfterMs: 45000,
});
},
};
const catalog = composeCatalog({
tools: coreTools,
services: { aircraft: limited },
});
await assert.rejects(
catalog.call('aircraft_in_area', { area: bay }),
(error) =>
error.name === 'ToolError' &&
error.code === 'retry_later' &&
error.retryAfterSeconds === 45 &&
error.message === 'OpenSky rate limited',
);
});
test('aircraft searches keep the feeds that answered and name the others', async () => {
const down = {
getSnapshot: async () => {
throw new LiveSourceError('unavailable', 'OpenSky HTTP 503');
},
};
const military = feed([
record('ae1234', 38, -77, { callsign: 'RCH123', registration: '05-5140' }),
]);
const catalog = composeCatalog({
tools: coreTools,
services: { aircraft: down, military },
});
const byCallsign = await catalog.call('find_aircraft', {
callsign: 'rch123',
});
assert.equal(
byCallsign.summary,
'Found 1 aircraft with callsign RCH123. The civil feed did not answer.',
);
assert.deepEqual(byCallsign.data.unavailable_feeds, ['civil']);
const civil = feed([]);
const byRegistration = await composeCatalog({
tools: coreTools,
services: { aircraft: civil, military },
}).call('find_aircraft', { registration: '05-5140' });
assert.equal(byRegistration.data.rows[0].id, 'ae1234');
assert.deepEqual(byRegistration.data.unavailable_feeds, []);
assert.equal(civil.calls.length, 0);
await assert.rejects(
composeCatalog({
tools: coreTools,
services: { aircraft: down, military: down },
}).call('find_aircraft', { callsign: 'RCH123' }),
(error) =>
error.code === 'unavailable' && error.message === 'OpenSky HTTP 503',
);
});
test('a regional fallback feed is named in the answer', async () => {
const regional = feed([record('abc123', 37.6, -122.3)], {
source: 'adsb.lol',
coverage: '250nm regional fallback',
});
const result = await composeCatalog({
tools: coreTools,
services: { aircraft: regional },
}).call('aircraft_in_area', {
area: { lat: 37.6, lon: -122.3, radius_km: 20 },
});
assert.match(result.summary, /\(regional feed: 250nm regional fallback\)\.$/);
});
test('aircraft searches say when a feed that answered is stale', async () => {
const stale = feed([], { freshness: 'stale' });
const result = await composeCatalog({
tools: coreTools,
services: { aircraft: stale, military: feed([]) },
}).call('find_aircraft', { callsign: 'UAL1' });
assert.equal(
result.summary,
'No aircraft with callsign UAL1 is currently reported. The civil feed data may be stale.',
);
assert.deepEqual(result.data.stale_feeds, ['civil']);
assert.deepEqual(
result.data.feeds.map((entry) => [entry.feed, entry.freshness]),
[
['civil', 'stale'],
['military', 'current'],
],
);
});
test('the real OpenSky source marks an hour-old snapshot stale for searches', async () => {
const { createFlightSource } =
await import('../../sources/live/standalone.js');
const now = Date.UTC(2026, 0, 1, 12);
const opensky = createFlightSource({
now: () => now,
fetchImpl: async () =>
Response.json({ time: now / 1000 - 3600, states: [] }),
});
const result = await composeCatalog({
tools: coreTools,
services: { aircraft: opensky },
}).call('find_aircraft', { callsign: 'UAL1' });
assert.deepEqual(result.data.stale_feeds, ['civil']);
assert.match(result.summary, /may be stale\.$/);
});
test('altitude bands require a reported altitude from normalized OpenSky records', async () => {
const { normalizeOpenSkyAircraft } =
await import('../../sources/live/aircraft.js');
const state = (id, baro, geometric) =>
normalizeOpenSkyAircraft([
id,
'TEST',
'Test',
1767225600,
1767225600,
0,
0,
baro,
false,
10,
90,
0,
[],
geometric,
]);
const records = [
state('abc001', null, null),
state('abc002', 0, null),
state('abc003', null, 50),
state('abc004', 500, null),
];
const catalog = composeCatalog({
tools: coreTools,
services: {
aircraft: { getSnapshot: async () => ({ records }) },
},
});
const area = { bbox: [-1, -1, 1, 1] };
assert.equal(
(await catalog.call('aircraft_in_area', { area })).data.total,
4,
'unfiltered answers retain unknown altitude',
);
for (const band of [
{ max_altitude_m: 100 },
{ min_altitude_m: -500, max_altitude_m: 100 },
]) {
const result = await catalog.call('aircraft_in_area', { area, ...band });
assert.deepEqual(
result.data.rows.map((row) => row.id),
['abc002', 'abc003'],
);
assert.deepEqual(
result.data.rows.map((row) => row.altitude_m),
[0, 50],
);
}
const minimum = await catalog.call('aircraft_in_area', {
area,
min_altitude_m: -500,
});
assert.equal(minimum.data.total, 3);
});