Files
Cleanuparr/e2e/tests/helpers/app-api.ts
2026-03-12 22:12:20 +02:00

126 lines
3.6 KiB
TypeScript

import { TEST_CONFIG } from './test-config';
const API = TEST_CONFIG.appUrl;
interface TokenResponse {
accessToken: string;
refreshToken: string;
expiresIn: number;
}
interface LoginResponse {
requiresTwoFactor: boolean;
loginToken?: string;
tokens?: TokenResponse;
}
export async function waitForApp(timeoutMs = 90_000): Promise<void> {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
try {
const res = await fetch(`${API}/health`);
if (res.ok) return;
} catch {
// Not ready yet
}
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error(`App did not become ready within ${timeoutMs}ms`);
}
export async function createAccountAndSetup(): Promise<void> {
const createRes = await fetch(`${API}/api/auth/setup/account`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: TEST_CONFIG.adminUsername,
password: TEST_CONFIG.adminPassword,
}),
});
// 409 = controller says account exists; 403 = middleware says setup already completed
if (!createRes.ok && createRes.status !== 409 && createRes.status !== 403) {
throw new Error(`Failed to create account: ${createRes.status}`);
}
const completeRes = await fetch(`${API}/api/auth/setup/complete`, {
method: 'POST',
});
if (!completeRes.ok && completeRes.status !== 409 && completeRes.status !== 403) {
throw new Error(`Failed to complete setup: ${completeRes.status}`);
}
}
export async function loginAndGetToken(): Promise<string> {
const res = await fetch(`${API}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: TEST_CONFIG.adminUsername,
password: TEST_CONFIG.adminPassword,
}),
});
if (!res.ok) throw new Error(`Login failed: ${res.status}`);
const data: LoginResponse = await res.json();
if (data.requiresTwoFactor || !data.tokens) {
throw new Error('Unexpected 2FA requirement in E2E test');
}
return data.tokens.accessToken;
}
export async function updateOidcConfig(
accessToken: string,
updates: Partial<{
enabled: boolean;
providerName: string;
issuerUrl: string;
clientId: string;
clientSecret: string;
scopes: string;
exclusiveMode: boolean;
}>,
): Promise<void> {
const getRes = await fetch(`${API}/api/account/oidc`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!getRes.ok) throw new Error(`Failed to get OIDC config: ${getRes.status}`);
const currentConfig = await getRes.json();
const putRes = await fetch(`${API}/api/account/oidc`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ ...currentConfig, ...updates }),
});
if (!putRes.ok) {
const body = await putRes.text();
throw new Error(`Failed to update OIDC config: ${putRes.status} ${body}`);
}
}
export async function configureOidc(accessToken: string): Promise<void> {
const putRes = await fetch(`${API}/api/account/oidc`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
enabled: true,
issuerUrl: `${TEST_CONFIG.keycloakUrl}/realms/${TEST_CONFIG.realm}`,
clientId: TEST_CONFIG.clientId,
clientSecret: TEST_CONFIG.clientSecret,
scopes: 'openid profile email',
providerName: TEST_CONFIG.oidcProviderName,
}),
});
if (!putRes.ok) {
const body = await putRes.text();
throw new Error(`Failed to configure OIDC: ${putRes.status} ${body}`);
}
}