test: add comprehensive Curio installation and runtime fixtures
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { createServer } from 'node:http';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import { readFile, stat } from 'node:fs/promises';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import path from 'node:path';
|
||||
import { output } from './suite.mjs';
|
||||
|
||||
// A local-only source server; only the two indexes and package files are exposed.
|
||||
export async function serve(port = 0) {
|
||||
let base;
|
||||
const server = createServer(async (request, response) => {
|
||||
try {
|
||||
if (request.method !== 'GET' && request.method !== 'HEAD') { response.writeHead(405).end(); return; }
|
||||
const pathname = new URL(request.url, 'http://localhost').pathname;
|
||||
if (['/index.json', '/faults-index.json'].includes(pathname)) {
|
||||
const index = JSON.parse(await readFile(path.join(output, pathname.slice(1)), 'utf8'));
|
||||
for (const curio of index.curios) for (const version of curio.versions) for (const artifact of version.artifacts) {
|
||||
artifact.url = `${base}/packages/${encodeURIComponent(path.basename(new URL(artifact.url).pathname))}`;
|
||||
}
|
||||
const body = JSON.stringify(index);
|
||||
response.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(body), 'Cache-Control': 'no-store' });
|
||||
response.end(request.method === 'HEAD' ? undefined : body);
|
||||
} else if (/^\/packages\/[a-z0-9][a-z0-9.+-]*\.curio$/.test(pathname)) {
|
||||
const file = path.join(output, pathname.slice(1));
|
||||
const metadata = await stat(file);
|
||||
response.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Length': metadata.size });
|
||||
if (request.method === 'HEAD') response.end();
|
||||
else await pipeline(createReadStream(file), response);
|
||||
} else { response.writeHead(404).end(); }
|
||||
} catch (error) {
|
||||
if (!response.headersSent) response.writeHead(error.code === 'ENOENT' ? 404 : 500);
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
await new Promise((resolve, reject) => {
|
||||
server.once('error', reject);
|
||||
server.listen(port, '127.0.0.1', resolve);
|
||||
});
|
||||
base = `http://127.0.0.1:${server.address().port}`;
|
||||
return { base, close: () => new Promise(resolve => server.close(resolve)) };
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
||||
const port = Number(process.env.QA_HTTP_PORT || 8765);
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('QA_HTTP_PORT must be 1..65535');
|
||||
const server = await serve(port);
|
||||
console.log(`常规 HTTP 源:${server.base}/index.json\n故障 HTTP 源:${server.base}/faults-index.json\nCtrl-C 停止服务。`);
|
||||
process.once('SIGINT', () => { void server.close(); });
|
||||
process.once('SIGTERM', () => { void server.close(); });
|
||||
}
|
||||
Reference in New Issue
Block a user