From 8adef83f23fb8cb03e5ed772d09757dd5bde741a Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Mon, 21 Sep 2026 18:48:47 +0200 Subject: [PATCH] Pixel art e2e checks run through in a full CI run (#130) CI has been red since 66d6aaf (tasks 221 and 223, two failures each). Both failures come from the tests added with #130, not from the editor. - Module 04 still expected 19 asset types in the + menu. Pixel art and Palette make it 21. - Module 48 died with "reading 'assets'". The password change in module 45 ends every other session of the e2e account, including the one the tools had cached, so api() got a 401. api() now drops the cached session and signs in once more on a 401. Tests: - GM_STEPS=4,45,48 (fresh seed): ALL PASSED, 102 checks. - Counter-check: without the retry module 48 dies with exactly the CI error. Co-Authored-By: Claude Opus 5 --- tools/e2e/04-assets.mjs | 4 ++-- tools/lib/auth.mjs | 8 ++++++++ tools/lib/harness.mjs | 13 ++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tools/e2e/04-assets.mjs b/tools/e2e/04-assets.mjs index ba2b42e4..e1ecdf4f 100644 --- a/tools/e2e/04-assets.mjs +++ b/tools/e2e/04-assets.mjs @@ -77,7 +77,7 @@ export async function assets(t) { // 4c. Create a new asset through the + menu. Since #43 the menu is two alphabetical groups – // what can be placed in a scene, a line, then what only lives in the library – and it is - // tall enough to show all nineteen types without scrolling. + // tall enough to show all twenty-one types without scrolling. await clickText('+'); const rows = await json('__gm.menuRows()'); const cut = rows.indexOf(null); @@ -90,7 +90,7 @@ export async function assets(t) { ); check( 'every asset type is offered once, each group sorted by label', - placeable.length + library.length === 19 && + placeable.length + library.length === 21 && String(placeable) === String([...placeable].sort()) && String(library) === String([...library].sort()), JSON.stringify({ placeable, library }), diff --git a/tools/lib/auth.mjs b/tools/lib/auth.mjs index 0753a92c..23077b47 100644 --- a/tools/lib/auth.mjs +++ b/tools/lib/auth.mjs @@ -52,6 +52,14 @@ export async function sessionCookie(baseUrl) { return cookie; } +/** + * Drops the cached session, so the next request signs in again. Changing the password ends every + * other session of the account (#182) – the step that does it leaves the tools' one dead. + */ +export function forgetSession(baseUrl) { + cookies.delete(baseUrl); +} + /** The headers a tool's `fetch` needs on top of its own. */ export async function authHeaders(baseUrl) { return { Cookie: await sessionCookie(baseUrl) }; diff --git a/tools/lib/harness.mjs b/tools/lib/harness.mjs index c0a78abd..229d15b5 100644 --- a/tools/lib/harness.mjs +++ b/tools/lib/harness.mjs @@ -4,7 +4,7 @@ * the summary line is missing, since chromium can die halfway under memory pressure. */ import { writeFileSync } from 'node:fs'; -import { authHeaders, SESSION_COOKIE, sessionToken } from './auth.mjs'; +import { authHeaders, forgetSession, SESSION_COOKIE, sessionToken } from './auth.mjs'; import { launchBrowser } from './browser.mjs'; /** @@ -400,9 +400,16 @@ function makeTools({ cdp, projectId, baseUrl, consoleLog, check, shotPrefix }) { // the whole run hangs on this line instead of failing. const api = async (route) => { const url = `${baseUrl}api/index.php?r=${route}`; + const request = async () => + fetch(url, { headers: await authHeaders(baseUrl), signal: AbortSignal.timeout(SERVER_TIMEOUT_MS) }); try { - const headers = await authHeaders(baseUrl); - return await (await fetch(url, { headers, signal: AbortSignal.timeout(SERVER_TIMEOUT_MS) })).json(); + let res = await request(); + // The session can have been ended by a password change in the login step (#182). + if (res.status === 401) { + forgetSession(baseUrl); + res = await request(); + } + return await res.json(); } catch (error) { throw new Error(`request to ${route} failed: ${error.message}`, { cause: error }); }