mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(track): read a late arrival frame before timing it out, so a measured wrong angle is never retried away
- observeArrivalRotation reads the sample before the deadline check and keeps a late frame's angle on the timeout result; arrivalAngleViolated then makes it final. Regressions: wrong late reveal and wrong late settle, each followed by a clean second attempt, still fail; a clean late frame stays retryable. - Overpass admission tests: a monotonic per-test clock removes order dependence on the module-level limiters; the proxy test covers ::1 and ::ffff:127.0.0.1 sockets; a new test pins the proxied: upstream bucket. - Retry edges (wrong on-time reveal, maxAttempts, inclusive 10° limit) and a check that every name the track harness imports is exported. - Docs: nginx needs Host, X-Forwarded-Proto and X-Forwarded-For; Docker bridge networking makes every host client one remote peer; list the exact forwarding headers; owner misses can also see 503.
This commit is contained in:
1 parent
8f16d10f7a
commit
01c764d4fd
8 files changed
+187
-50
No files matched your search
@@ -51,13 +51,21 @@ export function observeArrivalRotation({
|
||||
cleanup();
|
||||
resolve(result);
|
||||
};
|
||||
// A reveal timeout keeps the settle angle it already measured, so a wrong
|
||||
// settle frame is still visible to the caller and cannot be retried away.
|
||||
const expired = () => ({
|
||||
// A timeout keeps every angle it measured: the settle angle before a
|
||||
// reveal timeout, and the angle on a late frame itself (a late frame is
|
||||
// read first; the idle refresh can only repair an angle, so a wrong one
|
||||
// seen late was wrong on time too). `arrivalAngleViolated` then makes the
|
||||
// timeout final instead of retryable.
|
||||
const expired = (late = null) => ({
|
||||
error: `arrival ${moveEnd ? 'reveal' : 'moveEnd'} observation exceeded ${ARRIVAL_OBSERVATION_MS} ms`,
|
||||
elapsedMs: now() - startedAt,
|
||||
moveEndMs,
|
||||
...(moveEnd ? { dMoveEnd: moveEnd.rotationError } : {}),
|
||||
...(moveEnd
|
||||
? { dMoveEnd: moveEnd.rotationError }
|
||||
: late
|
||||
? { dMoveEnd: late.rotationError }
|
||||
: {}),
|
||||
...(moveEnd && late?.shown ? { dReveal: late.rotationError } : {}),
|
||||
flipFrame: -1,
|
||||
});
|
||||
const armDeadline = () => {
|
||||
@@ -69,11 +77,11 @@ export function observeArrivalRotation({
|
||||
try {
|
||||
const at = now();
|
||||
const elapsedMs = at - startedAt;
|
||||
const sample = read();
|
||||
if (at - phaseStartedAt >= ARRIVAL_OBSERVATION_MS) {
|
||||
finish(expired());
|
||||
finish(expired(sample));
|
||||
return;
|
||||
}
|
||||
const sample = read();
|
||||
if (moveEnd === null) {
|
||||
moveEnd = sample;
|
||||
moveEndMs = elapsedMs;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import * as arrivalModule from './trackArrivalObservation.mjs';
|
||||
import {
|
||||
ARRIVAL_OBSERVATION_MS,
|
||||
arrivalAngleViolated,
|
||||
@@ -182,11 +184,12 @@ test('missing moveEnd frames fail without waiting for a render callback', async
|
||||
// Let a retry's next attempt register its postRender listener.
|
||||
const settle = () => new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
function retryFixture() {
|
||||
function retryFixture(extra = {}) {
|
||||
const counts = { prepares: 0 };
|
||||
const f = fixture((options) =>
|
||||
observeArrivalRotationWithRetry({
|
||||
...options,
|
||||
...extra,
|
||||
async prepare() {
|
||||
counts.prepares++;
|
||||
},
|
||||
@@ -264,3 +267,92 @@ test('a missing or non-finite angle never passes', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('a wrong angle on a late reveal frame is final, not retried into a pass', async () => {
|
||||
const f = retryFixture();
|
||||
await settle();
|
||||
f.frame(100, { shown: true, rotationError: 0 });
|
||||
// Shown at 600 ms (500 ms after settle) still wearing the tampered nose.
|
||||
f.frame(600, { shown: true, rotationError: 1.5 });
|
||||
await settle();
|
||||
f.frame(608, { shown: true, rotationError: 0 });
|
||||
f.frame(688, { shown: true, rotationError: 0 });
|
||||
const result = await f.observation;
|
||||
assert.match(result.error, /reveal.*500 ms/);
|
||||
assert.equal(result.dReveal, 1.5);
|
||||
assert.equal(result.attempts, 1);
|
||||
assert.equal(f.resources.moveEnds, 1);
|
||||
assert.equal(arrivalRotationPassed(result), false);
|
||||
});
|
||||
|
||||
test('a wrong angle on a late settle frame is final, not retried into a pass', async () => {
|
||||
const f = retryFixture();
|
||||
await settle();
|
||||
f.frame(926, { shown: true, rotationError: 1.5 });
|
||||
await settle();
|
||||
f.frame(934, { shown: true, rotationError: 0 });
|
||||
f.frame(1014, { shown: true, rotationError: 0 });
|
||||
const result = await f.observation;
|
||||
assert.match(result.error, /moveEnd.*500 ms/);
|
||||
assert.equal(result.dMoveEnd, 1.5);
|
||||
assert.equal(result.attempts, 1);
|
||||
assert.equal(arrivalRotationPassed(result), false);
|
||||
});
|
||||
|
||||
test('a late frame with correct angles stays an inconclusive, retryable timeout', async () => {
|
||||
const f = retryFixture();
|
||||
await settle();
|
||||
f.frame(926, { shown: true, rotationError: 0 });
|
||||
await settle();
|
||||
f.frame(934, { shown: true, rotationError: 0 });
|
||||
f.frame(1014, { shown: true, rotationError: 0 });
|
||||
const result = await f.observation;
|
||||
assert.equal(result.attempts, 2);
|
||||
assert.equal(arrivalRotationPassed(result), true);
|
||||
});
|
||||
|
||||
test('a wrong first reveal on time is final: no retry', async () => {
|
||||
const f = retryFixture();
|
||||
await settle();
|
||||
f.frame(8, { shown: true, rotationError: 0 });
|
||||
f.frame(88, { shown: true, rotationError: 1.5 });
|
||||
const result = await f.observation;
|
||||
assert.equal(result.error, undefined);
|
||||
assert.equal(result.attempts, 1);
|
||||
assert.equal(f.resources.moveEnds, 1);
|
||||
assert.equal(arrivalRotationPassed(result), false);
|
||||
});
|
||||
|
||||
test('maxAttempts bounds inconclusive retries', async () => {
|
||||
const f = retryFixture({ maxAttempts: 3 });
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
await settle();
|
||||
f.expire(attempt * 500);
|
||||
}
|
||||
const result = await f.observation;
|
||||
assert.equal(result.attempts, 3);
|
||||
assert.equal(f.counts.prepares, 3);
|
||||
assert.equal(arrivalRotationPassed(result), false);
|
||||
});
|
||||
|
||||
test('the 10° limit is inclusive', () => {
|
||||
const limit = Math.PI / 18;
|
||||
assert.equal(arrivalAngleViolated({ dMoveEnd: limit }), true);
|
||||
assert.equal(arrivalAngleViolated({ dReveal: limit * 0.999 }), false);
|
||||
});
|
||||
|
||||
test('every name the track harness imports from this module is exported', () => {
|
||||
const harness = readFileSync(
|
||||
new URL('../../scripts/track-regression.mjs', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const names = [
|
||||
...harness.matchAll(
|
||||
/(?:import|const)\s*\{([^}]+)\}\s*(?:from\s*|=\s*await import\()[^;]*trackArrivalObservation\.mjs/g,
|
||||
),
|
||||
].flatMap((match) => match[1].split(',').map((name) => name.trim()));
|
||||
assert.ok(names.includes('arrivalRotationPassed'));
|
||||
assert.ok(names.includes('observeArrivalRotationWithRetry'));
|
||||
for (const name of names)
|
||||
assert.equal(typeof arrivalModule[name], 'function', name);
|
||||
});
|
||||
Reference in new issue
Block a user