Merge remote-tracking branch 'origin/codex/production-intelligence-terminal' into codex/issue-6-terminal-actions-hardening
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 5s
Build / test-and-image (pull_request) Successful in 1m4s

# Conflicts:
#	test/fetch-utils.test.mjs
This commit is contained in:
MrSphay
2026-05-17 20:39:04 +02:00
8 changed files with 185 additions and 15 deletions

82
test/adsb.test.mjs Normal file
View File

@@ -0,0 +1,82 @@
import test from 'node:test';
import assert from 'node:assert/strict';
async function withFetch(mockFetch, fn) {
const originalFetch = globalThis.fetch;
const originalAdsbKey = process.env.ADSB_API_KEY;
const originalRapidKey = process.env.RAPIDAPI_KEY;
globalThis.fetch = mockFetch;
delete process.env.ADSB_API_KEY;
delete process.env.RAPIDAPI_KEY;
try {
return await fn();
} finally {
globalThis.fetch = originalFetch;
if (originalAdsbKey === undefined) delete process.env.ADSB_API_KEY;
else process.env.ADSB_API_KEY = originalAdsbKey;
if (originalRapidKey === undefined) delete process.env.RAPIDAPI_KEY;
else process.env.RAPIDAPI_KEY = originalRapidKey;
}
}
function jsonResponse(payload, ok = true, status = 200) {
return {
ok,
status,
headers: { get: () => 'application/json' },
text: async () => JSON.stringify(payload),
};
}
test('ADS-B reports disabled when no key is configured and public feed fails', async () => {
await withFetch(async () => jsonResponse({ error: 'blocked' }, false, 403), async () => {
const { briefing } = await import('../apis/sources/adsb.mjs');
const data = await briefing();
assert.equal(data.status, 'disabled');
assert.match(data.error, /ADSB_API_KEY|RAPIDAPI_KEY/);
assert.equal(data.militaryAircraft.length, 0);
});
});
test('ADS-B reports degraded when RapidAPI and public feed fail', async () => {
await withFetch(async () => jsonResponse({ error: 'unavailable' }, false, 503), async () => {
process.env.ADSB_API_KEY = 'test-key';
const { briefing } = await import('../apis/sources/adsb.mjs');
const data = await briefing();
assert.equal(data.status, 'degraded');
assert.match(data.error, /providers returned no usable/);
assert.equal(data.failures.length, 2);
});
});
test('ADS-B returns live RapidAPI military aircraft payloads', async () => {
await withFetch(async () => jsonResponse({
ac: [{
hex: 'AE1234',
flight: 'RCH123',
t: 'KC135',
lat: 50,
lon: 8,
mil: true,
}],
}), async () => {
process.env.ADSB_API_KEY = 'test-key';
const { briefing } = await import('../apis/sources/adsb.mjs');
const data = await briefing();
assert.equal(data.status, 'live');
assert.equal(data.provider, 'rapidapi');
assert.equal(data.totalMilitary, 1);
assert.equal(data.militaryAircraft[0].callsign, 'RCH123');
});
});
test('runSource treats disabled source status as degraded health', async () => {
const { runSource } = await import('../apis/briefing.mjs');
const result = await runSource('ADS-B', async () => ({ status: 'disabled', message: 'missing key' }));
assert.equal(result.status, 'degraded');
assert.equal(result.error, null);
});

View File

@@ -120,6 +120,24 @@ test('dashboard exposes token configuration flow without devtools edits', () =>
assert.match(html, /SET TOKEN/);
});
test('server dashboard shell does not embed an operational snapshot', () => {
const html = readFileSync(new URL('../dashboard/public/jarvis.html', import.meta.url), 'utf8');
assert.match(html, /let D = createDashboardShellData\(\);/);
assert.doesNotMatch(html, /2026-04-03T16:18:10\.188Z/);
assert.doesNotMatch(html, /Trump announced new strikes on Iran/);
});
test('server dashboard fetches api data before initialization', () => {
const html = readFileSync(new URL('../dashboard/public/jarvis.html', import.meta.url), 'utf8');
const serverMode = html.indexOf('if (canProbeApi)');
const apiFetch = html.indexOf("fetch('/api/data')");
const firstInitAfterServerMode = html.indexOf('init();', serverMode);
assert.ok(serverMode > -1);
assert.ok(apiFetch > serverMode);
assert.ok(firstInitAfterServerMode > apiFetch);
});
test('stale alert is skipped for fresh health and resets active key', () => {
const state = { lastStaleAlertKey: 'old', lastStaleAlertAt: 100 };
const decision = shouldSendStaleAlert({ stale: false }, state, { now: 200 });