mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(status): stop false LOAD FAILED from a relayed adsb.lol 429 and the installations zoom prompt
Two independent triggers turned the global status chip red while every layer was still populated (reproduced at JFK in Cockpit; timeline in the issue). adsb.lol proxy: a non-OK upstream status was relayed verbatim even with a 12 s-old cache in hand, and the browser layer answers a 429 with a 45 s cooldown in which every refresh reports "adsb.lol rate limited" without a network call. The proxy now serves its cached body as STALE on a 429/5xx and backs off from upstream for Retry-After (bounded), mirroring the OpenSky proxy. A failure with nothing cached is still relayed. Mapped installations: the "zoom in" guidance was stored in `error`, so the manager reported a refresh failure and the chip a LOAD FAILED whenever the view could not be bounded (Cockpit looks at the horizon). The status is now set without an error string; the row reads the prompt from `statusMessage`, and the chip ignores `error` for guidance statuses, matching the carve-out layerFeedState already has.
This commit is contained in:
1 parent
db49edf363
commit
1a9cfb1809
8 files changed
+200
-21
No files matched your search
@@ -230,3 +230,62 @@ test('AIS preview route ingests through the socket, returns tracks and disposes
|
||||
for (let i = 0; i < 100 && sockets.length < 2; i++) await delay(10);
|
||||
assert.equal(sockets.length, 2);
|
||||
});
|
||||
|
||||
test('military aircraft route serves stale cache on an upstream 429 and cools down for Retry-After', async (t) => {
|
||||
let now = Date.now();
|
||||
let calls = 0;
|
||||
t.mock.method(Date, 'now', () => now);
|
||||
t.mock.method(console, 'warn', () => {});
|
||||
t.mock.method(globalThis, 'fetch', async () => {
|
||||
calls += 1;
|
||||
if (calls === 1) return Response.json({ ac: [{ hex: 'abc123' }] });
|
||||
return new Response(JSON.stringify({ error: 'rate limited' }), {
|
||||
status: 429,
|
||||
headers: { 'Retry-After': '20' },
|
||||
});
|
||||
});
|
||||
const request = install(providers.adsbLolProxy());
|
||||
const first = await request('/api/adsblol/mil');
|
||||
assert.equal(first.statusCode, 200);
|
||||
now += 13_000;
|
||||
const limited = await request('/api/adsblol/mil');
|
||||
assert.equal(calls, 2);
|
||||
assert.equal(
|
||||
limited.statusCode,
|
||||
200,
|
||||
'a 429 with a cached body is never relayed',
|
||||
);
|
||||
assert.equal(limited.body, first.body);
|
||||
assert.equal(limited.headers['x-ads-b-cache'], 'STALE');
|
||||
assert.equal(limited.headers['x-ads-b-upstream-status'], '429');
|
||||
now += 5_000;
|
||||
const cooling = await request('/api/adsblol/mil');
|
||||
assert.equal(calls, 2, 'no upstream call inside the Retry-After window');
|
||||
assert.equal(cooling.headers['x-ads-b-cache'], 'STALE');
|
||||
now += 16_000;
|
||||
await request('/api/adsblol/mil');
|
||||
assert.equal(calls, 3, 'upstream is retried once Retry-After elapses');
|
||||
});
|
||||
|
||||
test('military aircraft route relays an upstream 429 when nothing is cached', async (t) => {
|
||||
t.mock.method(console, 'warn', () => {});
|
||||
let calls = 0;
|
||||
t.mock.method(globalThis, 'fetch', async () => {
|
||||
calls += 1;
|
||||
return new Response('{"error":"rate limited"}', { status: 429 });
|
||||
});
|
||||
const request = install(providers.adsbLolProxy());
|
||||
const limited = await request('/api/adsblol/mil');
|
||||
assert.equal(limited.statusCode, 429);
|
||||
const again = await request('/api/adsblol/mil');
|
||||
assert.equal(again.statusCode, 429);
|
||||
assert.equal(
|
||||
calls,
|
||||
1,
|
||||
'the cooldown still protects upstream with nothing cached',
|
||||
);
|
||||
assert.ok(
|
||||
again.headers['retry-after'],
|
||||
'a cooling-down miss carries Retry-After',
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user