Run the layer acceptance under a default-deny network policy with per-layer assertion manifests and strict switch contracts

Every scenario runs through scripts/qa-strict-harness.mjs: each page, frame and worker gets an ordered block list before it runs (paid hosts and routes first, then only the preview, declared fixtures, keyless probes and cached free tile hosts), switches with real input on the picker, and records its assertions per layer and engine. The runner reads the catalog from the live page, fails any layer without an assertion on either engine, can build and serve a keyless checkout itself, and adds rapid, hidden-tab, context-loss, failure-injection, cold-chunk and soak scenarios. The main chunk gets a 1% size budget.
This commit is contained in:
German Khokhlov committed 2026-11-03 20:15:50 +04:00
1 parent 3144169057
commit 1773494116
36 files changed
+6003 -1774

No files matched your search

+147 -59
View File
@@ -1,19 +1,10 @@
import assert from 'node:assert/strict';
import puppeteer from 'puppeteer';
import { installAlprFixture } from './fixtures/w2/alpr.mjs';
import { firmsFixture } from './fixtures/w2/firms.mjs';
import { switchGlobe } from './qa-switch-globe.mjs';
import { blockPaidQaMaps } from './qa-map-policy.mjs';
const url = new URL(process.argv[2]),
import { installAlprFixture, cameras } from './fixtures/w2/alpr.mjs';
import { firmsFixture, FIRMS_VALID_COUNT } from './fixtures/w2/firms.mjs';
import { createStrict, localPreview } from './qa-strict-harness.mjs';
const url = localPreview(process.argv[2]),
fire = process.argv.includes('--fires'),
id = fire ? 'local-firms' : 'alpr-cameras';
if (
!['localhost', '127.0.0.1'].includes(url.hostname) ||
['', '4173', '4185', '4186', '4187', '4188', '4189', '4194'].includes(
url.port,
)
)
throw Error('Supply local keyless preview URL');
url.search = '?engine=navara&welcome=0';
url.hash =
'v=2&' +
@@ -21,13 +12,17 @@ url.hash =
? 'lat=37.77&lon=-122.42&alt=100000'
: 'lat=30.262&lon=-97.7431&alt=5500') +
'&pitch=-90&heading=0&map=sentinel2&l=';
const b = await puppeteer.launch({
headless: true,
args: ['--use-angle=metal', '--enable-gpu'],
// Records the fixtures produce: the valid FIRMS rows; the ALPR cameras the
// fixture tiles carry around Austin.
const COUNT = fire ? FIRMS_VALID_COUNT : cameras.length;
const strict = createStrict({
origin: url.origin,
layers: [id],
// installAlprFixture answers the camera tiles.
fixtures: ['https://tiles.dontgetflocked.com/*'],
});
try {
await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
const p = await b.newPage();
await blockPaidQaMaps(p);
await p.setViewport({ width: 1280, height: 900 });
const errors = [];
p.on('pageerror', (e) => errors.push(e.message));
@@ -48,7 +43,7 @@ try {
await p.goto(url.href);
await p.waitForFunction(
() =>
window.__godsEyeView?.engine.kind === 'navara' &&
window.__godsEyeView?.engine?.kind === 'navara' &&
document.querySelector('#loading-screen.hidden'),
);
await p.evaluate(async (id) => {
@@ -62,52 +57,123 @@ try {
await p.waitForFunction(() => window.qaGround?.getStats().count > 0, {
timeout: 30000,
});
const target = await p.waitForFunction(
(fire) => {
const a = window.__godsEyeView,
m = window.qaGround;
const points = fire
? m
.getDetectableObjects({ maxCount: 1000 })
.filter((v) => v.id === m.getAnalystRecords(1)[0]?.id)
: [...window.__gevContextStore.entities.values()]
.filter((v) => v.layerId === 'alpr-cameras')
.map((v) => ({
id: v.id,
position:
v.entity.gevAlprCanvasPosition ||
v.entity.gevDisplayPosition?.(),
}));
for (const row of points) {
if (!row.position) continue;
const s = a.engine.projector.worldToWindow(row.position);
if (!s || s.x < 430 || s.x > 850 || s.y < 220 || s.y > 730) continue;
const picked = a.engine.picker.pick(s);
if (fire || picked?.id === row.id || picked?.id?.id === row.id)
return { x: s.x, y: s.y, id: row.id };
}
return false;
},
{ timeout: 30000 },
fire,
);
const xy = await target.jsonValue();
await new Promise(r=>setTimeout(r,1000));
// A pickable target in the middle of the view: the camera the engine's
// own picker returns, or the hottest fire.
const findTarget = () =>
p
.waitForFunction(
(fire) => {
const a = window.__godsEyeView,
m = window.qaGround;
const points = fire
? m
.getDetectableObjects({ maxCount: 1000 })
.filter((v) => v.id === m.getAnalystRecords(1)[0]?.id)
: [...window.__gevContextStore.entities.values()]
.filter((v) => v.layerId === 'alpr-cameras')
.map((v) => ({
id: v.id,
position:
v.entity.gevAlprCanvasPosition ||
v.entity.gevDisplayPosition?.(),
}));
for (const row of points) {
if (!row.position) continue;
const s = a.engine.projector.worldToWindow(row.position);
if (!s || s.x < 430 || s.x > 850 || s.y < 220 || s.y > 600)
continue;
const canvas = a.engine.host.canvas;
const box = canvas.getBoundingClientRect();
if (
!canvas.contains(
document.elementFromPoint(box.left + s.x, box.top + s.y),
)
)
continue;
const picked = a.engine.picker.pick(s);
if (fire || picked?.id === row.id || picked?.id?.id === row.id)
return { x: box.left + s.x, y: box.top + s.y, id: row.id };
}
return false;
},
{ timeout: 30000 },
fire,
)
.then((handle) => handle.jsonValue());
const selected = (id) =>
p.waitForFunction(
(id, fire) =>
fire
? window.qaGround.getSelectedInfo()?.frp === 999
: window.__gevContextStore?.selectedEntityId === id,
{ timeout: 15000 },
id,
fire,
);
// A click on empty ground (nothing picked there, nothing covering it).
const clickEmpty = async () => {
const handle = await p.waitForFunction(
() => {
const { engine } = window.__godsEyeView;
const canvas = engine.host.canvas;
const box = canvas.getBoundingClientRect();
for (let y = 240; y < 600; y += 9)
for (let x = 440; x < 840; x += 9) {
if (
!canvas.contains(
document.elementFromPoint(box.left + x, box.top + y),
)
)
continue;
let free = true;
for (const [dx, dy] of [
[0, 0],
[6, 0],
[-6, 0],
[0, 6],
[0, -6],
])
if (engine.picker.pick({ x: x + dx, y: y + dy })) free = false;
if (free) return { x: box.left + x, y: box.top + y };
}
return false;
},
{ timeout: 15000 },
);
const at = await handle.jsonValue();
await p.mouse.click(at.x, at.y);
await p.waitForFunction(
(fire) =>
fire
? !window.qaGround.getSelectedInfo()
: !window.__gevContextStore?.selectedEntityId,
{ timeout: 15000 },
fire,
);
};
const xy = await findTarget();
await new Promise((r) => setTimeout(r, 1000));
await p.mouse.click(xy.x, xy.y);
await p.waitForFunction(
(id,fire) => fire ? window.qaGround.getSelectedInfo()?.frp === 999 : window.__gevContextStore?.selectedEntityId === id,
{},
xy.id, fire,
await selected(xy.id);
await strict.passed(
p,
id,
fire
? 'a click on the hottest fire selects it'
: 'a click on a camera selects it',
xy,
);
await new Promise((r) => setTimeout(r, 1600));
const expectedId = fire ? await p.evaluate(()=>window.qaGround.getSelectedInfo().id) : xy.id;
const expectedId = fire
? await p.evaluate(() => window.qaGround.getSelectedInfo().id)
: xy.id;
const before = await p.evaluate(() => ({
stamp: window.qaGround.getStats().lastUpdate,
records: window.qaGround.getAnalystRecords(1000).map((v) => v.id),
reads: window.qaFirmsReads,
}));
for (const kind of ['cesium', 'navara']) {
await switchGlobe(p, kind);
await strict.switchTo(p, kind);
const result = await p.evaluate(
(id) => ({
same:
@@ -129,9 +195,31 @@ try {
assert.equal(result.reads, before.reads);
assert.deepEqual(result.records, before.records);
assert.equal(result.stamp, before.stamp);
await strict.check(
p,
id,
`same model, ${COUNT} records, the clicked record still selected, no source read, same receipt time`,
result.count === COUNT,
{ count: result.count },
);
if (kind === 'cesium') {
// Click-picking on Cesium: clear the selection with a click on empty
// ground, then click the same record again.
await clickEmpty();
const again = await findTarget();
assert.equal(again.id, xy.id);
await p.mouse.click(again.x, again.y);
await selected(again.id);
await strict.passed(
p,
id,
fire
? 'a click on empty ground clears the fire; a click on it selects it again'
: 'a click on empty ground clears the camera; a click on it selects it again',
again,
);
}
}
assert.deepEqual(errors, []);
console.log('PASS retained source, stable clicked record on each planet');
} finally {
await b.close();
}
});