Files
continuum-backend/scripts/e2e-supabase-user.ts
T
octoturge c72bfe71d6 Show the picture in the real web console, in a real browser
Every earlier camera proof stood in for the browser with a werift peer.
The question that decides whether the feature works is whether a person
looking at the fleet page sees a picture, and the web console had never
been opened.

BROWSER=1 on scripts/e2e-camera.sh now starts the real Nuxt app and
drives it in real Chrome (puppeteer-core): sign in, open the fleet page,
wait for the camera player, and check the result. Measured against the
H2C: the app's own Supabase client signs in, the header reports Live
(the telemetry socket authenticated with the real token), the player
reports connected, Chrome decodes 1680x1080 frames, playback time
advances, the frame has picture content (mean luma 88, stdev 62.8), and
the browser's own ICE stats show the selected candidate is a TURN relay.
No uncaught page errors.

The login page only offers a magic link by email, which a script cannot
follow, so the driver signs in with the app's $supabase.client and a
password just set on a throwaway user. That means AUTH=supabase: the
backend verifies against the real project's JWKS rather than the local
dev issuer, with e2e-supabase-user.ts creating or reusing
continuum-e2e@example.invalid (a reserved .invalid address) and
resetting its password each run. The JSON it prints carries a password
and a live token, so the shell captures it and never echoes it.

The frame is judged by pixel statistics and no screenshot is kept: it is
the inside of someone's workshop, and "is there a picture" does not need
one stored. The relay is read from getStats() through a wrapped
RTCPeerConnection rather than inferred from configuration. A browser run
with fewer than its nine results fails, so a driver that dies quietly
cannot read as a pass.

puppeteer-core is a devDependency for these scripts alone. Its 22
resolved packages are all Apache-2.0, BSD-3-Clause, ISC or MIT.
2026-10-24 11:47:24 +00:00

55 lines
2.3 KiB
TypeScript

/**
* Provisions the real Supabase test user for scripts/e2e-camera.sh and prints
* one JSON object: { id, email, password, accessToken }.
*
* The output carries a password and a live access token, so the caller must
* capture it into a variable and never echo it. The account is reused rather
* than piling up one per run: `.invalid` is reserved by RFC 2606, so the
* address can never belong to a real person, and its password is reset every
* time to a fresh random one that exists only for this run.
*
* Environment: SUPABASE_URL, SUPABASE_SECRET_KEY, SUPABASE_PUBLISHABLE_KEY.
*/
const url = process.env.SUPABASE_URL!;
const secret = process.env.SUPABASE_SECRET_KEY!;
const publishable = process.env.SUPABASE_PUBLISHABLE_KEY!;
const email = "continuum-e2e@example.invalid";
const password = `e2e-${crypto.randomUUID()}`;
const admin = { apikey: secret, Authorization: `Bearer ${secret}`, "Content-Type": "application/json" };
const die = (message: string): never => {
console.error(`e2e-supabase-user: ${message}`);
process.exit(1);
};
let id: string | undefined;
const made = await fetch(`${url}/auth/v1/admin/users`, {
method: "POST",
headers: admin,
body: JSON.stringify({ email, password, email_confirm: true }),
});
if (made.ok) {
id = ((await made.json()) as { id?: string }).id;
} else {
const list = (await fetch(`${url}/auth/v1/admin/users?per_page=200`, { headers: admin }).then((r) => r.json())) as {
users?: { id: string; email: string }[];
};
id = list.users?.find((u) => u.email === email)?.id;
if (!id) die(`could not create or find the test user (HTTP ${made.status})`);
const reset = await fetch(`${url}/auth/v1/admin/users/${id}`, {
method: "PUT",
headers: admin,
body: JSON.stringify({ password, email_confirm: true }),
});
if (!reset.ok) die(`could not reset the test user's password (HTTP ${reset.status})`);
}
const session = (await fetch(`${url}/auth/v1/token?grant_type=password`, {
method: "POST",
headers: { apikey: publishable, "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
}).then((r) => r.json())) as { access_token?: string };
if (!session.access_token || !id) die("the test user could not sign in");
console.log(JSON.stringify({ id, email, password, accessToken: session.access_token }));