fixed event join token handling in the event admin. created new seeders with new tenants and package purchases. added new playwright test scenarios.

This commit is contained in:
Codex Agent
2025-10-26 14:44:47 +01:00
parent 6290a3a448
commit ecf5a23b28
59 changed files with 3900 additions and 691 deletions

View File

@@ -1,4 +1,6 @@
import { test as base, expect, Page } from '@playwright/test';
import 'dotenv/config';
import { test as base, expect, Page, APIRequestContext } from '@playwright/test';
import { randomBytes, createHash } from 'node:crypto';
export type TenantCredentials = {
email: string;
@@ -10,8 +12,8 @@ export type TenantAdminFixtures = {
signInTenantAdmin: () => Promise<void>;
};
const tenantAdminEmail = process.env.E2E_TENANT_EMAIL;
const tenantAdminPassword = process.env.E2E_TENANT_PASSWORD;
const tenantAdminEmail = process.env.E2E_TENANT_EMAIL ?? 'hello@lumen-moments.demo';
const tenantAdminPassword = process.env.E2E_TENANT_PASSWORD ?? 'Demo1234!';
export const test = base.extend<TenantAdminFixtures>({
tenantAdminCredentials: async ({}, use) => {
@@ -42,10 +44,93 @@ export const test = base.extend<TenantAdminFixtures>({
export const expectFixture = expect;
async function performTenantSignIn(page: Page, credentials: TenantCredentials) {
await page.goto('/event-admin/login');
await page.fill('input[name="email"]', credentials.email);
await page.fill('input[name="password"]', credentials.password);
await page.click('button[type="submit"]');
await page.waitForURL(/\/event-admin(\/welcome)?/);
const clientId = process.env.VITE_OAUTH_CLIENT_ID ?? 'tenant-admin-app';
const redirectUri = new URL('/event-admin/auth/callback', process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:8000').toString();
const scopes = (process.env.VITE_OAUTH_SCOPES as string | undefined) ?? 'tenant:read tenant:write';
async function performTenantSignIn(page: Page, _credentials: TenantCredentials) {
const tokens = await exchangeTokens(page.request);
await page.addInitScript(({ stored }) => {
localStorage.setItem('tenant_oauth_tokens.v1', JSON.stringify(stored));
}, { stored: tokens });
await page.goto('/event-admin');
await page.waitForLoadState('domcontentloaded');
}
type StoredTokenPayload = {
accessToken: string;
refreshToken: string;
expiresAt: number;
scope?: string;
};
async function exchangeTokens(request: APIRequestContext): Promise<StoredTokenPayload> {
const verifier = generateCodeVerifier();
const challenge = generateCodeChallenge(verifier);
const state = randomBytes(12).toString('hex');
const params = new URLSearchParams({
response_type: 'code',
client_id: clientId,
redirect_uri: redirectUri,
scope: scopes,
state,
code_challenge: challenge,
code_challenge_method: 'S256',
});
const authResponse = await request.get(`/api/v1/oauth/authorize?${params.toString()}`, {
maxRedirects: 0,
headers: {
'x-playwright-test': 'tenant-admin',
},
});
if (authResponse.status() >= 400) {
throw new Error(`OAuth authorize failed: ${authResponse.status()} ${await authResponse.text()}`);
}
const location = authResponse.headers()['location'];
if (!location) {
throw new Error('OAuth authorize did not return redirect location');
}
const code = new URL(location).searchParams.get('code');
if (!code) {
throw new Error('OAuth authorize response missing code');
}
const tokenResponse = await request.post('/api/v1/oauth/token', {
form: {
grant_type: 'authorization_code',
code,
client_id: clientId,
redirect_uri: redirectUri,
code_verifier: verifier,
},
});
if (!tokenResponse.ok()) {
throw new Error(`OAuth token exchange failed: ${tokenResponse.status()} ${await tokenResponse.text()}`);
}
const body = await tokenResponse.json();
const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : 3600;
return {
accessToken: body.access_token,
refreshToken: body.refresh_token,
expiresAt: Date.now() + Math.max(expiresIn - 30, 0) * 1000,
scope: body.scope,
};
}
function generateCodeVerifier(): string {
return randomBytes(32).toString('base64url');
}
function generateCodeChallenge(verifier: string): string {
return createHash('sha256').update(verifier).digest('base64url');
}