105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
// Public OAuth client_id/secret defaults for Gemini, Antigravity and Windsurf
|
|
// are resolved at module load through open-sse/utils/publicCreds.ts — no need
|
|
// to populate them here. Only the providers without a baked-in default need
|
|
// explicit env setup for this suite.
|
|
Object.assign(process.env, {
|
|
CLAUDE_OAUTH_CLIENT_ID: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
CODEX_OAUTH_CLIENT_ID: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
QWEN_OAUTH_CLIENT_ID: "f0304373b74a44d2b584a3fb70ca9e56",
|
|
KIMI_CODING_OAUTH_CLIENT_ID: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
|
GITHUB_OAUTH_CLIENT_ID: "Iv1.b507a08c87ecfe98",
|
|
});
|
|
|
|
const { claude } = await import("../../src/lib/oauth/providers/claude.ts");
|
|
const { CLAUDE_CONFIG } = await import("../../src/lib/oauth/constants/oauth.ts");
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
test.afterEach(() => {
|
|
globalThis.fetch = originalFetch;
|
|
});
|
|
|
|
test("Claude OAuth provider always uses the configured redirectUri when building the auth URL", () => {
|
|
const runtimeRedirectUri = "http://localhost:43121/callback";
|
|
const authUrl = claude.buildAuthUrl(
|
|
CLAUDE_CONFIG,
|
|
runtimeRedirectUri,
|
|
"state-123",
|
|
"challenge-456"
|
|
);
|
|
const parsed = new URL(authUrl);
|
|
|
|
assert.equal(parsed.searchParams.get("redirect_uri"), CLAUDE_CONFIG.redirectUri);
|
|
assert.equal(parsed.searchParams.get("state"), "state-123");
|
|
assert.equal(parsed.searchParams.get("code_challenge"), "challenge-456");
|
|
});
|
|
|
|
test("Claude OAuth provider always uses the configured redirectUri during token exchange", async () => {
|
|
let captured = null;
|
|
|
|
globalThis.fetch = async (url, init = {}) => {
|
|
captured = {
|
|
url: String(url),
|
|
method: init.method,
|
|
headers: init.headers,
|
|
body: JSON.parse(String(init.body)),
|
|
};
|
|
|
|
return new Response(JSON.stringify({ access_token: "token-1" }), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
};
|
|
|
|
const runtimeRedirectUri = "http://localhost:43121/callback";
|
|
await claude.exchangeToken(
|
|
CLAUDE_CONFIG,
|
|
"auth-code#state-from-fragment",
|
|
runtimeRedirectUri,
|
|
"verifier-123",
|
|
"state-from-request"
|
|
);
|
|
|
|
assert.equal(captured.url, CLAUDE_CONFIG.tokenUrl);
|
|
assert.equal(captured.method, "POST");
|
|
assert.equal(captured.body.redirect_uri, CLAUDE_CONFIG.redirectUri);
|
|
assert.equal(captured.body.code, "auth-code");
|
|
assert.equal(captured.body.state, "state-from-fragment");
|
|
assert.equal(captured.body.code_verifier, "verifier-123");
|
|
});
|
|
|
|
test("Claude OAuth token mapper persists the first non-empty token plan field", () => {
|
|
const cases = [
|
|
[{ account_tier: " Pro ", plan: "Max" }, "Pro"],
|
|
[{ account_tier: "", plan: "Max" }, "Max"],
|
|
[{ plan: "", subscription_type: "Team" }, "Team"],
|
|
[{ subscription_type: "", billing: { plan: "Enterprise" } }, "Enterprise"],
|
|
];
|
|
|
|
for (const [tokens, expected] of cases) {
|
|
const mapped = claude.mapTokens({ access_token: "token-1", ...tokens });
|
|
|
|
assert.equal(mapped.providerSpecificData.plan, expected);
|
|
}
|
|
});
|
|
|
|
test("Claude OAuth token mapper reads plan fields from userinfo extras after token fields", () => {
|
|
const mapped = claude.mapTokens(
|
|
{ access_token: "token-1" },
|
|
{ userInfo: { account_tier: "", subscription_type: "Max" } }
|
|
);
|
|
|
|
assert.equal(mapped.providerSpecificData.plan, "Max");
|
|
});
|
|
|
|
test("Claude OAuth token mapper leaves providerSpecificData.plan undefined without plan fields", () => {
|
|
const mapped = claude.mapTokens({ access_token: "token-1", scope: "user:profile" });
|
|
|
|
assert.ok(mapped.providerSpecificData);
|
|
assert.ok(typeof mapped.providerSpecificData.cliUserID === "string");
|
|
assert.equal(mapped.providerSpecificData.plan, undefined);
|
|
});
|