125 lines
2.8 KiB
JavaScript
125 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const DEFAULT_BASE_URL = `http://127.0.0.1:${process.env.PORT || "3001"}`;
|
|
const BASE_URL = stripTrailingSlash(
|
|
process.env.SMOKE_BASE_URL || process.argv[2] || DEFAULT_BASE_URL
|
|
);
|
|
const REQUEST_TIMEOUT_MS = Number(process.env.SMOKE_TIMEOUT_MS || 20_000);
|
|
|
|
const checks = [
|
|
{
|
|
path: "/",
|
|
method: "HEAD",
|
|
expect: { status: [200] },
|
|
},
|
|
{
|
|
path: "/classic",
|
|
method: "HEAD",
|
|
expect: { status: [307, 308], location: "/" },
|
|
},
|
|
{
|
|
path: "/chat/test-id",
|
|
method: "HEAD",
|
|
expect: { status: [307, 308], location: "/" },
|
|
},
|
|
{
|
|
path: "/settings",
|
|
method: "HEAD",
|
|
expect: {
|
|
status: [307, 308],
|
|
location: "/login?callbackUrl=%2Fsettings",
|
|
},
|
|
},
|
|
];
|
|
|
|
let hasFailure = false;
|
|
|
|
for (const check of checks) {
|
|
const result = await runCheck(check);
|
|
if (!result.ok) {
|
|
hasFailure = true;
|
|
console.error(`FAIL ${check.method} ${check.path} - ${result.message}`);
|
|
continue;
|
|
}
|
|
|
|
console.log(`PASS ${check.method} ${check.path} - ${result.message}`);
|
|
}
|
|
|
|
if (hasFailure) {
|
|
process.exit(1);
|
|
}
|
|
|
|
async function runCheck(check) {
|
|
const startedAt = Date.now();
|
|
const url = `${BASE_URL}${check.path}`;
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: check.method,
|
|
redirect: "manual",
|
|
headers: {
|
|
"user-agent": "Codex Route Smoke Check",
|
|
},
|
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
});
|
|
const durationMs = Date.now() - startedAt;
|
|
const location = response.headers.get("location");
|
|
|
|
if (!check.expect.status.includes(response.status)) {
|
|
return {
|
|
ok: false,
|
|
message: `expected status ${check.expect.status.join(
|
|
"/"
|
|
)}, got ${response.status} in ${durationMs}ms`,
|
|
};
|
|
}
|
|
|
|
if (
|
|
check.expect.location &&
|
|
!matchesLocation(location, check.expect.location)
|
|
) {
|
|
return {
|
|
ok: false,
|
|
message: `expected location ${check.expect.location}, got ${
|
|
location || "<empty>"
|
|
} in ${durationMs}ms`,
|
|
};
|
|
}
|
|
|
|
const redirectMessage = check.expect.location
|
|
? `, location ${check.expect.location}`
|
|
: "";
|
|
|
|
return {
|
|
ok: true,
|
|
message: `status ${response.status}${redirectMessage} in ${durationMs}ms`,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
ok: false,
|
|
message: error instanceof Error ? error.message : String(error),
|
|
};
|
|
}
|
|
}
|
|
|
|
function matchesLocation(actual, expected) {
|
|
if (!actual) {
|
|
return false;
|
|
}
|
|
|
|
if (actual === expected) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
const actualUrl = new URL(actual, BASE_URL);
|
|
return `${actualUrl.pathname}${actualUrl.search}` === expected;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function stripTrailingSlash(value) {
|
|
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
}
|