Skip to content

Commit 17d5819

Browse files
dsp-antclaude
andcommitted
test: Add tests for RequestInit passthrough to auth requests
Added comprehensive tests to verify that: - Custom headers (like user-agent) from RequestInit are passed to auth requests - Auth-specific headers override base headers when needed - All RequestInit options (credentials, mode, cache, etc.) are preserved All 553 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9fd1466 commit 17d5819

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed

src/client/auth.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,4 +2476,113 @@ describe('OAuth Authorization', () => {
24762476
expect(body.get('refresh_token')).toBe('refresh123');
24772477
});
24782478
});
2479+
2480+
describe('RequestInit headers passthrough', () => {
2481+
it('custom headers from RequestInit are passed to auth discovery requests', async () => {
2482+
const { createFetchWithInit } = await import('../shared/transport.js');
2483+
2484+
const customFetch = jest.fn().mockResolvedValue({
2485+
ok: true,
2486+
status: 200,
2487+
json: async () => ({
2488+
resource: 'https://resource.example.com',
2489+
authorization_servers: ['https://auth.example.com']
2490+
})
2491+
});
2492+
2493+
// Create a wrapped fetch with custom headers
2494+
const wrappedFetch = createFetchWithInit(customFetch, {
2495+
headers: {
2496+
'user-agent': 'MyApp/1.0',
2497+
'x-custom-header': 'test-value'
2498+
}
2499+
});
2500+
2501+
await discoverOAuthProtectedResourceMetadata('https://resource.example.com', undefined, wrappedFetch);
2502+
2503+
expect(customFetch).toHaveBeenCalledTimes(1);
2504+
const [url, options] = customFetch.mock.calls[0];
2505+
2506+
expect(url.toString()).toBe('https://resource.example.com/.well-known/oauth-protected-resource');
2507+
expect(options.headers).toMatchObject({
2508+
'user-agent': 'MyApp/1.0',
2509+
'x-custom-header': 'test-value',
2510+
'MCP-Protocol-Version': LATEST_PROTOCOL_VERSION
2511+
});
2512+
});
2513+
2514+
it('auth-specific headers override base headers from RequestInit', async () => {
2515+
const { createFetchWithInit } = await import('../shared/transport.js');
2516+
2517+
const customFetch = jest.fn().mockResolvedValue({
2518+
ok: true,
2519+
status: 200,
2520+
json: async () => ({
2521+
issuer: 'https://auth.example.com',
2522+
authorization_endpoint: 'https://auth.example.com/authorize',
2523+
token_endpoint: 'https://auth.example.com/token',
2524+
response_types_supported: ['code'],
2525+
code_challenge_methods_supported: ['S256']
2526+
})
2527+
});
2528+
2529+
// Create a wrapped fetch with a custom Accept header
2530+
const wrappedFetch = createFetchWithInit(customFetch, {
2531+
headers: {
2532+
'Accept': 'text/plain',
2533+
'user-agent': 'MyApp/1.0'
2534+
}
2535+
});
2536+
2537+
await discoverAuthorizationServerMetadata('https://auth.example.com', {
2538+
fetchFn: wrappedFetch
2539+
});
2540+
2541+
expect(customFetch).toHaveBeenCalled();
2542+
const [url, options] = customFetch.mock.calls[0];
2543+
2544+
// Auth-specific Accept header should override base Accept header
2545+
expect(options.headers).toMatchObject({
2546+
'Accept': 'application/json', // Auth-specific value wins
2547+
'user-agent': 'MyApp/1.0', // Base value preserved
2548+
'MCP-Protocol-Version': LATEST_PROTOCOL_VERSION
2549+
});
2550+
});
2551+
2552+
it('other RequestInit options are passed through', async () => {
2553+
const { createFetchWithInit } = await import('../shared/transport.js');
2554+
2555+
const customFetch = jest.fn().mockResolvedValue({
2556+
ok: true,
2557+
status: 200,
2558+
json: async () => ({
2559+
resource: 'https://resource.example.com',
2560+
authorization_servers: ['https://auth.example.com']
2561+
})
2562+
});
2563+
2564+
// Create a wrapped fetch with various RequestInit options
2565+
const wrappedFetch = createFetchWithInit(customFetch, {
2566+
credentials: 'include',
2567+
mode: 'cors',
2568+
cache: 'no-cache',
2569+
headers: {
2570+
'user-agent': 'MyApp/1.0'
2571+
}
2572+
});
2573+
2574+
await discoverOAuthProtectedResourceMetadata('https://resource.example.com', undefined, wrappedFetch);
2575+
2576+
expect(customFetch).toHaveBeenCalledTimes(1);
2577+
const [url, options] = customFetch.mock.calls[0];
2578+
2579+
// All RequestInit options should be preserved
2580+
expect(options.credentials).toBe('include');
2581+
expect(options.mode).toBe('cors');
2582+
expect(options.cache).toBe('no-cache');
2583+
expect(options.headers).toMatchObject({
2584+
'user-agent': 'MyApp/1.0'
2585+
});
2586+
});
2587+
});
24792588
});

0 commit comments

Comments
 (0)