e2e: relocation self-heal legs at FAST rate — pure arrival waits (v1.9.0)
CI / build + unit + e2e (pull_request) Successful in 5m31s

This commit is contained in:
2026-09-27 20:58:05 +00:00
parent d2b799e572
commit 479c5448f7
3 changed files with 12 additions and 9 deletions
+3
View File
@@ -1,5 +1,8 @@
# Changelog # Changelog
## v1.9.0 — 2026-09-27 20:57
- **E2E speedup follow-up (test-only, no game code changed).** `e2e/relocation.spec.ts`'s self-heal helper `flyHomeViaApi` now fast-forwards its cleanup legs at the v1.8.0 `FAST = 10_000` rate instead of a blanket `SCALE = 1000` — every scale-dependent wait in that file is a pure arrival wait (status `'available'` / `locationIcao === HUB`) with no mid-leg observation, so the higher rate is safe and the now-unused `SCALE` constant was dropped. Expected CI wall time for the file drops from ~40 s to ~25 s (~2–3 s off test 1, ~10–11 s each off tests 2 and 3). Test 3's deliberate `setScale(page, 1)` guard is untouched; `map.spec.ts` wheel-zoom (real-time-bound) and `multiplayer.spec.ts`'s N-player clock (measures sim-vs-real elapsed time) stay as-is.
## v1.8.0 — 2026-09-27 20:13 ## v1.8.0 — 2026-09-27 20:13
- **E2E speedup (test-only, no game code changed).** Every Playwright spec that fast-forwards legs now uses the fastest rate its assertions allow instead of a blanket `SCALE = 1000`: pure arrival waits (no mid-leg observation) run at `FAST` — a lagged tick can only land a plane sooner, never skip an observed state. Expected CI wall time for the affected tests drops ~5–6× overall (e.g. the antimeridian test's relocation chains go from ~3 min to ~15 s per direction; two-planes' parallel-flight test loses its ~24 s afterEach heal leg). - **E2E speedup (test-only, no game code changed).** Every Playwright spec that fast-forwards legs now uses the fastest rate its assertions allow instead of a blanket `SCALE = 1000`: pure arrival waits (no mid-leg observation) run at `FAST` — a lagged tick can only land a plane sooner, never skip an observed state. Expected CI wall time for the affected tests drops ~5–6× overall (e.g. the antimeridian test's relocation chains go from ~3 min to ~15 s per direction; two-planes' parallel-flight test loses its ~24 s afterEach heal leg).
- **Per-test rates.** `actions.spec.ts` + `hover.spec.ts` + `two-planes.spec.ts`: new local `FAST = 10_000` for expiry fast-forwards, landing waits and fly-home cleanups. `queue.spec.ts`: same `FAST`, plus `CHAIN = 4_000` for the auto-chain observation (max per-tick advance 2 s × S stays below the LEMD-length leg's ~1e7 sim-ms duration so one lagged tick can't complete it before a poll sees the chained state); its refuel-before-chained-leg test moves to an explicit `2_000` with the fuel lower bound relaxed from `> 1.3×required` to `> required` (at 2_000× the snapshot can land ~0.4×required into leg 2's burn under dind load; a wrong top-up level still lands far below the bound, and the exact level stays pinned by the unchanged cash assertion). `map-interactions.spec.ts`: `FAST = 50_000` for chain hops (≤3400 km each) and arrival waits. - **Per-test rates.** `actions.spec.ts` + `hover.spec.ts` + `two-planes.spec.ts`: new local `FAST = 10_000` for expiry fast-forwards, landing waits and fly-home cleanups. `queue.spec.ts`: same `FAST`, plus `CHAIN = 4_000` for the auto-chain observation (max per-tick advance 2 s × S stays below the LEMD-length leg's ~1e7 sim-ms duration so one lagged tick can't complete it before a poll sees the chained state); its refuel-before-chained-leg test moves to an explicit `2_000` with the fuel lower bound relaxed from `> 1.3×required` to `> required` (at 2_000× the snapshot can land ~0.4×required into leg 2's burn under dind load; a wrong top-up level still lands far below the bound, and the exact level stays pinned by the unchanged cash assertion). `map-interactions.spec.ts`: `FAST = 50_000` for chain hops (≤3400 km each) and arrival waits.
+8 -8
View File
@@ -14,7 +14,7 @@ test.beforeEach(async ({ page }) => { await authenticate(page); });
// works for FLYING planes — it then appends to POST /api/planes/{id}/queue instead of flying now. // works for FLYING planes — it then appends to POST /api/planes/{id}/queue instead of flying now.
// Marker-size regression is covered by the existing e2e/map.spec.ts click tests, not here. // Marker-size regression is covered by the existing e2e/map.spec.ts click tests, not here.
const SCALE = 1000; // test rate for fast-forwarding legs — keeps cleanup flights to seconds of real time const FAST = 10_000; // pure arrival waits (no mid-leg observation) — cleanup legs land in ~1 s
const WORLD = 24; // the fixed world rate (ISS-117) — must match lib/worldClock.ts const WORLD = 24; // the fixed world rate (ISS-117) — must match lib/worldClock.ts
const HUB = 'EDDF'; // Frankfurt Main — the seeded starter plane's home base (lib/game/seed.ts) const HUB = 'EDDF'; // Frankfurt Main — the seeded starter plane's home base (lib/game/seed.ts)
// LFRS Nantes Atlantique — same isolated marker as e2e/map.spec.ts. The v0.70.0 default view ([0,0] z2) // LFRS Nantes Atlantique — same isolated marker as e2e/map.spec.ts. The v0.70.0 default view ([0,0] z2)
@@ -93,8 +93,8 @@ async function flyHomeViaApi(page: Page): Promise<void> {
let s = await getState(page); let s = await getState(page);
let plane = s.planes.find((p) => p.id === 'plane-starter')!; let plane = s.planes.find((p) => p.id === 'plane-starter')!;
if (plane.status === 'flying' || plane.locationIcao !== HUB) { if (plane.status === 'flying' || plane.locationIcao !== HUB) {
// Fast-forward any leftover leg / repositioning flight (~20 s real time worst case at 1000×). // Fast-forward any leftover leg / repositioning flight (~2 s real time worst case at FAST).
await setScale(page, SCALE); await setScale(page, FAST);
} }
if (plane.status === 'flying') { if (plane.status === 'flying') {
// While flying, locationIcao stays at the DEPARTURE airport — re-read state after landing // While flying, locationIcao stays at the DEPARTURE airport — re-read state after landing
@@ -104,8 +104,8 @@ async function flyHomeViaApi(page: Page): Promise<void> {
plane = s.planes.find((p) => p.id === 'plane-starter')!; plane = s.planes.find((p) => p.id === 'plane-starter')!;
} }
if (plane.locationIcao !== HUB) { if (plane.locationIcao !== HUB) {
// Make sure the world rate is back at SCALE so the return leg fast-forwards. // Make sure the world rate is back at FAST so the return leg fast-forwards.
await setScale(page, SCALE); await setScale(page, FAST);
const res = await page.request.post(`/api/planes/${plane.id}/fly`, { data: { destIcao: HUB } }); const res = await page.request.post(`/api/planes/${plane.id}/fly`, { data: { destIcao: HUB } });
expect(res.ok()).toBeTruthy(); // repositioning needs no package (v0.12.0 manual flight) expect(res.ok()).toBeTruthy(); // repositioning needs no package (v0.12.0 manual flight)
await expect.poll(async () => ((await getState(page)).planes.find((p) => p.id === 'plane-starter')!.locationIcao), { timeout: 240_000 }).toBe(HUB); await expect.poll(async () => ((await getState(page)).planes.find((p) => p.id === 'plane-starter')!.locationIcao), { timeout: 240_000 }).toBe(HUB);
@@ -122,7 +122,7 @@ test.afterEach(async ({ page }) => {
test.describe.configure({ mode: 'serial' }); // shared live DB — order matters within this file test.describe.configure({ mode: 'serial' }); // shared live DB — order matters within this file
test('left pane Relocate section relocates a grounded plane', async ({ page }) => { test('left pane Relocate section relocates a grounded plane', async ({ page }) => {
test.setTimeout(300_000); // self-heal (worst case ~20 s at 1000×) + one short leg test.setTimeout(300_000); // self-heal (worst case ~2 s at FAST) + one short leg
await flyHomeViaApi(page); await flyHomeViaApi(page);
// Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway). // Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway).
@@ -154,7 +154,7 @@ test('left pane Relocate section relocates a grounded plane', async ({ page }) =
}); });
test('airport popup offers Relocate options for grounded planes', async ({ page }) => { test('airport popup offers Relocate options for grounded planes', async ({ page }) => {
test.setTimeout(300_000); // self-heal (worst case ~20 s at 1000×) + one leg to LFRS test.setTimeout(300_000); // self-heal (worst case ~2 s at FAST) + one leg to LFRS
await flyHomeViaApi(page); await flyHomeViaApi(page);
// Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway). // Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway).
@@ -221,7 +221,7 @@ test('airport popup offers Relocate options for grounded planes', async ({ page
}); });
test('airport popup Relocate queues a leg for a flying plane', async ({ page }) => { test('airport popup Relocate queues a leg for a flying plane', async ({ page }) => {
test.setTimeout(300_000); // self-heal (worst case ~20 s at 1000×) + one short leg, fast-forwarded in cleanup test.setTimeout(300_000); // self-heal (worst case ~2 s at FAST) + one short leg, fast-forwarded in cleanup
await flyHomeViaApi(page); await flyHomeViaApi(page);
// Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway). // Full tank before departure — deterministic baseline (ISS-082 hook; dispatch would auto-refuel anyway).
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "flight-ops", "name": "flight-ops",
"version": "1.8.0", "version": "1.9.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3000", "dev": "next dev -p 3000",