mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Merge branch 'main' into upstream/traffic-layer-stale
This commit is contained in:
27 files changed
+458
-20
No files matched your search
@@ -75,4 +75,5 @@ test('the Docker install path keeps its loopback-only, launcher-owned contract',
|
||||
assert.match(compose, /^\s+- GEV_KEY_SETUP_TRUSTED_PEERS$/m, 'the operator policy reaches the launcher without a hard-coded address');
|
||||
assert.match(compose, /^\s+- GEV_ALLOWED_HOSTS$/m, 'the host allowlist reaches Vite');
|
||||
assert.doesNotMatch(compose, /network_mode/, 'the container keeps its own network namespace');
|
||||
assert.match(compose, /^\s+- \.:\/app:z$/m, 'SELinux hosts can read the bind-mounted checkout');
|
||||
});
|
||||
+6
-5
@@ -574,11 +574,12 @@ export class IntelHUD {
|
||||
* @returns {string} Formatted DMS string, e.g. `"38°53'23.10"N"`.
|
||||
*/
|
||||
_toDMS(decimal, type) {
|
||||
const abs = Math.abs(decimal);
|
||||
const deg = Math.floor(abs);
|
||||
const minFloat = (abs - deg) * 60;
|
||||
const min = Math.floor(minFloat);
|
||||
const sec = ((minFloat - min) * 60).toFixed(2);
|
||||
// Round once, in hundredths of a second, so a value that rounds up to
|
||||
// 60.00" carries into the minutes and degrees instead of printing 60.
|
||||
const hundredths = Math.round(Math.abs(decimal) * 360000);
|
||||
const deg = Math.floor(hundredths / 360000);
|
||||
const min = Math.floor(hundredths / 6000) % 60;
|
||||
const sec = ((hundredths % 6000) / 100).toFixed(2);
|
||||
|
||||
let dir;
|
||||
if (type === 'lat') dir = decimal >= 0 ? 'N' : 'S';
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// The HUD's LAT/LON readout in degrees-minutes-seconds. Seconds are printed to
|
||||
// two decimals, so a value within 0.005" of the next whole minute rounds up.
|
||||
// That carry must reach the minutes (and degrees): `122°20'60.00"W` is not a
|
||||
// coordinate. Ordinary decimal inputs land there through binary floating
|
||||
// point: 122.35 - 122 is 0.34999999999999432, so -122.35 read 20'60.00".
|
||||
//
|
||||
// hud.js imports `mgrs`, a CommonJS package whose named exports Node's ESM
|
||||
// loader cannot see, so this installs the same one-specifier stub as
|
||||
// hudAltitudeDatum.test.mjs before importing it.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { registerHooks } from 'node:module';
|
||||
|
||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
||||
registerHooks({
|
||||
resolve(specifier, context, next) {
|
||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
||||
return next(specifier, context);
|
||||
},
|
||||
load(url, context, next) {
|
||||
if (url === MGRS_STUB_URL) {
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
source:
|
||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
||||
};
|
||||
}
|
||||
return next(url, context);
|
||||
},
|
||||
});
|
||||
|
||||
const { IntelHUD } = await import('./hud.js');
|
||||
|
||||
const toDMS = (decimal, type) => IntelHUD.prototype._toDMS(decimal, type);
|
||||
|
||||
test('a rounded-up second carries into the minutes', () => {
|
||||
assert.equal(toDMS(-122.35, 'lon'), `122°21'00.00"W`);
|
||||
assert.equal(toDMS(139.7, 'lon'), `139°42'00.00"E`);
|
||||
assert.equal(toDMS(37.0166666, 'lat'), `37°01'00.00"N`);
|
||||
});
|
||||
|
||||
test('a carry through 59 minutes reaches the degrees', () => {
|
||||
assert.equal(toDMS(38.999999, 'lat'), `39°00'00.00"N`);
|
||||
assert.equal(toDMS(-0.999999, 'lat'), `01°00'00.00"S`);
|
||||
assert.equal(toDMS(179.999999, 'lon'), `180°00'00.00"E`);
|
||||
});
|
||||
|
||||
test('ordinary values keep their existing format', () => {
|
||||
assert.equal(toDMS(38.889722, 'lat'), `38°53'23.00"N`);
|
||||
assert.equal(toDMS(-77.035278, 'lon'), `077°02'07.00"W`);
|
||||
assert.equal(toDMS(51.5, 'lat'), `51°30'00.00"N`);
|
||||
assert.equal(toDMS(-33.8568, 'lat'), `33°51'24.48"S`);
|
||||
assert.equal(toDMS(0, 'lon'), `000°00'00.00"E`);
|
||||
assert.equal(toDMS(90, 'lat'), `90°00'00.00"N`);
|
||||
});
|
||||
|
||||
test('the telemetry tick paints the carried value into both readouts', () => {
|
||||
const elements = new Map(
|
||||
[
|
||||
'hud-alt',
|
||||
'hud-summary',
|
||||
'hud-mgrs',
|
||||
'hud-latlon',
|
||||
'hud-bottom-line',
|
||||
'hud-gsd',
|
||||
'hud-coll',
|
||||
'hud-ona',
|
||||
'hud-mode',
|
||||
].map((id) => [id, { textContent: '' }]),
|
||||
);
|
||||
const previousDocument = globalThis.document;
|
||||
globalThis.document = {
|
||||
getElementById: (id) => elements.get(id) ?? null,
|
||||
querySelectorAll: () => [],
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
};
|
||||
const viewer = {
|
||||
camera: {
|
||||
pitch: -Math.PI / 2,
|
||||
positionCartographic: {
|
||||
latitude: (37.75 * Math.PI) / 180,
|
||||
longitude: (-122.35 * Math.PI) / 180,
|
||||
height: 1500,
|
||||
},
|
||||
computeViewRectangle: () => undefined,
|
||||
moveStart: { addEventListener() {}, removeEventListener() {} },
|
||||
moveEnd: { addEventListener() {}, removeEventListener() {} },
|
||||
},
|
||||
};
|
||||
let hud;
|
||||
try {
|
||||
hud = new IntelHUD(viewer);
|
||||
hud._updateCameraData();
|
||||
const latlon = elements.get('hud-latlon').textContent;
|
||||
const bottom = elements.get('hud-bottom-line').textContent;
|
||||
assert.equal(latlon, `37°45'00.00"N 122°21'00.00"W`);
|
||||
assert.match(bottom, /LON: 122°21'00\.00"W/);
|
||||
assert.doesNotMatch(`${latlon} ${bottom}`, /60\.00"/);
|
||||
} finally {
|
||||
hud?.destroy();
|
||||
if (previousDocument === undefined) delete globalThis.document;
|
||||
else globalThis.document = previousDocument;
|
||||
}
|
||||
});
|
||||
@@ -14,6 +14,8 @@ const GLOBAL_TILE_MAXIMUM_LEVEL = 3;
|
||||
// Six decoded 2048×1024 canvases (8 MiB each) bound the cache below 50 MiB.
|
||||
const MAX_MOSAICS = 6;
|
||||
const MAX_PREFETCH_TILES = 8;
|
||||
// How often a staged frame waiting out its quiet interval rechecks readiness.
|
||||
export const READY_WAKE_MS = 150;
|
||||
|
||||
/** Globe imagery: own at most a displayed and a staging frame. Use native Cesium
|
||||
* tile scheduling, projection and texture disposal; the application clock is never touched. */
|
||||
@@ -126,6 +128,7 @@ function createGlobeRendering({
|
||||
frame.offRender?.();
|
||||
frame.offCamera?.();
|
||||
clearTimeout(frame.timeout);
|
||||
clearTimeout(frame.wake);
|
||||
for (const request of frame.requests) request.cancel?.();
|
||||
frame.requests.clear();
|
||||
frame.retries.clear();
|
||||
@@ -277,6 +280,8 @@ function createGlobeRendering({
|
||||
frame.offCamera?.();
|
||||
frame.offCamera = null;
|
||||
clearTimeout(frame.timeout);
|
||||
clearTimeout(frame.wake);
|
||||
frame.wake = null;
|
||||
frame.offAbort?.();
|
||||
if (ok) {
|
||||
lastError = null;
|
||||
@@ -450,8 +455,20 @@ function createGlobeRendering({
|
||||
if (++settled >= 2) finish(true);
|
||||
else viewer.scene.requestRender();
|
||||
} else settled = 0;
|
||||
if (incoming === frame && frame.pending === 0 && frame.loaded > 0)
|
||||
viewer.scene.requestRender();
|
||||
// Waiting out the quiet interval needs another readiness check
|
||||
// later, not a render every frame: wake on a timer so the render
|
||||
// governor's frame cap still holds while a frame is staged.
|
||||
if (
|
||||
incoming === frame &&
|
||||
frame.pending === 0 &&
|
||||
frame.loaded > 0 &&
|
||||
!frame.wake
|
||||
)
|
||||
frame.wake = setTimeout(() => {
|
||||
frame.wake = null;
|
||||
if (!frame.closed && incoming === frame)
|
||||
viewer.scene.requestRender();
|
||||
}, READY_WAKE_MS);
|
||||
});
|
||||
viewer.scene.requestRender();
|
||||
onChange();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createWeatherRendering } from './rendering.js';
|
||||
import { createWeatherRendering, READY_WAKE_MS } from './rendering.js';
|
||||
import { createWeatherLayer } from './index.js';
|
||||
import { createWeatherClock } from './clock.js';
|
||||
import * as Cesium from 'cesium';
|
||||
@@ -242,6 +242,50 @@ test('a ready weather frame does not wait for unrelated terrain, but requires su
|
||||
h.rendering.clear();
|
||||
});
|
||||
|
||||
test('a staged frame waiting out its quiet interval wakes on a timer, not every render', async (t) => {
|
||||
t.mock.timers.enable({ apis: ['setTimeout'] });
|
||||
let now = 0;
|
||||
const h = renderingHarness({ now: () => now });
|
||||
h.viewer.scene.globe.tilesLoaded = false;
|
||||
const loaded = h.rendering.setFrame(snapshot, times[0]);
|
||||
const tile = deferred();
|
||||
h.providers[0].response = tile;
|
||||
const request = h.providers[0].requestImage(0, 0, 0, {});
|
||||
tile.resolve({});
|
||||
await request;
|
||||
const before = h.renders();
|
||||
for (let i = 0; i < 10; i++) h.postRender.emit();
|
||||
assert.equal(
|
||||
h.renders(),
|
||||
before,
|
||||
'renders while waiting do not request further renders',
|
||||
);
|
||||
t.mock.timers.tick(READY_WAKE_MS - 1);
|
||||
assert.equal(h.renders(), before);
|
||||
t.mock.timers.tick(1);
|
||||
assert.equal(h.renders(), before + 1, 'one wake per interval');
|
||||
now = 200;
|
||||
h.postRender.emit();
|
||||
h.postRender.emit();
|
||||
assert.equal(await loaded, true);
|
||||
const settledRenders = h.renders();
|
||||
t.mock.timers.tick(READY_WAKE_MS * 4);
|
||||
assert.equal(h.renders(), settledRenders, 'commit clears the wake timer');
|
||||
|
||||
const abandoned = h.rendering.setFrame(snapshot, times[1]);
|
||||
const second = deferred();
|
||||
h.providers.at(-1).response = second;
|
||||
const secondRequest = h.providers.at(-1).requestImage(0, 0, 0, {});
|
||||
second.resolve({});
|
||||
await secondRequest;
|
||||
h.postRender.emit();
|
||||
h.rendering.clear();
|
||||
assert.equal(await abandoned, false);
|
||||
const clearedRenders = h.renders();
|
||||
t.mock.timers.tick(READY_WAKE_MS * 4);
|
||||
assert.equal(h.renders(), clearedRenders, 'clear cancels the wake timer');
|
||||
});
|
||||
|
||||
test('camera movement drops abandoned deferred tiles but still waits for admitted requests', async () => {
|
||||
let now = 0;
|
||||
const h = renderingHarness({ now: () => now });
|
||||
|
||||
@@ -38,6 +38,7 @@ export function createGoogleTokenSource({
|
||||
headers: { Accept: 'application/json' },
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (response.status === 204) return null;
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
return null;
|
||||
|
||||
@@ -79,3 +79,12 @@ test('a server that does not offer tokens yields none', async () => {
|
||||
assert.equal(await createGoogleTokenSource({ fetchImpl }).token(), null);
|
||||
}
|
||||
});
|
||||
|
||||
test('an empty 204 response explicitly means no Google token is available', async () => {
|
||||
const { calls, fetchImpl } = server([
|
||||
new Response(null, { status: 204, headers: { 'Cache-Control': 'no-store' } }),
|
||||
]);
|
||||
const tokens = createGoogleTokenSource({ fetchImpl });
|
||||
assert.equal(await tokens.token(), null);
|
||||
assert.deepEqual(calls, [GOOGLE_TOKEN_PATH]);
|
||||
});
|
||||
@@ -113,6 +113,9 @@ test('Pinokio menu resolves the nested install marker and exposes each lifecycle
|
||||
runtime.url = url;
|
||||
const items = await menu(kernel, info);
|
||||
assert.equal(items.filter((item) => item.default).length, 1);
|
||||
const ready = installed && running === 'start.js' && Boolean(url);
|
||||
assert.equal(items[0].popout, ready ? true : undefined);
|
||||
assert.ok(items.slice(1).every((item) => item.popout === undefined));
|
||||
return items.map(({ text, href, default: isDefault = false }) => ({ text, href, default: isDefault }));
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createServer } from 'node:http';
|
||||
import { once } from 'node:events';
|
||||
import { createGoogleTokenSource } from '../maps/googleTokens.js';
|
||||
import { googleTokenUnavailablePlugin } from '../../server/standalone/google-token-unavailable.js';
|
||||
|
||||
async function withServer({ beforeFallback } = {}, check) {
|
||||
const middlewares = [];
|
||||
const app = {
|
||||
middlewares: {
|
||||
use: (handler) => middlewares.push(handler),
|
||||
},
|
||||
};
|
||||
if (beforeFallback) app.middlewares.use(beforeFallback);
|
||||
googleTokenUnavailablePlugin().configureServer(app);
|
||||
app.middlewares.use((_req, res) => {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Unknown API route' }));
|
||||
});
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
let index = 0;
|
||||
const next = () => middlewares[index++]?.(req, res, next);
|
||||
next();
|
||||
});
|
||||
server.listen(0, '127.0.0.1');
|
||||
await once(server, 'listening');
|
||||
const address = server.address();
|
||||
const url = 'http://127.0.0.1:' + address.port;
|
||||
try {
|
||||
await check(url);
|
||||
} finally {
|
||||
server.close();
|
||||
await once(server, 'close');
|
||||
}
|
||||
}
|
||||
|
||||
test('keyless GET and HEAD return uncached 204 instead of 404', async () => {
|
||||
await withServer({}, async (url) => {
|
||||
for (const method of ['GET', 'HEAD']) {
|
||||
const response = await fetch(url + '/api/google/tiles-token?probe=1', {
|
||||
method,
|
||||
});
|
||||
assert.equal(response.status, 204);
|
||||
assert.equal(response.headers.get('cache-control'), 'no-store');
|
||||
assert.equal(await response.text(), '');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('the existing client treats unavailable tokens as null', async () => {
|
||||
await withServer({}, async (url) => {
|
||||
const source = createGoogleTokenSource({
|
||||
fetchImpl: (path, options) => fetch(new URL(path, url), options),
|
||||
});
|
||||
assert.equal(await source.token(), null);
|
||||
});
|
||||
});
|
||||
|
||||
test('unrelated API paths and unsupported methods remain 404', async () => {
|
||||
await withServer({}, async (url) => {
|
||||
for (const [path, method] of [
|
||||
['/api/google/tiles-token/extra', 'GET'],
|
||||
['/api/not-a-token', 'GET'],
|
||||
['/api/google/tiles-token', 'POST'],
|
||||
]) {
|
||||
const response = await fetch(new URL(path, url), { method });
|
||||
assert.equal(response.status, 404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('a real token provider registered first still wins', async () => {
|
||||
await withServer(
|
||||
{
|
||||
beforeFallback: (req, res, next) => {
|
||||
if (req.url !== '/api/google/tiles-token') return next();
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
accessToken: 'server-token',
|
||||
expiresAt: 10000000000000,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
async (url) => {
|
||||
const response = await fetch(url + '/api/google/tiles-token');
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal((await response.json()).accessToken, 'server-token');
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
@@ -5,7 +6,11 @@ import { rm, writeFile } from 'node:fs/promises';
|
||||
import { request } from 'node:http';
|
||||
import path from 'node:path';
|
||||
import { build, createServer, preview } from 'vite';
|
||||
import { BROWSER_CSP, createBrowserViteConfig } from '../../build/vite.js';
|
||||
import {
|
||||
BROWSER_CSP,
|
||||
createBrowserViteConfig,
|
||||
escapeFsDenyPath,
|
||||
} from '../../build/vite.js';
|
||||
import standaloneConfig, * as compatibility from '../../vite.config.js';
|
||||
import * as providers from '../../server/providers/local.js';
|
||||
import { apiNotFoundPlugin } from '../../server/standalone/api-not-found.js';
|
||||
@@ -30,6 +35,20 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
assert.deepEqual(config.server.allowedHosts, ['localhost', '127.0.0.1']);
|
||||
assert.ok(config.server.fs.deny.includes('**/ENVIRONMENT'));
|
||||
assert.ok(config.server.fs.deny.includes('.env.*'));
|
||||
assert.ok(config.server.fs.deny.includes('**/.gev-logs/**'));
|
||||
assert.ok(config.server.fs.deny.includes('**/.gev-cache/**'));
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
.replace(/\\/g, '/')
|
||||
.replace(/\/$/, '');
|
||||
assert.deepEqual(
|
||||
config.server.fs.deny.filter((pattern) => pattern.endsWith('/dist/**')),
|
||||
[`${escapeFsDenyPath(root)}/dist/**`],
|
||||
'only project build output is denied, not dependency dist directories',
|
||||
);
|
||||
assert.equal(
|
||||
escapeFsDenyPath('C:/Users/a/project (1) [test]{copy}'),
|
||||
'C:/Users/a/project \\(1\\) \\[test\\]\\{copy\\}',
|
||||
);
|
||||
assert.equal(config.server.headers['X-Frame-Options'], 'DENY');
|
||||
assert.equal(config.server.headers['X-Content-Type-Options'], 'nosniff');
|
||||
const csp = config.server.headers['Content-Security-Policy'];
|
||||
@@ -98,7 +117,7 @@ test('root config retains existing named exports and standalone provider order',
|
||||
assert.equal(compatibility[name], value, name);
|
||||
const config = standaloneConfig({ mode: 'test' });
|
||||
const standaloneProviders = config.plugins
|
||||
.slice(4, -5)
|
||||
.slice(4, -6)
|
||||
.map((plugin) => plugin.name);
|
||||
assert.equal(
|
||||
standaloneProviders.filter((name) => name === 'gemini-live-proxy').length,
|
||||
@@ -112,9 +131,10 @@ test('root config retains existing named exports and standalone provider order',
|
||||
standaloneProviders.filter((name) => name !== 'gemini-live-proxy'),
|
||||
providers.localProviderPlugins().map((plugin) => plugin.name),
|
||||
);
|
||||
assert.equal(config.plugins.at(-6).name, 'gev-key-setup');
|
||||
// The local MCP route follows every provider and precedes the API fallback.
|
||||
assert.equal(config.plugins.at(-5).name, 'local-mcp');
|
||||
assert.equal(config.plugins.at(-7).name, 'gev-key-setup');
|
||||
// Token fallback follows real providers and local MCP before the API 404.
|
||||
assert.equal(config.plugins.at(-6).name, 'local-mcp');
|
||||
assert.equal(config.plugins.at(-5).name, 'google-token-unavailable');
|
||||
assert.equal(config.plugins.at(-4).name, 'api-not-found');
|
||||
// Config-only, so it sits after the provider slice rather than in it.
|
||||
assert.equal(config.plugins.at(-3).name, 'gev-unwatched-runtime-dirs');
|
||||
|
||||
Reference in new issue
Block a user