/** * Signing the check tools in. Since #177 every API route but `auth/*` needs a session, so every * seed, smoke and e2e run logs in first – as `e2e`, the account that owns the four test projects. * * The password is never in the repo: it comes from `GAME_MAKER_E2E_PASSWORD` or from * `data/e2e-password.txt`, which is git-ignored and written once per server (CI writes its own). */ import { readFile } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; export const E2E_USER = 'e2e'; const PASSWORD_FILE = resolve(dirname(fileURLToPath(import.meta.url)), '../../data/e2e-password.txt'); const COOKIE = 'game_maker_session'; /** One session per server for the whole process; logging in per request would be pointless load. */ const cookies = new Map(); /** The password of the `e2e` account; the step that drives the sign-in screen types it in. */ export async function e2ePassword() { const fromEnv = process.env.GAME_MAKER_E2E_PASSWORD; if (fromEnv) return fromEnv; try { return (await readFile(PASSWORD_FILE, 'utf8')).trim(); } catch { throw new Error( `no password for the "${E2E_USER}" account: set GAME_MAKER_E2E_PASSWORD or put it in ${PASSWORD_FILE}`, ); } } /** `game_maker_session=…` for `Cookie` headers, logging in on first use. */ export async function sessionCookie(baseUrl) { const cached = cookies.get(baseUrl); if (cached) return cached; const res = await fetch(`${baseUrl}api/index.php?r=auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: E2E_USER, password: await e2ePassword() }), signal: AbortSignal.timeout(20_000), }); if (!res.ok) { throw new Error(`could not sign in as "${E2E_USER}" at ${baseUrl}: HTTP ${res.status}`); } // `getSetCookie` keeps the headers apart; a joined string cannot be split on commas, because a // cookie's own Expires date carries one. const set = res.headers.getSetCookie().find((c) => c.startsWith(`${COOKIE}=`)); if (!set) throw new Error(`signing in at ${baseUrl} returned no ${COOKIE} cookie`); const cookie = set.split(';')[0]; cookies.set(baseUrl, cookie); 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) }; } /** The session token alone – what the browser gets handed over CDP. */ export async function sessionToken(baseUrl) { return (await sessionCookie(baseUrl)).slice(COOKIE.length + 1); } export const SESSION_COOKIE = COOKIE;