Skip to content

Commit 50f2aab

Browse files
committed
vitest types
1 parent a924548 commit 50f2aab

File tree

4 files changed

+113
-109
lines changed

4 files changed

+113
-109
lines changed

src/client/auth.test.ts

Lines changed: 75 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ import {
1515
} from './auth.js';
1616
import { ServerError } from '../server/auth/errors.js';
1717
import { AuthorizationServerMetadata } from '../shared/auth.js';
18+
import { vi, type Mock } from 'vitest';
19+
20+
// Mock pkce-challenge
21+
vi.mock('pkce-challenge', () => ({
22+
default: () => ({
23+
code_verifier: 'test_verifier',
24+
code_challenge: 'test_challenge'
25+
})
26+
}));
1827

1928
// Mock fetch globally
20-
const mockFetch = jest.fn();
29+
const mockFetch = vi.fn();
2130
global.fetch = mockFetch;
2231

2332
describe('OAuth Authorization', () => {
@@ -30,7 +39,7 @@ describe('OAuth Authorization', () => {
3039
const resourceUrl = 'https://resource.example.com/.well-known/oauth-protected-resource';
3140
const mockResponse = {
3241
headers: {
33-
get: jest.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${resourceUrl}"` : null))
42+
get: vi.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${resourceUrl}"` : null))
3443
}
3544
} as unknown as Response;
3645

@@ -41,7 +50,7 @@ describe('OAuth Authorization', () => {
4150
const scope = 'read';
4251
const mockResponse = {
4352
headers: {
44-
get: jest.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp", scope="${scope}"` : null))
53+
get: vi.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp", scope="${scope}"` : null))
4554
}
4655
} as unknown as Response;
4756

@@ -53,7 +62,7 @@ describe('OAuth Authorization', () => {
5362
const scope = 'read';
5463
const mockResponse = {
5564
headers: {
56-
get: jest.fn(name =>
65+
get: vi.fn(name =>
5766
name === 'WWW-Authenticate' ? `Basic realm="mcp", resource_metadata="${resourceUrl}", scope="${scope}"` : null
5867
)
5968
}
@@ -65,7 +74,7 @@ describe('OAuth Authorization', () => {
6574
it('returns empty object if resource_metadata and scope not present', async () => {
6675
const mockResponse = {
6776
headers: {
68-
get: jest.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp"` : null))
77+
get: vi.fn(name => (name === 'WWW-Authenticate' ? `Bearer realm="mcp"` : null))
6978
}
7079
} as unknown as Response;
7180

@@ -77,7 +86,7 @@ describe('OAuth Authorization', () => {
7786
const scope = 'read';
7887
const mockResponse = {
7988
headers: {
80-
get: jest.fn(name =>
89+
get: vi.fn(name =>
8190
name === 'WWW-Authenticate' ? `Bearer realm="mcp", resource_metadata="${resourceUrl}", scope="${scope}"` : null
8291
)
8392
}
@@ -393,7 +402,7 @@ describe('OAuth Authorization', () => {
393402
authorization_servers: ['https://auth.example.com']
394403
};
395404

396-
const customFetch = jest.fn().mockResolvedValue({
405+
const customFetch = vi.fn().mockResolvedValue({
397406
ok: true,
398407
status: 200,
399408
json: async () => validMetadata
@@ -689,7 +698,7 @@ describe('OAuth Authorization', () => {
689698
code_challenge_methods_supported: ['S256']
690699
};
691700

692-
const customFetch = jest.fn().mockResolvedValue({
701+
const customFetch = vi.fn().mockResolvedValue({
693702
ok: true,
694703
status: 200,
695704
json: async () => validMetadata
@@ -851,7 +860,7 @@ describe('OAuth Authorization', () => {
851860
});
852861

853862
it('supports custom fetch function', async () => {
854-
const customFetch = jest.fn().mockResolvedValue({
863+
const customFetch = vi.fn().mockResolvedValue({
855864
ok: true,
856865
status: 200,
857866
json: async () => validOAuthMetadata
@@ -1110,17 +1119,9 @@ describe('OAuth Authorization', () => {
11101119
});
11111120

11121121
expect(tokens).toEqual(validTokens);
1113-
expect(mockFetch).toHaveBeenCalledWith(
1114-
expect.objectContaining({
1115-
href: 'https://auth.example.com/token'
1116-
}),
1117-
expect.objectContaining({
1118-
method: 'POST',
1119-
headers: new Headers({
1120-
'Content-Type': 'application/x-www-form-urlencoded'
1121-
})
1122-
})
1123-
);
1122+
expect(mockFetch).toHaveBeenCalledTimes(1);
1123+
expect(mockFetch.mock.calls[0][0].toString()).toBe('https://auth.example.com/token');
1124+
expect(mockFetch.mock.calls[0][1].method).toBe('POST');
11241125

11251126
const body = mockFetch.mock.calls[0][1].body as URLSearchParams;
11261127
expect(body.get('grant_type')).toBe('authorization_code');
@@ -1217,7 +1218,7 @@ describe('OAuth Authorization', () => {
12171218
});
12181219

12191220
it('supports overriding the fetch function used for requests', async () => {
1220-
const customFetch = jest.fn().mockResolvedValue({
1221+
const customFetch = vi.fn().mockResolvedValue({
12211222
ok: true,
12221223
status: 200,
12231224
json: async () => validTokens
@@ -1506,16 +1507,16 @@ describe('OAuth Authorization', () => {
15061507
client_name: 'Test Client'
15071508
};
15081509
},
1509-
clientInformation: jest.fn(),
1510-
tokens: jest.fn(),
1511-
saveTokens: jest.fn(),
1512-
redirectToAuthorization: jest.fn(),
1513-
saveCodeVerifier: jest.fn(),
1514-
codeVerifier: jest.fn()
1510+
clientInformation: vi.fn(),
1511+
tokens: vi.fn(),
1512+
saveTokens: vi.fn(),
1513+
redirectToAuthorization: vi.fn(),
1514+
saveCodeVerifier: vi.fn(),
1515+
codeVerifier: vi.fn()
15151516
};
15161517

15171518
beforeEach(() => {
1518-
jest.clearAllMocks();
1519+
vi.clearAllMocks();
15191520
});
15201521

15211522
it('falls back to /.well-known/oauth-authorization-server when no protected-resource-metadata', async () => {
@@ -1567,9 +1568,9 @@ describe('OAuth Authorization', () => {
15671568
});
15681569

15691570
// Mock provider methods
1570-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue(undefined);
1571-
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
1572-
mockProvider.saveClientInformation = jest.fn();
1571+
(mockProvider.clientInformation as Mock).mockResolvedValue(undefined);
1572+
(mockProvider.tokens as Mock).mockResolvedValue(undefined);
1573+
mockProvider.saveClientInformation = vi.fn();
15731574

15741575
// Call the auth function
15751576
const result = await auth(mockProvider, {
@@ -1619,13 +1620,13 @@ describe('OAuth Authorization', () => {
16191620
});
16201621

16211622
// Mock provider methods for authorization flow
1622-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1623+
(mockProvider.clientInformation as Mock).mockResolvedValue({
16231624
client_id: 'test-client',
16241625
client_secret: 'test-secret'
16251626
});
1626-
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
1627-
(mockProvider.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
1628-
(mockProvider.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
1627+
(mockProvider.tokens as Mock).mockResolvedValue(undefined);
1628+
(mockProvider.saveCodeVerifier as Mock).mockResolvedValue(undefined);
1629+
(mockProvider.redirectToAuthorization as Mock).mockResolvedValue(undefined);
16291630

16301631
// Call auth without authorization code (should trigger redirect)
16311632
const result = await auth(mockProvider, {
@@ -1641,7 +1642,7 @@ describe('OAuth Authorization', () => {
16411642
})
16421643
);
16431644

1644-
const redirectCall = (mockProvider.redirectToAuthorization as jest.Mock).mock.calls[0];
1645+
const redirectCall = (mockProvider.redirectToAuthorization as Mock).mock.calls[0];
16451646
const authUrl: URL = redirectCall[0];
16461647
expect(authUrl.searchParams.get('resource')).toBe('https://api.example.com/mcp-server');
16471648
});
@@ -1689,12 +1690,12 @@ describe('OAuth Authorization', () => {
16891690
});
16901691

16911692
// Mock provider methods for token exchange
1692-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1693+
(mockProvider.clientInformation as Mock).mockResolvedValue({
16931694
client_id: 'test-client',
16941695
client_secret: 'test-secret'
16951696
});
1696-
(mockProvider.codeVerifier as jest.Mock).mockResolvedValue('test-verifier');
1697-
(mockProvider.saveTokens as jest.Mock).mockResolvedValue(undefined);
1697+
(mockProvider.codeVerifier as Mock).mockResolvedValue('test-verifier');
1698+
(mockProvider.saveTokens as Mock).mockResolvedValue(undefined);
16981699

16991700
// Call auth with authorization code
17001701
const result = await auth(mockProvider, {
@@ -1755,15 +1756,15 @@ describe('OAuth Authorization', () => {
17551756
});
17561757

17571758
// Mock provider methods for token refresh
1758-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1759+
(mockProvider.clientInformation as Mock).mockResolvedValue({
17591760
client_id: 'test-client',
17601761
client_secret: 'test-secret'
17611762
});
1762-
(mockProvider.tokens as jest.Mock).mockResolvedValue({
1763+
(mockProvider.tokens as Mock).mockResolvedValue({
17631764
access_token: 'old-access',
17641765
refresh_token: 'refresh123'
17651766
});
1766-
(mockProvider.saveTokens as jest.Mock).mockResolvedValue(undefined);
1767+
(mockProvider.saveTokens as Mock).mockResolvedValue(undefined);
17671768

17681769
// Call auth with existing tokens (should trigger refresh)
17691770
const result = await auth(mockProvider, {
@@ -1783,7 +1784,7 @@ describe('OAuth Authorization', () => {
17831784
});
17841785

17851786
it('skips default PRM resource validation when custom validateResourceURL is provided', async () => {
1786-
const mockValidateResourceURL = jest.fn().mockResolvedValue(undefined);
1787+
const mockValidateResourceURL = vi.fn().mockResolvedValue(undefined);
17871788
const providerWithCustomValidation = {
17881789
...mockProvider,
17891790
validateResourceURL: mockValidateResourceURL
@@ -1821,13 +1822,13 @@ describe('OAuth Authorization', () => {
18211822
});
18221823

18231824
// Mock provider methods
1824-
(providerWithCustomValidation.clientInformation as jest.Mock).mockResolvedValue({
1825+
(providerWithCustomValidation.clientInformation as Mock).mockResolvedValue({
18251826
client_id: 'test-client',
18261827
client_secret: 'test-secret'
18271828
});
1828-
(providerWithCustomValidation.tokens as jest.Mock).mockResolvedValue(undefined);
1829-
(providerWithCustomValidation.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
1830-
(providerWithCustomValidation.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
1829+
(providerWithCustomValidation.tokens as Mock).mockResolvedValue(undefined);
1830+
(providerWithCustomValidation.saveCodeVerifier as Mock).mockResolvedValue(undefined);
1831+
(providerWithCustomValidation.redirectToAuthorization as Mock).mockResolvedValue(undefined);
18311832

18321833
// Call auth - should succeed despite resource mismatch because custom validation overrides default
18331834
const result = await auth(providerWithCustomValidation, {
@@ -1876,13 +1877,13 @@ describe('OAuth Authorization', () => {
18761877
});
18771878

18781879
// Mock provider methods
1879-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1880+
(mockProvider.clientInformation as Mock).mockResolvedValue({
18801881
client_id: 'test-client',
18811882
client_secret: 'test-secret'
18821883
});
1883-
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
1884-
(mockProvider.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
1885-
(mockProvider.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
1884+
(mockProvider.tokens as Mock).mockResolvedValue(undefined);
1885+
(mockProvider.saveCodeVerifier as Mock).mockResolvedValue(undefined);
1886+
(mockProvider.redirectToAuthorization as Mock).mockResolvedValue(undefined);
18861887

18871888
// Call auth with a URL that has the resource as prefix
18881889
const result = await auth(mockProvider, {
@@ -1898,7 +1899,7 @@ describe('OAuth Authorization', () => {
18981899
})
18991900
);
19001901

1901-
const redirectCall = (mockProvider.redirectToAuthorization as jest.Mock).mock.calls[0];
1902+
const redirectCall = (mockProvider.redirectToAuthorization as Mock).mock.calls[0];
19021903
const authUrl: URL = redirectCall[0];
19031904
// Should use the PRM's resource value, not the full requested URL
19041905
expect(authUrl.searchParams.get('resource')).toBe('https://api.example.com/');
@@ -1934,13 +1935,13 @@ describe('OAuth Authorization', () => {
19341935
});
19351936

19361937
// Mock provider methods
1937-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1938+
(mockProvider.clientInformation as Mock).mockResolvedValue({
19381939
client_id: 'test-client',
19391940
client_secret: 'test-secret'
19401941
});
1941-
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
1942-
(mockProvider.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
1943-
(mockProvider.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
1942+
(mockProvider.tokens as Mock).mockResolvedValue(undefined);
1943+
(mockProvider.saveCodeVerifier as Mock).mockResolvedValue(undefined);
1944+
(mockProvider.redirectToAuthorization as Mock).mockResolvedValue(undefined);
19441945

19451946
// Call auth - should not include resource parameter
19461947
const result = await auth(mockProvider, {
@@ -1956,7 +1957,7 @@ describe('OAuth Authorization', () => {
19561957
})
19571958
);
19581959

1959-
const redirectCall = (mockProvider.redirectToAuthorization as jest.Mock).mock.calls[0];
1960+
const redirectCall = (mockProvider.redirectToAuthorization as Mock).mock.calls[0];
19601961
const authUrl: URL = redirectCall[0];
19611962
// Resource parameter should not be present when PRM is not available
19621963
expect(authUrl.searchParams.has('resource')).toBe(false);
@@ -2001,12 +2002,12 @@ describe('OAuth Authorization', () => {
20012002
});
20022003

20032004
// Mock provider methods for token exchange
2004-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
2005+
(mockProvider.clientInformation as Mock).mockResolvedValue({
20052006
client_id: 'test-client',
20062007
client_secret: 'test-secret'
20072008
});
2008-
(mockProvider.codeVerifier as jest.Mock).mockResolvedValue('test-verifier');
2009-
(mockProvider.saveTokens as jest.Mock).mockResolvedValue(undefined);
2009+
(mockProvider.codeVerifier as Mock).mockResolvedValue('test-verifier');
2010+
(mockProvider.saveTokens as Mock).mockResolvedValue(undefined);
20102011

20112012
// Call auth with authorization code
20122013
const result = await auth(mockProvider, {
@@ -2064,15 +2065,15 @@ describe('OAuth Authorization', () => {
20642065
});
20652066

20662067
// Mock provider methods for token refresh
2067-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
2068+
(mockProvider.clientInformation as Mock).mockResolvedValue({
20682069
client_id: 'test-client',
20692070
client_secret: 'test-secret'
20702071
});
2071-
(mockProvider.tokens as jest.Mock).mockResolvedValue({
2072+
(mockProvider.tokens as Mock).mockResolvedValue({
20722073
access_token: 'old-access',
20732074
refresh_token: 'refresh123'
20742075
});
2075-
(mockProvider.saveTokens as jest.Mock).mockResolvedValue(undefined);
2076+
(mockProvider.saveTokens as Mock).mockResolvedValue(undefined);
20762077

20772078
// Call auth with existing tokens (should trigger refresh)
20782079
const result = await auth(mockProvider, {
@@ -2125,13 +2126,13 @@ describe('OAuth Authorization', () => {
21252126
});
21262127

21272128
// Mock provider methods
2128-
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
2129+
(mockProvider.clientInformation as Mock).mockResolvedValue({
21292130
client_id: 'test-client',
21302131
client_secret: 'test-secret'
21312132
});
2132-
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
2133-
(mockProvider.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
2134-
(mockProvider.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
2133+
(mockProvider.tokens as Mock).mockResolvedValue(undefined);
2134+
(mockProvider.saveCodeVerifier as Mock).mockResolvedValue(undefined);
2135+
(mockProvider.redirectToAuthorization as Mock).mockResolvedValue(undefined);
21352136

21362137
// Call auth with serverUrl that has a path
21372138
const result = await auth(mockProvider, {
@@ -2151,7 +2152,7 @@ describe('OAuth Authorization', () => {
21512152
});
21522153

21532154
it('supports overriding the fetch function used for requests', async () => {
2154-
const customFetch = jest.fn();
2155+
const customFetch = vi.fn();
21552156

21562157
// Mock PRM discovery
21572158
customFetch.mockResolvedValueOnce({
@@ -2187,15 +2188,15 @@ describe('OAuth Authorization', () => {
21872188
redirect_uris: ['http://localhost:3000/callback']
21882189
};
21892190
},
2190-
clientInformation: jest.fn().mockResolvedValue({
2191+
clientInformation: vi.fn().mockResolvedValue({
21912192
client_id: 'client123',
21922193
client_secret: 'secret123'
21932194
}),
2194-
tokens: jest.fn().mockResolvedValue(undefined),
2195-
saveTokens: jest.fn(),
2196-
redirectToAuthorization: jest.fn(),
2197-
saveCodeVerifier: jest.fn(),
2198-
codeVerifier: jest.fn().mockResolvedValue('verifier123')
2195+
tokens: vi.fn().mockResolvedValue(undefined),
2196+
saveTokens: vi.fn(),
2197+
redirectToAuthorization: vi.fn(),
2198+
saveCodeVerifier: vi.fn(),
2199+
codeVerifier: vi.fn().mockResolvedValue('verifier123')
21992200
};
22002201

22012202
const result = await auth(mockProvider, {

0 commit comments

Comments
 (0)