fix test docker
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
web:
|
||||
environment:
|
||||
SESSION_SECRET: "test-session-secret-32chars!xxx"
|
||||
SESSION_SECRET: "test-session-secret-32chars!xxxY"
|
||||
ADMIN_USERNAME: testadmin
|
||||
ADMIN_PASSWORD: "TestPassword2026!"
|
||||
BASE_URL: http://localhost:3000
|
||||
|
||||
+37
-20
@@ -1,4 +1,4 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { execFileSync, execFileSync as runCmd } from 'node:child_process';
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
@@ -10,41 +10,53 @@ const COMPOSE_ARGS = [
|
||||
const HEALTH_URL = 'http://localhost:3000/api/health';
|
||||
const AUTH_DIR = resolve(process.cwd(), 'tests/.auth');
|
||||
const AUTH_FILE = resolve(AUTH_DIR, 'admin.json');
|
||||
const MAX_WAIT_MS = 120_000;
|
||||
const POLL_INTERVAL_MS = 2_000;
|
||||
const MAX_WAIT_MS = 180_000;
|
||||
const POLL_INTERVAL_MS = 3_000;
|
||||
|
||||
async function waitForHealth(): Promise<void> {
|
||||
const start = Date.now();
|
||||
let attempt = 0;
|
||||
while (Date.now() - start < MAX_WAIT_MS) {
|
||||
attempt++;
|
||||
try {
|
||||
const res = await fetch(HEALTH_URL);
|
||||
if (res.status === 200) {
|
||||
console.log('[global-setup] App is healthy');
|
||||
console.log(`[global-setup] App is healthy (attempt ${attempt})`);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// not ready yet
|
||||
console.log(`[global-setup] Health check attempt ${attempt}: HTTP ${res.status}, retrying...`);
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
console.log(`[global-setup] Health check attempt ${attempt}: ${msg}, retrying in ${POLL_INTERVAL_MS / 1000}s...`);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
||||
}
|
||||
|
||||
// Dump container logs to help diagnose the failure
|
||||
console.error('[global-setup] Health check timed out. Container logs:');
|
||||
try {
|
||||
execFileSync('docker', [...COMPOSE_ARGS, 'logs', '--tail=50'], { stdio: 'inherit', cwd: process.cwd() });
|
||||
} catch { /* ignore */ }
|
||||
|
||||
throw new Error(`App did not become healthy within ${MAX_WAIT_MS}ms`);
|
||||
}
|
||||
|
||||
async function seedAuthState(): Promise<void> {
|
||||
// Navigate via the web login form to get a real session cookie.
|
||||
// The app uses credentials-based NextAuth signin.
|
||||
// We POST to the credentials callback directly.
|
||||
const callbackUrl = 'http://localhost:3000';
|
||||
console.log('[global-setup] Seeding auth state...');
|
||||
|
||||
// First, get CSRF token from NextAuth
|
||||
// Get CSRF token from NextAuth
|
||||
const csrfRes = await fetch('http://localhost:3000/api/auth/csrf');
|
||||
if (!csrfRes.ok) {
|
||||
throw new Error(`CSRF request failed: ${csrfRes.status}`);
|
||||
}
|
||||
const csrfData = await csrfRes.json() as { csrfToken: string };
|
||||
console.log('[global-setup] Got CSRF token');
|
||||
|
||||
const params = new URLSearchParams({
|
||||
csrfToken: csrfData.csrfToken,
|
||||
username: 'testadmin',
|
||||
password: 'TestPassword2026!',
|
||||
callbackUrl,
|
||||
password: 'TestPassword2026!', // matches ADMIN_PASSWORD in docker-compose.test.yml
|
||||
callbackUrl: 'http://localhost:3000',
|
||||
json: 'true',
|
||||
});
|
||||
|
||||
@@ -58,7 +70,9 @@ async function seedAuthState(): Promise<void> {
|
||||
redirect: 'manual',
|
||||
});
|
||||
|
||||
// Collect all cookies from both responses
|
||||
console.log(`[global-setup] Sign-in response: HTTP ${signinRes.status}`);
|
||||
|
||||
// Collect cookies from both responses
|
||||
const allCookieHeaders: string[] = [];
|
||||
for (const [k, v] of csrfRes.headers.entries()) {
|
||||
if (k === 'set-cookie') allCookieHeaders.push(v);
|
||||
@@ -94,23 +108,26 @@ async function seedAuthState(): Promise<void> {
|
||||
}).filter(Boolean)
|
||||
);
|
||||
|
||||
const storageState = {
|
||||
cookies,
|
||||
origins: [],
|
||||
};
|
||||
console.log(`[global-setup] Collected ${cookies.length} cookies`);
|
||||
|
||||
const storageState = { cookies, origins: [] };
|
||||
mkdirSync(AUTH_DIR, { recursive: true });
|
||||
writeFileSync(AUTH_FILE, JSON.stringify(storageState, null, 2));
|
||||
console.log('[global-setup] Auth state seeded at', AUTH_FILE);
|
||||
console.log('[global-setup] Auth state saved to', AUTH_FILE);
|
||||
}
|
||||
|
||||
export default async function globalSetup() {
|
||||
console.log('[global-setup] Starting Docker Compose test stack...');
|
||||
execFileSync('docker', [...COMPOSE_ARGS, 'up', '-d', '--build'], {
|
||||
execFileSync('docker', [
|
||||
...COMPOSE_ARGS,
|
||||
'up', '-d', '--build',
|
||||
'--wait', '--wait-timeout', '120',
|
||||
], {
|
||||
stdio: 'inherit',
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
|
||||
console.log('[global-setup] Containers up. Waiting for /api/health...');
|
||||
await waitForHealth();
|
||||
await seedAuthState();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user