mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Format live source modules and adopt their formatting checks
This commit is contained in:
1 parent
e968053d2c
commit
80c5a09f15
7 files changed
+537
-129
No files matched your search
@@ -1,13 +1,40 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createOpenSkySource, createAdsbLolSource, createAisStreamSource, normalizeReadsbAircraft, normalizeVesselObservation, openSkySnapshot, readsbSnapshot } from './index.js';
|
||||
import {
|
||||
createOpenSkySource,
|
||||
createAdsbLolSource,
|
||||
createAisStreamSource,
|
||||
normalizeReadsbAircraft,
|
||||
normalizeVesselObservation,
|
||||
openSkySnapshot,
|
||||
readsbSnapshot,
|
||||
} from './index.js';
|
||||
|
||||
const now = 1800000000000;
|
||||
const aircraft = ['AbC123', 'TEST', 'Example', now / 1000 - 15, now / 1000 - 2, -97, 30, 1000, false, 100, 250, -2, null, 1040];
|
||||
const response = (payload, headers = {}, status = 200) => Response.json(payload, { headers, status });
|
||||
const aircraft = [
|
||||
'AbC123',
|
||||
'TEST',
|
||||
'Example',
|
||||
now / 1000 - 15,
|
||||
now / 1000 - 2,
|
||||
-97,
|
||||
30,
|
||||
1000,
|
||||
false,
|
||||
100,
|
||||
250,
|
||||
-2,
|
||||
null,
|
||||
1040,
|
||||
];
|
||||
const response = (payload, headers = {}, status = 200) =>
|
||||
Response.json(payload, { headers, status });
|
||||
|
||||
test('civil observations retain identity, separate altitude datums and source epochs', () => {
|
||||
const snapshot = openSkySnapshot({ time: now / 1000 - 130, states: [aircraft] }, { now, source: 'Test source', coverage: 'regional' });
|
||||
const snapshot = openSkySnapshot(
|
||||
{ time: now / 1000 - 130, states: [aircraft] },
|
||||
{ now, source: 'Test source', coverage: 'regional' },
|
||||
);
|
||||
const row = snapshot.records[0];
|
||||
assert.equal(row.id, 'abc123');
|
||||
assert.equal(row.baroAltitudeM, 1000);
|
||||
@@ -17,40 +44,77 @@ test('civil observations retain identity, separate altitude datums and source ep
|
||||
assert.equal(snapshot.ageMs, 130000);
|
||||
assert.equal(snapshot.freshness, 'stale');
|
||||
assert.equal(snapshot.coverage, 'regional');
|
||||
const missing = openSkySnapshot({ states: [[...aircraft.slice(0, 3), null, null, ...aircraft.slice(5)]] }, { now });
|
||||
const missing = openSkySnapshot(
|
||||
{ states: [[...aircraft.slice(0, 3), null, null, ...aircraft.slice(5)]] },
|
||||
{ now },
|
||||
);
|
||||
assert.equal(missing.observedAtMs, null);
|
||||
assert.equal(missing.freshness, 'unknown');
|
||||
assert.equal(missing.records[0].positionTimeMs, null);
|
||||
});
|
||||
|
||||
test('mixed-invalid and duplicate admissions are explicitly incomplete; all-invalid rejects', () => {
|
||||
const invalid = [...aircraft]; invalid[5] = 181;
|
||||
const invalid = [...aircraft];
|
||||
invalid[5] = 181;
|
||||
const partial = openSkySnapshot({ states: [aircraft, invalid, aircraft] });
|
||||
assert.equal(partial.records.length, 1);
|
||||
assert.equal(partial.complete, false);
|
||||
assert.equal(partial.rejectedCount, 2);
|
||||
assert.throws(() => openSkySnapshot({ states: [invalid] }), { code: 'malformed' });
|
||||
assert.throws(() => openSkySnapshot({ states: [invalid] }), {
|
||||
code: 'malformed',
|
||||
});
|
||||
assert.throws(() => openSkySnapshot({}), { code: 'malformed' });
|
||||
assert.equal(openSkySnapshot({ states: [] }).complete, true);
|
||||
});
|
||||
|
||||
test('readsb units and position/contact ages are normalized once against the source epoch', () => {
|
||||
const row = normalizeReadsbAircraft({ hex: 'abc123', lat: '30', lon: '-97', alt_baro: 10000, alt_geom: 10100, gs: 120, baro_rate: -600, seen: 2, seen_pos: 12 }, now);
|
||||
const row = normalizeReadsbAircraft(
|
||||
{
|
||||
hex: 'abc123',
|
||||
lat: '30',
|
||||
lon: '-97',
|
||||
alt_baro: 10000,
|
||||
alt_geom: 10100,
|
||||
gs: 120,
|
||||
baro_rate: -600,
|
||||
seen: 2,
|
||||
seen_pos: 12,
|
||||
},
|
||||
now,
|
||||
);
|
||||
assert.equal(row.baroAltitudeM, 3048);
|
||||
assert.equal(row.ellipsoidAltitudeM, 3078.48);
|
||||
assert.equal(row.speedMps, 120 * 0.514444);
|
||||
assert.equal(row.verticalRateMps, -600 * 0.00508);
|
||||
assert.equal(row.positionTimeMs, now - 12000);
|
||||
assert.equal(row.contactTimeMs, now - 2000);
|
||||
const ground = normalizeReadsbAircraft({ hex: 'abc123', lat: 30, lon: -97, alt_baro: 'ground' }, null);
|
||||
const ground = normalizeReadsbAircraft(
|
||||
{ hex: 'abc123', lat: 30, lon: -97, alt_baro: 'ground' },
|
||||
null,
|
||||
);
|
||||
assert.equal(ground.onGround, true);
|
||||
assert.equal(ground.baroAltitudeM, null);
|
||||
assert.equal(ground.positionTimeMs, null);
|
||||
assert.equal(readsbSnapshot({ ac: [] }, { observedAtMs: null }).freshness, 'unknown');
|
||||
assert.equal(
|
||||
readsbSnapshot({ ac: [] }, { observedAtMs: null }).freshness,
|
||||
'unknown',
|
||||
);
|
||||
});
|
||||
|
||||
test('vessel identity, opaque reference and heading/course remain distinct, unknowns stay null', () => {
|
||||
const row = normalizeVesselObservation({ mmsi: '123456789', lat: 30, lon: -97, speed: 10, heading: 45, course: 60, last_position_epoch: now / 1000, extra: 'discard' }, 'opaque-reference');
|
||||
const row = normalizeVesselObservation(
|
||||
{
|
||||
mmsi: '123456789',
|
||||
lat: 30,
|
||||
lon: -97,
|
||||
speed: 10,
|
||||
heading: 45,
|
||||
course: 60,
|
||||
last_position_epoch: now / 1000,
|
||||
extra: 'discard',
|
||||
},
|
||||
'opaque-reference',
|
||||
);
|
||||
assert.equal(row.id, '123456789');
|
||||
assert.equal(row.reference, 'opaque-reference');
|
||||
assert.equal(row.headingDeg, 45);
|
||||
@@ -59,23 +123,44 @@ test('vessel identity, opaque reference and heading/course remain distinct, unkn
|
||||
assert.equal(row.altitudeDatum, 'sea-surface');
|
||||
assert.equal(row.observedAtMs, now);
|
||||
assert.equal('extra' in row, false);
|
||||
assert.equal(normalizeVesselObservation({ mmsi: 'x', lat: null, lon: null }), null);
|
||||
assert.equal(
|
||||
normalizeVesselObservation({ mmsi: 'x', lat: null, lon: null }),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('construction is inert; adapters preserve routes, viewport query, cache epoch and history datum', async () => {
|
||||
const requests = [];
|
||||
const fetchImpl = async (url, init) => {
|
||||
requests.push({ url, init });
|
||||
if (url.startsWith('/api/opensky?')) return response({ time: now / 1000, states: [aircraft] }, { 'x-flight-source': 'adsb.lol', 'x-flight-coverage': '250 nm regional' });
|
||||
if (url.startsWith('/api/opensky-track')) return response({ path: [[now / 1000 - 30, 30, -97, 1000, 45, false]] });
|
||||
if (url === '/api/adsblol/mil') return response({ ac: [{ hex: 'abc123', lat: 30, lon: -97, seen_pos: 15 }] }, { 'x-ads-b-cache-age-ms': '60000', 'x-ads-b-cache': 'STALE' });
|
||||
return response({ timestamp: now / 1000 - 60, trace: [[10, 30, -97, 1000]] });
|
||||
if (url.startsWith('/api/opensky?'))
|
||||
return response(
|
||||
{ time: now / 1000, states: [aircraft] },
|
||||
{
|
||||
'x-flight-source': 'adsb.lol',
|
||||
'x-flight-coverage': '250 nm regional',
|
||||
},
|
||||
);
|
||||
if (url.startsWith('/api/opensky-track'))
|
||||
return response({ path: [[now / 1000 - 30, 30, -97, 1000, 45, false]] });
|
||||
if (url === '/api/adsblol/mil')
|
||||
return response(
|
||||
{ ac: [{ hex: 'abc123', lat: 30, lon: -97, seen_pos: 15 }] },
|
||||
{ 'x-ads-b-cache-age-ms': '60000', 'x-ads-b-cache': 'STALE' },
|
||||
);
|
||||
return response({
|
||||
timestamp: now / 1000 - 60,
|
||||
trace: [[10, 30, -97, 1000]],
|
||||
});
|
||||
};
|
||||
const civil = createOpenSkySource({ fetchImpl, now: () => now });
|
||||
const military = createAdsbLolSource({ fetchImpl, now: () => now });
|
||||
assert.equal(requests.length, 0);
|
||||
const signal = new AbortController().signal;
|
||||
const snapshot = await civil.getSnapshot({ latitude: 30.123456, longitude: -97 }, { signal });
|
||||
const snapshot = await civil.getSnapshot(
|
||||
{ latitude: 30.123456, longitude: -97 },
|
||||
{ signal },
|
||||
);
|
||||
assert.equal(requests[0].url, '/api/opensky?lat=30.1235&lon=-97.0000');
|
||||
assert.equal(requests[0].init.signal, signal);
|
||||
assert.equal(snapshot.source, 'adsb.lol');
|
||||
@@ -95,9 +180,17 @@ test('construction is inert; adapters preserve routes, viewport query, cache epo
|
||||
test('cancellation after slow body parsing rejects even with a transport that ignores abort', async () => {
|
||||
let release;
|
||||
const controller = new AbortController();
|
||||
const source = createOpenSkySource({ fetchImpl: async () => ({ ok: true, json: () => new Promise(resolve => { release = resolve; }) }) });
|
||||
const source = createOpenSkySource({
|
||||
fetchImpl: async () => ({
|
||||
ok: true,
|
||||
json: () =>
|
||||
new Promise((resolve) => {
|
||||
release = resolve;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const pending = source.getSnapshot({}, { signal: controller.signal });
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
controller.abort();
|
||||
release({ states: [aircraft] });
|
||||
await assert.rejects(pending, { name: 'AbortError' });
|
||||
@@ -106,8 +199,13 @@ test('cancellation after slow body parsing rejects even with a transport that ig
|
||||
test('denials and outages never start another source and do not echo arbitrary response bodies', async () => {
|
||||
for (const status of [401, 403, 429, 502]) {
|
||||
let calls = 0;
|
||||
const source = createOpenSkySource({ fetchImpl: async () => { calls++; return response({ error: 'sensitive upstream detail' }, {}, status); } });
|
||||
await assert.rejects(source.getSnapshot(), error => {
|
||||
const source = createOpenSkySource({
|
||||
fetchImpl: async () => {
|
||||
calls++;
|
||||
return response({ error: 'sensitive upstream detail' }, {}, status);
|
||||
},
|
||||
});
|
||||
await assert.rejects(source.getSnapshot(), (error) => {
|
||||
assert.equal(error.status, status);
|
||||
assert.equal(error.message.includes('sensitive'), false);
|
||||
assert.ok(error.retryAfterMs >= 20000);
|
||||
@@ -118,15 +216,34 @@ test('denials and outages never start another source and do not echo arbitrary r
|
||||
});
|
||||
|
||||
test('AIS reports limited received coverage and keeps connection state separate from positions', async () => {
|
||||
const source = createAisStreamSource({ origin: () => 'http://example.test', fetchImpl: async url => {
|
||||
if (url.includes('/track?')) return response({ samples: [{ lat: 30, lon: -97, t: now / 1000 }] });
|
||||
assert.equal(url, 'http://example.test/api/ais-live?maxRows=500');
|
||||
return response({ status: 'reconnecting', refreshing: true, rows: [{ mmsi: '123456789', lat: 30, lon: -97, last_position_UTC: new Date(now).toISOString() }], newestPositionAt: new Date(now).toISOString() });
|
||||
} });
|
||||
const source = createAisStreamSource({
|
||||
origin: () => 'http://example.test',
|
||||
fetchImpl: async (url) => {
|
||||
if (url.includes('/track?'))
|
||||
return response({ samples: [{ lat: 30, lon: -97, t: now / 1000 }] });
|
||||
assert.equal(url, 'http://example.test/api/ais-live?maxRows=500');
|
||||
return response({
|
||||
status: 'reconnecting',
|
||||
refreshing: true,
|
||||
rows: [
|
||||
{
|
||||
mmsi: '123456789',
|
||||
lat: 30,
|
||||
lon: -97,
|
||||
last_position_UTC: new Date(now).toISOString(),
|
||||
},
|
||||
],
|
||||
newestPositionAt: new Date(now).toISOString(),
|
||||
});
|
||||
},
|
||||
});
|
||||
const snapshot = await source.getSnapshot({ maxRows: 500 });
|
||||
assert.equal(snapshot.complete, false);
|
||||
assert.equal(snapshot.stale, true);
|
||||
assert.equal(snapshot.observedAtMs, now);
|
||||
assert.equal(snapshot.transportStatus, 'reconnecting');
|
||||
assert.equal((await source.getTrack('123456789')).records[0].observedAtMs, now);
|
||||
assert.equal(
|
||||
(await source.getTrack('123456789')).records[0].observedAtMs,
|
||||
now,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user