test(e2e): close fixture server reliably

This commit is contained in:
Timo
2026-08-26 00:08:00 +02:00
parent 76a123fac9
commit 5854c106b8
4 changed files with 116 additions and 55 deletions
+73 -47
View File
@@ -8,8 +8,8 @@ import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'fixtures');
const port = Number(process.argv[2] || 4173);
const modulePath = fileURLToPath(import.meta.url);
const root = path.resolve(path.dirname(modulePath), 'fixtures');
const TYPES = {
'.html': 'text/html; charset=utf-8',
@@ -18,62 +18,88 @@ const TYPES = {
'.json': 'application/json'
};
const server = http.createServer((req, res) => {
const requested = decodeURIComponent(new URL(req.url, 'http://localhost').pathname);
if (requested === '/redirect/hidden-player') {
res.writeHead(302, {
Location: `http://127.0.0.1:${port}/pages/frames/player-frame-2.html?redirected=hidden`,
'Cache-Control': 'no-store'
}).end();
return;
}
const filePath = path.resolve(root, `.${requested}`);
export function createFixtureServer(port) {
return http.createServer((req, res) => {
const requested = decodeURIComponent(new URL(req.url, 'http://localhost').pathname);
if (requested === '/redirect/hidden-player') {
res.writeHead(302, {
Location: `http://127.0.0.1:${port}/pages/frames/player-frame-2.html?redirected=hidden`,
'Cache-Control': 'no-store'
}).end();
return;
}
const filePath = path.resolve(root, `.${requested}`);
if (!filePath.startsWith(root + path.sep)) {
res.writeHead(403).end('forbidden');
return;
}
fs.stat(filePath, (statErr, stat) => {
if (statErr || !stat.isFile()) {
res.writeHead(404).end('not found');
if (!filePath.startsWith(root + path.sep)) {
res.writeHead(403).end('forbidden');
return;
}
const contentType = TYPES[path.extname(filePath)] || 'application/octet-stream';
// Media needs byte ranges: without them Chromium reports an empty
// seekable range and seeking silently does nothing, which would make
// the remote-seek test fail for a reason that has nothing to do with
// the extension.
const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || '');
if (range) {
const start = range[1] ? Number(range[1]) : 0;
const end = range[2] ? Number(range[2]) : stat.size - 1;
if (Number.isNaN(start) || Number.isNaN(end) || start > end || end >= stat.size) {
res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` }).end();
fs.stat(filePath, (statErr, stat) => {
if (statErr || !stat.isFile()) {
res.writeHead(404).end('not found');
return;
}
res.writeHead(206, {
const contentType = TYPES[path.extname(filePath)] || 'application/octet-stream';
// Media needs byte ranges: without them Chromium reports an empty
// seekable range and seeking silently does nothing, which would make
// the remote-seek test fail for a reason that has nothing to do with
// the extension.
const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || '');
if (range) {
const start = range[1] ? Number(range[1]) : 0;
const end = range[2] ? Number(range[2]) : stat.size - 1;
if (Number.isNaN(start) || Number.isNaN(end) || start > end || end >= stat.size) {
res.writeHead(416, { 'Content-Range': `bytes */${stat.size}` }).end();
return;
}
res.writeHead(206, {
'Content-Type': contentType,
'Content-Length': end - start + 1,
'Content-Range': `bytes ${start}-${end}/${stat.size}`,
'Accept-Ranges': 'bytes',
'Cache-Control': 'no-store'
});
fs.createReadStream(filePath, { start, end }).pipe(res);
return;
}
res.writeHead(200, {
'Content-Type': contentType,
'Content-Length': end - start + 1,
'Content-Range': `bytes ${start}-${end}/${stat.size}`,
'Content-Length': stat.size,
'Accept-Ranges': 'bytes',
'Cache-Control': 'no-store'
});
fs.createReadStream(filePath, { start, end }).pipe(res);
return;
}
res.writeHead(200, {
'Content-Type': contentType,
'Content-Length': stat.size,
'Accept-Ranges': 'bytes',
'Cache-Control': 'no-store'
fs.createReadStream(filePath).pipe(res);
});
fs.createReadStream(filePath).pipe(res);
});
});
}
server.listen(port, '127.0.0.1', () => {
export async function startFixtureServer(port = Number(process.env.KOALA_E2E_PORT || 4173)) {
const server = createFixtureServer(port);
await new Promise((resolve, reject) => {
const onError = error => reject(error);
server.once('error', onError);
server.listen(port, '127.0.0.1', () => {
server.off('error', onError);
resolve();
});
});
return server;
}
export async function stopFixtureServer(server) {
if (!server?.listening) return;
await new Promise((resolve, reject) => {
server.close(error => error ? reject(error) : resolve());
server.closeAllConnections?.();
});
}
const isMainModule = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(modulePath);
if (isMainModule) {
const port = Number(process.argv[2] || process.env.KOALA_E2E_PORT || 4173);
await startFixtureServer(port);
console.log(`fixture server on http://localhost:${port}`);
});
}