test(auth): add codex oauth red-blue coverage

This commit is contained in:
Vincent Koc
2026-04-17 14:04:50 -07:00
parent 1e7c7dd02f
commit 5edf876a5e
20 changed files with 606 additions and 502 deletions

View File

@@ -96,6 +96,49 @@ describe("readOpenAICodexCliOAuthProfile", () => {
expect(parsed).toBeNull();
});
it("allows the runtime-only Codex CLI profile when the stored default already matches", () => {
const accessToken = buildJwt({
exp: Math.floor(Date.now() / 1000) + 600,
"https://api.openai.com/profile": {
email: "codex@example.com",
},
});
vi.spyOn(fs, "readFileSync").mockReturnValue(
JSON.stringify({
auth_mode: "chatgpt",
tokens: {
access_token: accessToken,
refresh_token: "refresh-token",
account_id: "acct_123",
},
}),
);
const firstParse = readOpenAICodexCliOAuthProfile({
store: { version: 1, profiles: {} },
});
expect(firstParse).not.toBeNull();
const parsed = readOpenAICodexCliOAuthProfile({
store: {
version: 1,
profiles: {
[OPENAI_CODEX_DEFAULT_PROFILE_ID]: firstParse!.credential,
},
},
});
expect(parsed).toMatchObject({
profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID,
credential: {
access: accessToken,
refresh: "refresh-token",
accountId: "acct_123",
email: "codex@example.com",
},
});
});
it("returns null without logging when the Codex CLI auth file is missing", () => {
const error = Object.assign(new Error("missing"), {
code: "ENOENT",

View File

@@ -67,7 +67,6 @@ function oauthCredentialMatches(a: OAuthCredential, b: OAuthCredential): boolean
a.provider === b.provider &&
a.access === b.access &&
a.refresh === b.refresh &&
a.expires === b.expires &&
a.clientId === b.clientId &&
a.email === b.email &&
a.displayName === b.displayName &&

View File

@@ -268,38 +268,41 @@ describe("buildQaRuntimeEnv", () => {
expect(env.CODEX_HOME).toBe("/custom/codex-home");
});
it("scrubs direct and live provider keys in mock mode", () => {
const env = buildQaRuntimeEnv({
...createParams({
ANTHROPIC_API_KEY: "anthropic-live",
ANTHROPIC_OAUTH_TOKEN: "anthropic-oauth",
GEMINI_API_KEY: "gemini-live",
GEMINI_API_KEYS: "gemini-a gemini-b",
GOOGLE_API_KEY: "google-live",
OPENAI_API_KEY: "openai-live",
OPENAI_API_KEYS: "openai-a,openai-b",
CODEX_HOME: "/host/.codex",
OPENCLAW_LIVE_ANTHROPIC_KEY: "anthropic-live",
OPENCLAW_LIVE_ANTHROPIC_KEYS: "anthropic-a,anthropic-b",
OPENCLAW_LIVE_GEMINI_KEY: "gemini-live",
OPENCLAW_LIVE_OPENAI_KEY: "openai-live",
}),
providerMode: "mock-openai",
});
it.each(["mock-openai", "aimock"] as const)(
"scrubs direct and live provider keys in %s mode",
(providerMode) => {
const env = buildQaRuntimeEnv({
...createParams({
ANTHROPIC_API_KEY: "anthropic-live",
ANTHROPIC_OAUTH_TOKEN: "anthropic-oauth",
GEMINI_API_KEY: "gemini-live",
GEMINI_API_KEYS: "gemini-a gemini-b",
GOOGLE_API_KEY: "google-live",
OPENAI_API_KEY: "openai-live",
OPENAI_API_KEYS: "openai-a,openai-b",
CODEX_HOME: "/host/.codex",
OPENCLAW_LIVE_ANTHROPIC_KEY: "anthropic-live",
OPENCLAW_LIVE_ANTHROPIC_KEYS: "anthropic-a,anthropic-b",
OPENCLAW_LIVE_GEMINI_KEY: "gemini-live",
OPENCLAW_LIVE_OPENAI_KEY: "openai-live",
}),
providerMode,
});
expect(env.OPENAI_API_KEY).toBeUndefined();
expect(env.OPENAI_API_KEYS).toBeUndefined();
expect(env.CODEX_HOME).toBeUndefined();
expect(env.ANTHROPIC_API_KEY).toBeUndefined();
expect(env.ANTHROPIC_OAUTH_TOKEN).toBeUndefined();
expect(env.GEMINI_API_KEY).toBeUndefined();
expect(env.GEMINI_API_KEYS).toBeUndefined();
expect(env.GOOGLE_API_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_OPENAI_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_ANTHROPIC_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_ANTHROPIC_KEYS).toBeUndefined();
expect(env.OPENCLAW_LIVE_GEMINI_KEY).toBeUndefined();
});
expect(env.OPENAI_API_KEY).toBeUndefined();
expect(env.OPENAI_API_KEYS).toBeUndefined();
expect(env.CODEX_HOME).toBeUndefined();
expect(env.ANTHROPIC_API_KEY).toBeUndefined();
expect(env.ANTHROPIC_OAUTH_TOKEN).toBeUndefined();
expect(env.GEMINI_API_KEY).toBeUndefined();
expect(env.GEMINI_API_KEYS).toBeUndefined();
expect(env.GOOGLE_API_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_OPENAI_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_ANTHROPIC_KEY).toBeUndefined();
expect(env.OPENCLAW_LIVE_ANTHROPIC_KEYS).toBeUndefined();
expect(env.OPENCLAW_LIVE_GEMINI_KEY).toBeUndefined();
},
);
it("treats restart socket closures as retryable gateway call errors", () => {
expect(__testing.isRetryableGatewayCallError("gateway closed (1006 abnormal closure)")).toBe(

View File

@@ -79,4 +79,32 @@ describe("qa aimock server", () => {
await server.stop();
}
});
it("treats OpenAI Codex model refs as OpenAI-compatible snapshots", async () => {
const server = await startQaAimockServer({
host: "127.0.0.1",
port: 0,
});
try {
const response = await fetch(`${server.baseUrl}/v1/responses`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
model: "openai-codex/gpt-5.4",
stream: false,
input: [makeResponsesInput("hello codex-compatible aimock")],
}),
});
expect(response.status).toBe(200);
const debug = await fetch(`${server.baseUrl}/debug/last-request`);
expect(debug.status).toBe(200);
expect(await debug.json()).toMatchObject({
model: "openai-codex/gpt-5.4",
providerVariant: "openai",
});
} finally {
await server.stop();
}
});
});