mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
test: share openai codex oauth fixtures
This commit is contained in:
@@ -25,6 +25,13 @@ vi.mock("./provider-openai-codex-oauth-tls.js", () => ({
|
||||
|
||||
import { loginOpenAICodexOAuth } from "./provider-openai-codex-oauth.js";
|
||||
|
||||
const CODEX_AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize?state=abc";
|
||||
|
||||
type CodexLoginOptions = {
|
||||
onAuth: (event: { url: string }) => Promise<void>;
|
||||
onManualCodeInput?: () => Promise<string>;
|
||||
};
|
||||
|
||||
function createPrompter() {
|
||||
const spin = { update: vi.fn(), stop: vi.fn() };
|
||||
const prompter: Pick<WizardPrompter, "note" | "progress" | "text"> = {
|
||||
@@ -45,6 +52,22 @@ function createRuntime(): RuntimeEnv {
|
||||
};
|
||||
}
|
||||
|
||||
function createCodexCredentials(extra: Record<string, unknown> = {}) {
|
||||
return {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
async function startCodexAuth(opts: CodexLoginOptions) {
|
||||
await opts.onAuth({ url: CODEX_AUTHORIZE_URL });
|
||||
expect(opts.onManualCodeInput).toBeTypeOf("function");
|
||||
}
|
||||
|
||||
async function runCodexOAuth(params: {
|
||||
isRemote: boolean;
|
||||
openUrl?: (url: string) => Promise<void>;
|
||||
@@ -68,13 +91,7 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
});
|
||||
|
||||
it("returns credentials on successful oauth login", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockResolvedValue(creds);
|
||||
|
||||
const { result, spin, runtime } = await runCodexOAuth({ isRemote: false });
|
||||
@@ -89,13 +106,7 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
});
|
||||
|
||||
it("passes through Pi-provided authorize URLs without mutation", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: { onAuth: (event: { url: string }) => Promise<void> }) => {
|
||||
await opts.onAuth({
|
||||
@@ -117,18 +128,10 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
});
|
||||
|
||||
it("preserves authorize urls that omit scope", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: { onAuth: (event: { url: string }) => Promise<void> }) => {
|
||||
await opts.onAuth({
|
||||
url: "https://auth.openai.com/oauth/authorize?state=abc",
|
||||
});
|
||||
await opts.onAuth({ url: CODEX_AUTHORIZE_URL });
|
||||
return creds;
|
||||
},
|
||||
);
|
||||
@@ -136,17 +139,11 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
const openUrl = vi.fn(async () => {});
|
||||
await runCodexOAuth({ isRemote: false, openUrl });
|
||||
|
||||
expect(openUrl).toHaveBeenCalledWith("https://auth.openai.com/oauth/authorize?state=abc");
|
||||
expect(openUrl).toHaveBeenCalledWith(CODEX_AUTHORIZE_URL);
|
||||
});
|
||||
|
||||
it("preserves slash-terminated authorize paths too", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: { onAuth: (event: { url: string }) => Promise<void> }) => {
|
||||
await opts.onAuth({
|
||||
@@ -185,26 +182,12 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
});
|
||||
|
||||
it("passes manual code input hook for remote oauth flows", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: {
|
||||
onAuth: (event: { url: string }) => Promise<void>;
|
||||
onManualCodeInput?: () => Promise<string>;
|
||||
}) => {
|
||||
await opts.onAuth({
|
||||
url: "https://auth.openai.com/oauth/authorize?state=abc",
|
||||
});
|
||||
expect(opts.onManualCodeInput).toBeTypeOf("function");
|
||||
await expect(opts.onManualCodeInput?.()).resolves.toContain("code=test");
|
||||
return creds;
|
||||
},
|
||||
);
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockImplementation(async (opts: CodexLoginOptions) => {
|
||||
await startCodexAuth(opts);
|
||||
await expect(opts.onManualCodeInput?.()).resolves.toContain("code=test");
|
||||
return creds;
|
||||
});
|
||||
|
||||
const { result, prompter } = await runCodexOAuth({ isRemote: true });
|
||||
|
||||
@@ -219,32 +202,17 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
vi.useFakeTimers();
|
||||
const { prompter } = createPrompter();
|
||||
const runtime = createRuntime();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: {
|
||||
onAuth: (event: { url: string }) => Promise<void>;
|
||||
onManualCodeInput?: () => Promise<string>;
|
||||
}) => {
|
||||
await opts.onAuth({
|
||||
url: "https://auth.openai.com/oauth/authorize?state=abc",
|
||||
});
|
||||
expect(opts.onManualCodeInput).toBeTypeOf("function");
|
||||
const manualPromise = opts.onManualCodeInput?.();
|
||||
await vi.advanceTimersByTimeAsync(14_000);
|
||||
expect(manualPromise).toBeDefined();
|
||||
expect(prompter.text).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
expect(prompter.text).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
return {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
manualCode: await manualPromise,
|
||||
};
|
||||
},
|
||||
);
|
||||
mocks.loginOpenAICodex.mockImplementation(async (opts: CodexLoginOptions) => {
|
||||
await startCodexAuth(opts);
|
||||
const manualPromise = opts.onManualCodeInput?.();
|
||||
await vi.advanceTimersByTimeAsync(14_000);
|
||||
expect(manualPromise).toBeDefined();
|
||||
expect(prompter.text).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
expect(prompter.text).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
return createCodexCredentials({ manualCode: await manualPromise });
|
||||
});
|
||||
|
||||
await expect(
|
||||
loginOpenAICodexOAuth({
|
||||
@@ -270,25 +238,11 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
|
||||
it("clears the local manual fallback timer when browser callback settles first", async () => {
|
||||
vi.useFakeTimers();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: {
|
||||
onAuth: (event: { url: string }) => Promise<void>;
|
||||
onManualCodeInput?: () => Promise<string>;
|
||||
}) => {
|
||||
await opts.onAuth({
|
||||
url: "https://auth.openai.com/oauth/authorize?state=abc",
|
||||
});
|
||||
expect(opts.onManualCodeInput).toBeTypeOf("function");
|
||||
void opts.onManualCodeInput?.();
|
||||
return {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
);
|
||||
mocks.loginOpenAICodex.mockImplementation(async (opts: CodexLoginOptions) => {
|
||||
await startCodexAuth(opts);
|
||||
void opts.onManualCodeInput?.();
|
||||
return createCodexCredentials();
|
||||
});
|
||||
|
||||
await expect(runCodexOAuth({ isRemote: false })).resolves.toMatchObject({
|
||||
result: expect.objectContaining({
|
||||
@@ -302,13 +256,7 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
});
|
||||
|
||||
it("continues OAuth flow on non-certificate preflight failures", async () => {
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.runOpenAIOAuthTlsPreflight.mockResolvedValue({
|
||||
ok: false,
|
||||
kind: "network",
|
||||
@@ -332,13 +280,7 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
message: "unable to get local issuer certificate",
|
||||
});
|
||||
mocks.formatOpenAIOAuthTlsPreflightFix.mockReturnValue("Run brew postinstall openssl@3");
|
||||
const creds = {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
const creds = createCodexCredentials();
|
||||
mocks.loginOpenAICodex.mockResolvedValue(creds);
|
||||
|
||||
const { prompter } = createPrompter();
|
||||
@@ -368,14 +310,7 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
async (opts: { onManualCodeInput?: () => Promise<string> }) => {
|
||||
expect(opts.onManualCodeInput).toBeTypeOf("function");
|
||||
const manualCode = await opts.onManualCodeInput?.();
|
||||
return {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
manualCode,
|
||||
};
|
||||
return createCodexCredentials({ manualCode });
|
||||
},
|
||||
);
|
||||
|
||||
@@ -403,25 +338,12 @@ describe("loginOpenAICodexOAuth", () => {
|
||||
vi.useFakeTimers();
|
||||
const { prompter } = createPrompter();
|
||||
const runtime = createRuntime();
|
||||
mocks.loginOpenAICodex.mockImplementation(
|
||||
async (opts: {
|
||||
onAuth: (event: { url: string }) => Promise<void>;
|
||||
onManualCodeInput?: () => Promise<string>;
|
||||
}) => {
|
||||
await opts.onAuth({
|
||||
url: "https://auth.openai.com/oauth/authorize?state=abc",
|
||||
});
|
||||
void opts.onManualCodeInput?.();
|
||||
await vi.advanceTimersByTimeAsync(15_500);
|
||||
return {
|
||||
provider: "openai-codex" as const,
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
);
|
||||
mocks.loginOpenAICodex.mockImplementation(async (opts: CodexLoginOptions) => {
|
||||
await startCodexAuth(opts);
|
||||
void opts.onManualCodeInput?.();
|
||||
await vi.advanceTimersByTimeAsync(15_500);
|
||||
return createCodexCredentials();
|
||||
});
|
||||
|
||||
await expect(
|
||||
loginOpenAICodexOAuth({
|
||||
|
||||
Reference in New Issue
Block a user