From bca26b009441e951035622977eae3c9a835cdaa9 Mon Sep 17 00:00:00 2001 From: Sebin Thomas Date: Fri, 22 May 2026 12:08:51 +0200 Subject: [PATCH] test(mcp): add McpOAuthProvider unit tests for scope and callbackPort Covers: - redirectUrl uses callbackPort when set, falls back to default 19876 - redirectUri takes precedence over callbackPort when both are set - clientMetadata includes scope when set in config - clientMetadata omits scope when not set (backward compat) - token_endpoint_auth_method based on clientSecret presence Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../opencode/test/mcp/oauth-provider.test.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 packages/opencode/test/mcp/oauth-provider.test.ts diff --git a/packages/opencode/test/mcp/oauth-provider.test.ts b/packages/opencode/test/mcp/oauth-provider.test.ts new file mode 100644 index 0000000000..64c2cb6687 --- /dev/null +++ b/packages/opencode/test/mcp/oauth-provider.test.ts @@ -0,0 +1,61 @@ +import { test, expect, describe } from "bun:test" +import { McpOAuthProvider, OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH } from "../../src/mcp/oauth-provider" +import type { McpAuth } from "../../src/mcp/auth" + +// Stub auth — only synchronous getters are exercised in these tests +const stubAuth = {} as McpAuth.Interface + +const makeProvider = (config: ConstructorParameters[2]) => + new McpOAuthProvider("test-server", "https://mcp.example.com/mcp", config, { onRedirect: async () => {} }, stubAuth) + +describe("McpOAuthProvider.redirectUrl", () => { + test("defaults to 127.0.0.1:19876/mcp/oauth/callback", () => { + const provider = makeProvider({}) + expect(provider.redirectUrl).toBe(`http://127.0.0.1:${OAUTH_CALLBACK_PORT}${OAUTH_CALLBACK_PATH}`) + }) + + test("uses callbackPort when set", () => { + const provider = makeProvider({ callbackPort: 6620 }) + expect(provider.redirectUrl).toBe(`http://127.0.0.1:6620${OAUTH_CALLBACK_PATH}`) + }) + + test("redirectUri takes precedence over callbackPort", () => { + const provider = makeProvider({ + callbackPort: 6620, + redirectUri: "http://127.0.0.1:9999/custom/callback", + }) + expect(provider.redirectUrl).toBe("http://127.0.0.1:9999/custom/callback") + }) + + test("uses explicit redirectUri when set without callbackPort", () => { + const provider = makeProvider({ redirectUri: "http://127.0.0.1:8080/oauth/callback" }) + expect(provider.redirectUrl).toBe("http://127.0.0.1:8080/oauth/callback") + }) +}) + +describe("McpOAuthProvider.clientMetadata", () => { + test("includes redirect_uris from redirectUrl", () => { + const provider = makeProvider({ callbackPort: 6620 }) + expect(provider.clientMetadata.redirect_uris).toEqual([`http://127.0.0.1:6620${OAUTH_CALLBACK_PATH}`]) + }) + + test("includes scope when set in config", () => { + const provider = makeProvider({ scope: "openid offline_access" }) + expect(provider.clientMetadata.scope).toBe("openid offline_access") + }) + + test("omits scope when not set in config", () => { + const provider = makeProvider({}) + expect(provider.clientMetadata.scope).toBeUndefined() + }) + + test("sets token_endpoint_auth_method to client_secret_post when clientSecret provided", () => { + const provider = makeProvider({ clientSecret: "secret" }) + expect(provider.clientMetadata.token_endpoint_auth_method).toBe("client_secret_post") + }) + + test("sets token_endpoint_auth_method to none when no clientSecret", () => { + const provider = makeProvider({}) + expect(provider.clientMetadata.token_endpoint_auth_method).toBe("none") + }) +})