Skip to content
File

Blob: tests/e2e/specs/oidc-flow.spec.ts

typescript82 lines
1import { execFile as execFileCallback, spawn } from "node:child_process";
2import { readFileSync } from "node:fs";
3import { promisify } from "node:util";
4 
5import { expect, test } from "@playwright/test";
6 
7import { E2E_CONTEXT_PATH_ENV, type PersistedE2eContext } from "../global-setup";
8import { getFreePort, seedInvite, stopDevServer } from "../harness";
9 
10const execFile = promisify(execFileCallback);
11 
12test("runs the OIDC and browser consent simulators against isolated local D1", async ({ page }) => {
13 const contextPath = process.env[E2E_CONTEXT_PATH_ENV];
14 if (!contextPath) throw new Error("Missing isolated e2e context");
15 const context = JSON.parse(readFileSync(contextPath, "utf8")) as PersistedE2eContext;
16 const invite = await seedInvite(context.tempDir, "oidc-e2e@example.com");
17 const accepted = await page.request.post(`${context.baseUrl}/api/invite/${invite.token}`, {
18 headers: { origin: context.baseUrl },
19 data: { name: "OIDC E2E", password: invite.password, turnstileToken: "loopback" },
20 });
21 expect(accepted.ok()).toBe(true);
22 
23 // Promote only this fixture in the test runner's temporary local database.
24 await execFile("npx", [
25 "--no-install",
26 "wrangler",
27 "d1",
28 "execute",
29 "tessera-prod",
30 "--local",
31 "--persist-to",
32 context.tempDir,
33 "--command",
34 "UPDATE users SET role = 'admin' WHERE email = 'oidc-e2e@example.com'",
35 ]);
36 const clientRun = await execFile("npm", ["run", "test:client"], {
37 env: { ...process.env, ISSUER: context.baseUrl, TEST_EMAIL: invite.email, TEST_PASSWORD: invite.password },
38 });
39 expect(clientRun.stdout).toContain("OIDC roundtrip succeeded");
40 
41 const port = await getFreePort();
42 const harnessUrl = `http://127.0.0.1:${port}`;
43 const created = await page.request.post(`${context.baseUrl}/api/admin/clients`, {
44 headers: { origin: context.baseUrl },
45 data: { name: "consent e2e", redirectUris: [`${harnessUrl}/cb`] },
46 });
47 expect(created.status()).toBe(201);
48 const client = (await created.json()) as { client_id: string; client_secret: string };
49 const server = spawn("npm", ["run", "test:consent"], {
50 env: {
51 ...process.env,
52 ISSUER: context.baseUrl,
53 PORT: String(port),
54 CLIENT_ID: client.client_id,
55 CLIENT_SECRET: client.client_secret,
56 },
57 stdio: "ignore",
58 detached: process.platform !== "win32",
59 });
60 try {
61 await expect
62 .poll(
63 async () => {
64 try {
65 return (await fetch(harnessUrl)).status;
66 } catch {
67 return 0;
68 }
69 },
70 { timeout: 15_000 },
71 )
72 .toBe(200);
73 await page.goto(harnessUrl);
74 await page.getByRole("link", { name: "Start /authorize →" }).click();
75 await expect(page.getByRole("heading", { name: "Authorize access" })).toBeVisible();
76 await page.getByRole("button", { name: "Authorize", exact: true }).click();
77 await expect(page.getByRole("heading", { name: "Last result ✓ ok" })).toBeVisible();
78 } finally {
79 await stopDevServer(server);
80 }
81});