Files
codex-retry-gateway/scripts/test-launch-ui.mjs
T

230 lines
7.3 KiB
JavaScript

#!/usr/bin/env node
import http from "node:http";
import net from "node:net";
import { once } from "node:events";
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { spawn } from "node:child_process";
const scriptsRoot = import.meta.dirname;
const launchScript = path.join(scriptsRoot, "launch-ui.ps1");
const restoreScript = path.join(scriptsRoot, "restore-codex-config.ps1");
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
async function getFreePort() {
const server = net.createServer();
server.listen(0, "127.0.0.1");
await once(server, "listening");
const address = server.address();
const port = address && typeof address === "object" ? address.port : null;
server.close();
await once(server, "close");
if (!port) {
throw new Error("Failed to allocate a free port");
}
return port;
}
function startFakeUpstream(port) {
const server = http.createServer((req, res) => {
if (req.method === "GET" && req.url === "/v1/models") {
res.writeHead(200, {
"content-type": "application/json; charset=utf-8",
"x-upstream-test": "launch-ui-ok",
});
res.end(JSON.stringify({ object: "list", data: [{ id: "launch-ui-test-model" }] }));
return;
}
if (req.method === "POST" && (req.url === "/responses" || req.url === "/v1/responses")) {
let body = "";
req.setEncoding("utf8");
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
const parsed = JSON.parse(body || "{}");
const reasoning = parsed.test_reasoning_tokens ?? 128;
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
res.end(
JSON.stringify({
id: "launch-ui-response",
usage: {
output_tokens_details: {
reasoning_tokens: reasoning,
},
},
}),
);
});
return;
}
res.writeHead(404, { "content-type": "application/json; charset=utf-8" });
res.end(JSON.stringify({ error: "not found" }));
});
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(port, "127.0.0.1", () => resolve(server));
});
}
async function runPowerShellScript(scriptPath, args) {
const child = spawn(
"powershell",
["-ExecutionPolicy", "Bypass", "-File", scriptPath, ...args],
{ stdio: ["ignore", "pipe", "pipe"] },
);
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += chunk.toString();
});
child.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
const [exitCode] = await once(child, "exit");
if (exitCode !== 0) {
throw new Error(`PowerShell script failed: ${scriptPath}\nstdout:\n${stdout}\nstderr:\n${stderr}`);
}
return { stdout, stderr };
}
async function run() {
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "codex-retry-gateway-launch-"));
const codexDir = path.join(tempRoot, ".codex");
const stateRoot = path.join(tempRoot, ".codex-retry-gateway");
const codexConfigPath = path.join(codexDir, "config.toml");
const upstreamPort = await getFreePort();
const gatewayPort = await getFreePort();
const gatewayBaseUrl = `http://127.0.0.1:${gatewayPort}`;
const upstreamBaseUrl = `http://127.0.0.1:${upstreamPort}`;
await mkdir(codexDir, { recursive: true });
await writeFile(
codexConfigPath,
[
'model_provider = "custom"',
"",
"[model_providers.custom]",
'name = "Launch UI Test"',
`base_url = "${upstreamBaseUrl}"`,
'wire_api = "responses"',
"",
].join("\n"),
"utf8",
);
const upstream = await startFakeUpstream(upstreamPort);
try {
await runPowerShellScript(launchScript, [
"-CodexConfigPath",
codexConfigPath,
"-StateRoot",
stateRoot,
"-ListenPort",
String(gatewayPort),
"-NoOpen",
]);
const installedConfig = await readFile(codexConfigPath, "utf8");
assert(
installedConfig.includes(`base_url = "${gatewayBaseUrl}"`),
"First launch did not redirect the current provider to the local gateway",
);
const uiResponse = await fetch(`${gatewayBaseUrl}/__codex_retry_gateway/ui`);
assert(uiResponse.status === 200, `UI page was not reachable after first launch: ${uiResponse.status}`);
const statusResponse = await fetch(`${gatewayBaseUrl}/__codex_retry_gateway/api/status`);
const statusPayload = await statusResponse.json();
assert(statusResponse.status === 200, `Status API failed after first launch: ${statusResponse.status}`);
assert(
statusPayload.state?.original_base_url === upstreamBaseUrl,
"First launch did not persist the original upstream base URL",
);
assert(
statusPayload.config?.reasoning_match_mode === "formula_518n_minus_2",
"First launch did not default reasoning_match_mode to formula_518n_minus_2",
);
const firstStateRaw = await readFile(path.join(stateRoot, "state.json"), "utf8");
const firstState = JSON.parse(firstStateRaw);
await runPowerShellScript(launchScript, [
"-CodexConfigPath",
codexConfigPath,
"-StateRoot",
stateRoot,
"-ListenPort",
String(gatewayPort),
"-NoOpen",
]);
const secondStateRaw = await readFile(path.join(stateRoot, "state.json"), "utf8");
const secondState = JSON.parse(secondStateRaw);
assert(
secondState.original_base_url === firstState.original_base_url,
"Second launch overwrote original_base_url unexpectedly",
);
assert(
secondState.gateway_base_url === gatewayBaseUrl,
"Second launch did not preserve gateway_base_url",
);
const proxiedModels = await fetch(`${gatewayBaseUrl}/v1/models`);
assert(proxiedModels.status === 200, `/v1/models through launch UI flow failed: ${proxiedModels.status}`);
assert(
proxiedModels.headers.get("x-upstream-test") === "launch-ui-ok",
"Gateway did not preserve upstream headers after second launch",
);
const blockedResponse = await fetch(`${gatewayBaseUrl}/responses`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ test_reasoning_tokens: 516 }),
});
assert(blockedResponse.status === 502, `Default 516 interception was not active: ${blockedResponse.status}`);
const blockedFormulaResponse = await fetch(`${gatewayBaseUrl}/responses`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ test_reasoning_tokens: 2070 }),
});
assert(blockedFormulaResponse.status === 502, `Default 2070 formula interception was not active: ${blockedFormulaResponse.status}`);
process.stdout.write("PASS launch-ui flow\n");
} finally {
try {
await runPowerShellScript(restoreScript, [
"-CodexConfigPath",
codexConfigPath,
"-StateRoot",
stateRoot,
]);
} catch {
// 测试清理阶段允许忽略恢复失败,避免覆盖主失败原因。
}
upstream.close();
await once(upstream, "close");
await rm(tempRoot, { recursive: true, force: true });
}
}
run().catch((error) => {
process.stderr.write(`${error?.stack || error}\n`);
process.exit(1);
});