93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
import { execFileSync } from 'node:child_process';
|
||
|
||
const image = process.env.EASYFLOW_FRONTEND_IMAGE || 'easyflow-frontend:0.1';
|
||
const containerName = `easyflow-frontend-smoke-${process.pid}`;
|
||
|
||
function docker(args) {
|
||
return execFileSync('docker', args, {
|
||
encoding: 'utf8',
|
||
stdio: ['ignore', 'pipe', 'pipe'],
|
||
}).trim();
|
||
}
|
||
|
||
async function waitForServer(origin) {
|
||
let lastError;
|
||
for (let attempt = 0; attempt < 40; attempt += 1) {
|
||
try {
|
||
const response = await fetch(`${origin}/flow/`);
|
||
if (response.ok) {
|
||
return;
|
||
}
|
||
lastError = new Error(`HTTP ${response.status}`);
|
||
} catch (error) {
|
||
lastError = error;
|
||
}
|
||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||
}
|
||
throw new Error(`Nginx 未在预期时间内启动: ${String(lastError)}`);
|
||
}
|
||
|
||
function assert(condition, message) {
|
||
if (!condition) {
|
||
throw new Error(message);
|
||
}
|
||
}
|
||
|
||
try {
|
||
docker([
|
||
'run',
|
||
'--detach',
|
||
'--rm',
|
||
'--publish',
|
||
'127.0.0.1::80',
|
||
'--name',
|
||
containerName,
|
||
image,
|
||
]);
|
||
|
||
const portBinding = docker(['port', containerName, '80/tcp'])
|
||
.split('\n')
|
||
.find((line) => line.startsWith('127.0.0.1:'));
|
||
const port = portBinding?.match(/:(\d+)$/)?.[1];
|
||
assert(port, `无法解析容器端口: ${portBinding || 'empty'}`);
|
||
|
||
const origin = `http://127.0.0.1:${port}`;
|
||
await waitForServer(origin);
|
||
|
||
const rootResponse = await fetch(`${origin}/`, { redirect: 'manual' });
|
||
assert(rootResponse.status === 302, `根路径状态异常: ${rootResponse.status}`);
|
||
const rootLocation = rootResponse.headers.get('location');
|
||
assert(
|
||
rootLocation && new URL(rootLocation, origin).pathname === '/flow/',
|
||
`根路径重定向异常: ${rootLocation}`,
|
||
);
|
||
|
||
const flowResponse = await fetch(`${origin}/flow/`);
|
||
const indexHtml = await flowResponse.text();
|
||
assert(flowResponse.ok, `/flow/ 状态异常: ${flowResponse.status}`);
|
||
assert(indexHtml.includes('<div id="app">'), '/flow/ 未返回应用入口');
|
||
|
||
const assetPath = indexHtml.match(/(?:src|href)="(\/flow\/[^"]+)"/)?.[1];
|
||
assert(assetPath, '构建产物未使用 /flow/ 静态资源前缀');
|
||
const assetResponse = await fetch(`${origin}${assetPath}`);
|
||
assert(assetResponse.ok, `静态资源状态异常: ${assetResponse.status}`);
|
||
|
||
const invalidRootShareResponse = await fetch(
|
||
`${origin}/share/knowledge?shareKey=smoke-test`,
|
||
);
|
||
assert(
|
||
invalidRootShareResponse.status === 404,
|
||
`根路径分享地址应返回 404,实际为 ${invalidRootShareResponse.status}`,
|
||
);
|
||
|
||
console.log(
|
||
'部署冒烟通过:根路径重定向、/flow/ 入口、静态资源前缀和错误分享路径均符合预期。',
|
||
);
|
||
} finally {
|
||
try {
|
||
docker(['rm', '--force', containerName]);
|
||
} catch {
|
||
// 容器可能因 --rm 已自动删除。
|
||
}
|
||
}
|