mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Merge branch 'main' into fix/director-goto-shot
This commit is contained in:
commit
31ef637ba7
10 files changed
+167
-10
No files matched your search
@@ -39,8 +39,6 @@ export class MilitaryFlightRecords {
|
||||
? prevMeta.altitudeFt * 0.3048
|
||||
: 0
|
||||
: 3048);
|
||||
const track = aircraft.courseDeg || 0;
|
||||
const speedMps = aircraft.speedMps ?? 0;
|
||||
const verticalRateMps = aircraft.verticalRateMps;
|
||||
const callsign = aircraft.callsign;
|
||||
const type = aircraft.typeCode;
|
||||
@@ -148,8 +146,15 @@ export class MilitaryFlightRecords {
|
||||
// altitudeFt/baro).
|
||||
geoAltitudeM,
|
||||
renderAltitudeM,
|
||||
speedMps: stickyNumber(speedMps, prevMeta?.speedMps, null),
|
||||
track: stickyNumber(track, prevMeta?.track, null),
|
||||
// A poll that omits track/speed holds the last values, like the civil
|
||||
// records; 0 only until one is known. A grounded row without a speed
|
||||
// reads 0, so a parked plane never coasts at its landing speed.
|
||||
speedMps: stickyNumber(
|
||||
aircraft.speedMps,
|
||||
onGround ? 0 : prevMeta?.speedMps,
|
||||
0,
|
||||
),
|
||||
track: stickyNumber(aircraft.courseDeg, prevMeta?.track, 0),
|
||||
// Analyst seam (additive): sticky like the other kinematics.
|
||||
verticalRateMps: stickyNumber(
|
||||
verticalRateMps,
|
||||
|
||||
@@ -114,3 +114,39 @@ test('military emergency status follows the latest poll instead of sticking', ()
|
||||
);
|
||||
assert.equal(next.meta.emergency, null);
|
||||
});
|
||||
|
||||
test('military records hold the last track and speed when a poll omits them', () => {
|
||||
const records = store();
|
||||
records.receive(observation({ courseDeg: 270, speedMps: 200 }), context());
|
||||
const missing = records.receive(
|
||||
observation({ courseDeg: null, speedMps: null }),
|
||||
context(),
|
||||
);
|
||||
assert.equal(missing.meta.track, 270);
|
||||
assert.equal(missing.meta.speedMps, 200);
|
||||
const north = records.receive(
|
||||
observation({ courseDeg: 0, speedMps: 0 }),
|
||||
context(),
|
||||
);
|
||||
assert.equal(north.meta.track, 0);
|
||||
assert.equal(north.meta.speedMps, 0);
|
||||
const unseen = store().receive(
|
||||
observation({ courseDeg: undefined, speedMps: undefined }),
|
||||
context(),
|
||||
);
|
||||
assert.equal(unseen.meta.track, 0);
|
||||
assert.equal(unseen.meta.speedMps, 0);
|
||||
const landing = store();
|
||||
landing.receive(observation({ courseDeg: 90, speedMps: 70 }), context());
|
||||
const parked = landing.receive(
|
||||
observation({
|
||||
onGround: true,
|
||||
baroAltitudeM: null,
|
||||
courseDeg: null,
|
||||
speedMps: null,
|
||||
}),
|
||||
context(),
|
||||
);
|
||||
assert.equal(parked.meta.speedMps, 0);
|
||||
assert.equal(parked.meta.track, 90);
|
||||
});
|
||||
@@ -126,6 +126,32 @@ test('production reader rejects oversized, malformed and runtime-failure bodies'
|
||||
}
|
||||
});
|
||||
|
||||
test('element data that mentions a failure phrase is data, and sets no cooldown', async () => {
|
||||
const names = ['Timed Out Escape Rooms', 'Runtime Error Bar', 'Out of Memory Lane', 'Too Many Requests Cafe', 'rate_limited'];
|
||||
for (const [i, name] of names.entries()) {
|
||||
const body = JSON.stringify({ elements: [{ type: 'node', id: 1, lat: 1, lon: 2, tags: { name } }] });
|
||||
let count = 0;
|
||||
const options = { endpoints: [`https://phrase-${i}.example/`], fetchImpl: async () => {
|
||||
count++; return new Response(body);
|
||||
} };
|
||||
const payload = await fetchOverpassPayload('first', 1024, options);
|
||||
assert.equal(payload.status, 200, name);
|
||||
assert.ok(overpassPayloadIsData(payload), name);
|
||||
assert.ok(payload.body.includes(name), name);
|
||||
await fetchOverpassPayload('different', 1024, options);
|
||||
assert.equal(count, 2, name);
|
||||
}
|
||||
const remark = JSON.stringify({ elements: [], remark: 'runtime error: rate_limited' });
|
||||
const limited = await fetchOverpassPayload('x', 1024, { endpoints: ['https://remark.example/'],
|
||||
fetchImpl: async () => new Response(remark, { headers: { 'Retry-After': '90' } }) });
|
||||
assert.equal(limited.status, 429);
|
||||
assert.equal(limited.retryAfterMs, 90_000);
|
||||
const error = await fetchOverpassPayload('x', 1024, { endpoints: ['https://error.example/'],
|
||||
fetchImpl: async () => new Response('{"error":"rate_limited"}', { headers: { 'Retry-After': '120' } }) });
|
||||
assert.equal(error.status, 429);
|
||||
assert.equal(error.retryAfterMs, 120_000);
|
||||
});
|
||||
|
||||
function proxyHandler() {
|
||||
const plugin = createViteConfig({ mode: 'test' }).plugins.find(p => p.name === 'overpass-proxy');
|
||||
const routes = new Map();
|
||||
|
||||
@@ -111,6 +111,8 @@ export const aircraftInArea = defineTool({
|
||||
(record) =>
|
||||
areaContains(area, { lat: record.latitude, lon: record.longitude }) &&
|
||||
!(args.airborne_only && record.onGround) &&
|
||||
((args.min_altitude_m == null && args.max_altitude_m == null) ||
|
||||
Number.isFinite(altitude(record))) &&
|
||||
(args.min_altitude_m == null ||
|
||||
altitude(record) >= args.min_altitude_m) &&
|
||||
(args.max_altitude_m == null ||
|
||||
|
||||
@@ -388,3 +388,62 @@ test('the real OpenSky source marks an hour-old snapshot stale for searches', as
|
||||
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);
|
||||
});
|
||||
Reference in new issue
Block a user