chore: initialize qiming workspace repository
This commit is contained in:
178
qimingcode/packages/opencode/test/mcp/headers.test.ts
Normal file
178
qimingcode/packages/opencode/test/mcp/headers.test.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { test, expect, mock, beforeEach } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import type { MCP as MCPNS } from "../../src/mcp/index"
|
||||
|
||||
// Track what options were passed to each transport constructor
|
||||
const transportCalls: Array<{
|
||||
type: "streamable" | "sse"
|
||||
url: string
|
||||
options: { authProvider?: unknown; requestInit?: RequestInit }
|
||||
}> = []
|
||||
|
||||
// Mock the transport constructors to capture their arguments
|
||||
void mock.module("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
|
||||
StreamableHTTPClientTransport: class MockStreamableHTTP {
|
||||
constructor(url: URL, options?: { authProvider?: unknown; requestInit?: RequestInit }) {
|
||||
transportCalls.push({
|
||||
type: "streamable",
|
||||
url: url.toString(),
|
||||
options: options ?? {},
|
||||
})
|
||||
}
|
||||
async start() {
|
||||
throw new Error("Mock transport cannot connect")
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
void mock.module("@modelcontextprotocol/sdk/client/sse.js", () => ({
|
||||
SSEClientTransport: class MockSSE {
|
||||
constructor(url: URL, options?: { authProvider?: unknown; requestInit?: RequestInit }) {
|
||||
transportCalls.push({
|
||||
type: "sse",
|
||||
url: url.toString(),
|
||||
options: options ?? {},
|
||||
})
|
||||
}
|
||||
async start() {
|
||||
throw new Error("Mock transport cannot connect")
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
transportCalls.length = 0
|
||||
})
|
||||
|
||||
// Import MCP after mocking
|
||||
const { MCP } = await import("../../src/mcp/index")
|
||||
const { AppRuntime } = await import("../../src/effect/app-runtime")
|
||||
const { Instance } = await import("../../src/project/instance")
|
||||
const { tmpdir } = await import("../fixture/fixture")
|
||||
const service = MCP.Service as unknown as Effect.Effect<MCPNS.Interface, never, never>
|
||||
|
||||
test("headers are passed to transports when oauth is enabled (default)", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(
|
||||
`${dir}/opencode.json`,
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
"test-server": {
|
||||
type: "remote",
|
||||
url: "https://example.com/mcp",
|
||||
headers: {
|
||||
Authorization: "Bearer test-token",
|
||||
"X-Custom-Header": "custom-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
// Trigger MCP initialization - it will fail to connect but we can check the transport options
|
||||
await AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const mcp = yield* service
|
||||
yield* mcp
|
||||
.add("test-server", {
|
||||
type: "remote",
|
||||
url: "https://example.com/mcp",
|
||||
headers: {
|
||||
Authorization: "Bearer test-token",
|
||||
"X-Custom-Header": "custom-value",
|
||||
},
|
||||
})
|
||||
.pipe(Effect.catch(() => Effect.void))
|
||||
}),
|
||||
)
|
||||
|
||||
// Both transports should have been created with headers
|
||||
expect(transportCalls.length).toBeGreaterThanOrEqual(1)
|
||||
|
||||
for (const call of transportCalls) {
|
||||
expect(call.options.requestInit).toBeDefined()
|
||||
expect(call.options.requestInit?.headers).toEqual({
|
||||
Authorization: "Bearer test-token",
|
||||
"X-Custom-Header": "custom-value",
|
||||
})
|
||||
// OAuth should be enabled by default, so authProvider should exist
|
||||
expect(call.options.authProvider).toBeDefined()
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("headers are passed to transports when oauth is explicitly disabled", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
transportCalls.length = 0
|
||||
|
||||
await AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const mcp = yield* service
|
||||
yield* mcp
|
||||
.add("test-server-no-oauth", {
|
||||
type: "remote",
|
||||
url: "https://example.com/mcp",
|
||||
oauth: false,
|
||||
headers: {
|
||||
Authorization: "Bearer test-token",
|
||||
},
|
||||
})
|
||||
.pipe(Effect.catch(() => Effect.void))
|
||||
}),
|
||||
)
|
||||
|
||||
expect(transportCalls.length).toBeGreaterThanOrEqual(1)
|
||||
|
||||
for (const call of transportCalls) {
|
||||
expect(call.options.requestInit).toBeDefined()
|
||||
expect(call.options.requestInit?.headers).toEqual({
|
||||
Authorization: "Bearer test-token",
|
||||
})
|
||||
// OAuth is disabled, so no authProvider
|
||||
expect(call.options.authProvider).toBeUndefined()
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("no requestInit when headers are not provided", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
transportCalls.length = 0
|
||||
|
||||
await AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const mcp = yield* service
|
||||
yield* mcp
|
||||
.add("test-server-no-headers", {
|
||||
type: "remote",
|
||||
url: "https://example.com/mcp",
|
||||
})
|
||||
.pipe(Effect.catch(() => Effect.void))
|
||||
}),
|
||||
)
|
||||
|
||||
expect(transportCalls.length).toBeGreaterThanOrEqual(1)
|
||||
|
||||
for (const call of transportCalls) {
|
||||
// No headers means requestInit should be undefined
|
||||
expect(call.options.requestInit).toBeUndefined()
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user