import assert from 'node:assert/strict'; import { createServer as createHttpServer } from 'node:http'; import test from 'node:test'; import { createServer as createViteServer } from 'vite'; test('development server proxies login, API, and logout to one backend', async () => { const seen = []; const backend = createHttpServer((request, response) => { seen.push(`${request.method} ${request.url}`); response.writeHead(request.url === '/login' ? 303 : 200, { 'Content-Type': 'application/json', Location: '/oidc' }); response.end('{}'); }); await listen(backend); const address = backend.address(); if (address === null || typeof address === 'string') { throw new Error('backend did not bind a TCP port'); } const previous = process.env.NUTHATCH_DEV_BACKEND; process.env.NUTHATCH_DEV_BACKEND = `http://127.0.0.1:${address.port}`; const vite = await createViteServer({ server: { host: '127.0.0.1', port: 0 } }); try { await vite.listen(); const origin = vite.resolvedUrls?.local[0]; if (origin === undefined) throw new Error('Vite did not publish a local URL'); await fetch(new URL('/api/session', origin)); await fetch(new URL('/login', origin), { redirect: 'manual' }); await fetch(new URL('/logout', origin), { method: 'POST' }); assert.deepEqual(seen, [ 'GET /api/session', 'GET /login', 'POST /logout' ]); } finally { await vite.close(); await close(backend); if (previous === undefined) { delete process.env.NUTHATCH_DEV_BACKEND; } else { process.env.NUTHATCH_DEV_BACKEND = previous; } } }); function listen(server) { return new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); }); } function close(server) { return new Promise((resolve, reject) => { server.close((error) => (error === undefined ? resolve() : reject(error))); }); }