Skip to content

Commit e1a9817

Browse files
committed
fix(e2e): preflight openai chat tools auth
1 parent 4dad7bd commit e1a9817

2 files changed

Lines changed: 92 additions & 2 deletions

File tree

scripts/e2e/openai-chat-tools-docker.sh

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,39 @@ if [ ! -f "$PROFILE_FILE" ] && [ -f "$HOME/.profile" ]; then
1313
PROFILE_FILE="$HOME/.profile"
1414
fi
1515

16+
read_profile_openai_api_key() {
17+
local profile_file="$1"
18+
(
19+
set +u
20+
set -a
21+
# shellcheck disable=SC1090
22+
source "$profile_file" >/dev/null
23+
set +a
24+
printf '%s' "${OPENAI_API_KEY:-}"
25+
)
26+
}
27+
28+
PROFILE_STATUS="none"
29+
if [ -f "$PROFILE_FILE" ] && [ -r "$PROFILE_FILE" ]; then
30+
PROFILE_STATUS="$PROFILE_FILE"
31+
fi
32+
33+
OPENAI_API_KEY_VALUE="${OPENAI_API_KEY:-}"
34+
if [ "$PROFILE_STATUS" != "none" ]; then
35+
OPENAI_API_KEY_VALUE="$(read_profile_openai_api_key "$PROFILE_FILE")"
36+
fi
37+
if [[ "$OPENAI_API_KEY_VALUE" == "undefined" || "$OPENAI_API_KEY_VALUE" == "null" ]]; then
38+
OPENAI_API_KEY_VALUE=""
39+
fi
40+
if [ -z "$OPENAI_API_KEY_VALUE" ]; then
41+
echo "ERROR: OPENAI_API_KEY was not available after sourcing $PROFILE_STATUS." >&2
42+
exit 1
43+
fi
44+
1645
docker_e2e_build_or_reuse "$IMAGE_NAME" openai-chat-tools "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD"
1746
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 openai-chat-tools empty)"
1847

1948
PROFILE_MOUNT=()
20-
PROFILE_STATUS="none"
2149
if [ -f "$PROFILE_FILE" ] && [ -r "$PROFILE_FILE" ]; then
2250
set -a
2351
# shellcheck disable=SC1090

test/scripts/openai-chat-tools-client.test.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { spawn, spawnSync } from "node:child_process";
2-
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
2+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
33
import { createServer, type Server } from "node:http";
44
import { tmpdir } from "node:os";
55
import path from "node:path";
66
import { describe, expect, it } from "vitest";
77

88
const clientPath = path.resolve("scripts/e2e/lib/openai-chat-tools/client.mjs");
9+
const dockerRunnerPath = path.resolve("scripts/e2e/openai-chat-tools-docker.sh");
910
const writeConfigPath = path.resolve("scripts/e2e/lib/openai-chat-tools/write-config.mjs");
1011

1112
interface ClientResult {
@@ -96,6 +97,20 @@ function runWriteConfig(root: string, env: Record<string, string> = {}) {
9697
});
9798
}
9899

100+
function runDockerRunnerAuthPreflight(root: string, env: Record<string, string> = {}) {
101+
return spawnSync("bash", [dockerRunnerPath], {
102+
encoding: "utf8",
103+
env: {
104+
...process.env,
105+
HOME: root,
106+
OPENAI_API_KEY: "",
107+
OPENAI_BASE_URL: "",
108+
OPENCLAW_OPENAI_CHAT_TOOLS_PROFILE_FILE: path.join(root, "missing.profile"),
109+
...env,
110+
},
111+
});
112+
}
113+
99114
function toolCallResponse() {
100115
return {
101116
choices: [
@@ -118,6 +133,53 @@ function toolCallResponse() {
118133
}
119134

120135
describe("scripts/e2e/lib/openai-chat-tools/client.mjs", () => {
136+
it("keeps full profile exports out of the Docker build phase", () => {
137+
const runner = readFileSync(dockerRunnerPath, "utf8");
138+
const preflightSourceIndex = runner.indexOf('source "$profile_file"');
139+
const buildIndex = runner.indexOf("docker_e2e_build_or_reuse");
140+
const fullProfileSourceIndex = runner.indexOf('source "$PROFILE_FILE"', buildIndex);
141+
142+
expect(preflightSourceIndex).toBeGreaterThanOrEqual(0);
143+
expect(buildIndex).toBeGreaterThan(preflightSourceIndex);
144+
expect(fullProfileSourceIndex).toBeGreaterThan(buildIndex);
145+
});
146+
147+
it("fails auth preflight before Docker build work starts", () => {
148+
const root = mkdtempSync(path.join(tmpdir(), "openclaw-openai-chat-tools-"));
149+
try {
150+
const result = runDockerRunnerAuthPreflight(root);
151+
const output = `${result.stdout}\n${result.stderr}`;
152+
153+
expect(result.status).toBe(1);
154+
expect(output).toContain("OPENAI_API_KEY was not available");
155+
expect(output).not.toContain("Building Docker image:");
156+
expect(output).not.toContain("Reusing Docker image:");
157+
expect(output).not.toContain("Running OpenAI Chat Completions tools Docker E2E");
158+
} finally {
159+
rmSync(root, { force: true, recursive: true });
160+
}
161+
});
162+
163+
it("treats placeholder profile auth as missing before Docker build work starts", () => {
164+
const root = mkdtempSync(path.join(tmpdir(), "openclaw-openai-chat-tools-"));
165+
try {
166+
const profile = path.join(root, "profile");
167+
writeFileSync(profile, "OPENAI_API_KEY=undefined\n");
168+
const result = runDockerRunnerAuthPreflight(root, {
169+
OPENCLAW_OPENAI_CHAT_TOOLS_PROFILE_FILE: profile,
170+
});
171+
const output = `${result.stdout}\n${result.stderr}`;
172+
173+
expect(result.status).toBe(1);
174+
expect(output).toContain("OPENAI_API_KEY was not available");
175+
expect(output).not.toContain("Building Docker image:");
176+
expect(output).not.toContain("Reusing Docker image:");
177+
expect(output).not.toContain("Running OpenAI Chat Completions tools Docker E2E");
178+
} finally {
179+
rmSync(root, { force: true, recursive: true });
180+
}
181+
});
182+
121183
it("rejects loose timeout env values instead of parsing numeric prefixes", async () => {
122184
const result = await runClient(1, {
123185
OPENCLAW_OPENAI_CHAT_TOOLS_TIMEOUT_SECONDS: "1e3",

0 commit comments

Comments
 (0)