fix(qa): reject loose mock OpenAI ports

This commit is contained in:
Vincent Koc
2026-06-16 20:37:36 +02:00
parent b9e193ce22
commit ae99ce729a
7 changed files with 88 additions and 8 deletions
@@ -1,5 +1,26 @@
// Mock OpenAI model config helpers for E2E fixture generation.
function formatMockPortValue(value) {
return value === undefined ? "<missing>" : JSON.stringify(String(value));
}
export function parseMockOpenAiPort(value, label = "mock OpenAI port") {
const text = String(value ?? "").trim();
if (!/^[1-9]\d*$/u.test(text)) {
throw new Error(
`${label} must be a TCP port from 1 to 65535. Got: ${formatMockPortValue(value)}`,
);
}
const port = Number(text);
if (!Number.isSafeInteger(port) || port > 65535) {
throw new Error(
`${label} must be a TCP port from 1 to 65535. Got: ${formatMockPortValue(value)}`,
);
}
return port;
}
export function applyMockOpenAiModelConfig(cfg, params) {
const mockPort = parseMockOpenAiPort(params.mockPort);
const modelRef = params.modelRef ?? "openai/gpt-5.5";
const modelId = modelRef.split("/").at(-1) ?? "gpt-5.5";
const cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
@@ -10,7 +31,7 @@ export function applyMockOpenAiModelConfig(cfg, params) {
...cfg.models?.providers,
openai: {
...cfg.models?.providers?.openai,
baseUrl: `http://127.0.0.1:${params.mockPort}/v1`,
baseUrl: `http://127.0.0.1:${mockPort}/v1`,
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
api: "openai-responses",
agentRuntime: { id: "openclaw" },
@@ -8,7 +8,10 @@ import {
} from "../agent-turn-output.mjs";
import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs";
import { readPositiveIntEnv } from "../env-limits.mjs";
import { applyMockOpenAiModelConfig } from "../fixtures/mock-openai-config.mjs";
import {
applyMockOpenAiModelConfig,
parseMockOpenAiPort,
} from "../fixtures/mock-openai-config.mjs";
import { readTextFileBounded, readTextFileTail } from "../text-file-utils.mjs";
const command = process.argv[2];
@@ -117,7 +120,7 @@ function assertOnboardState() {
}
function configureMockModel() {
const mockPort = Number(process.argv[3]);
const mockPort = parseMockOpenAiPort(process.argv[3]);
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
const cfg = readJson(configPath);
applyMockOpenAiModelConfig(cfg, { mockPort });
@@ -125,7 +128,7 @@ function configureMockModel() {
}
function assertMockModelConfig() {
const mockPort = Number(process.argv[3]);
const mockPort = parseMockOpenAiPort(process.argv[3]);
const expectedModelRef = "openai/gpt-5.5";
const expectedBaseUrl = `http://127.0.0.1:${mockPort}/v1`;
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
@@ -7,7 +7,10 @@ import {
assertOpenAiRequestLogUsed,
} from "../agent-turn-output.mjs";
import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs";
import { applyMockOpenAiModelConfig } from "../fixtures/mock-openai-config.mjs";
import {
applyMockOpenAiModelConfig,
parseMockOpenAiPort,
} from "../fixtures/mock-openai-config.mjs";
import { readPluginInstallRecords } from "../plugin-index-sqlite.mjs";
import { readTextFileTail } from "../text-file-utils.mjs";
@@ -145,7 +148,7 @@ function readStateText() {
}
function configureMockOpenAi() {
const mockPort = Number(process.argv[3]);
const mockPort = parseMockOpenAiPort(process.argv[3]);
const cfg = readJson(configPath());
applyMockOpenAiModelConfig(cfg, { mockPort, includeImageDefaults: true });
writeConfig(cfg);
@@ -7,7 +7,10 @@ import {
assertOpenAiRequestLogUsed,
} from "../agent-turn-output.mjs";
import { readBoundedResponseText as readBoundedResponseTextWithLimit } from "../bounded-response-text.mjs";
import { applyMockOpenAiModelConfig } from "../fixtures/mock-openai-config.mjs";
import {
applyMockOpenAiModelConfig,
parseMockOpenAiPort,
} from "../fixtures/mock-openai-config.mjs";
import { readPluginInstallRecords } from "../plugin-index-sqlite.mjs";
import { readTextFileTail } from "../text-file-utils.mjs";
@@ -192,7 +195,7 @@ function assertOnboard() {
}
function configureMockModel() {
const mockPort = Number(process.argv[3]);
const mockPort = parseMockOpenAiPort(process.argv[3]);
const cfg = readJson(configPath());
applyMockOpenAiModelConfig(cfg, { mockPort });
writeConfig(cfg);
@@ -84,6 +84,17 @@ function runOnboardAssert(home: string) {
});
}
function runMockModelAssert(home: string, command: string, port: string) {
return spawnSync(process.execPath, [assertionsPath, command, port], {
encoding: "utf8",
env: {
...process.env,
HOME: home,
NODE_OPTIONS: nodeOptionsWithoutExperimentalWarnings(),
},
});
}
function runStatusAssert(
channel: string,
channelsStatus: unknown,
@@ -110,6 +121,22 @@ function runStatusAssert(
}
describe("npm onboard channel agent assertions", () => {
it("rejects loose mock OpenAI port args", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
try {
for (const command of ["configure-mock-model", "assert-mock-model-config"]) {
const result = runMockModelAssert(tempDir, command, "1e3");
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("mock OpenAI port must be a TCP port from 1 to 65535");
expect(result.stderr).toContain('"1e3"');
}
} finally {
fs.rmSync(tempDir, { force: true, recursive: true });
}
});
it("validates OpenAI env refs from the SQLite auth profile store", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
const agentDir = path.join(tempDir, ".openclaw", "agents", "main", "agent");
@@ -55,6 +55,14 @@ function writeAuthProfileStoreSqlite(agentDir: string, store: unknown) {
}
describe("release scenario assertions", () => {
it("rejects loose mock OpenAI port args", () => {
const result = runAssertion(["configure-mock-openai", "1e3"]);
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("mock OpenAI port must be a TCP port from 1 to 65535");
expect(result.stderr).toContain('"1e3"');
});
it("scans large files when checking release scenario output text", () => {
const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-scenarios-"));
const outputPath = path.join(root, "output.log");
@@ -88,6 +88,21 @@ async function startTcpFixtureServer(handler: (socket: Socket) => void): Promise
}
describe("release user journey assertions", () => {
it("rejects loose mock OpenAI port args", () => {
const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-"));
const home = path.join(root, "home");
try {
const result = runAssertion(home, ["configure-mock-model", "1e3"]);
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("mock OpenAI port must be a TCP port from 1 to 65535");
expect(result.stderr).toContain('"1e3"');
} finally {
rmSync(root, { force: true, recursive: true });
}
});
it("scans large files when checking release user journey output text", () => {
const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-user-assertions-"));
const home = path.join(root, "home");