Skip to content

Commit 415ca4c

Browse files
committed
test scope parameter passing
1 parent 70b965f commit 415ca4c

File tree

1 file changed

+84
-7
lines changed

1 file changed

+84
-7
lines changed

client/src/components/__tests__/AuthDebugger.test.tsx

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import "@testing-library/jest-dom";
99
import { describe, it, beforeEach, jest } from "@jest/globals";
1010
import AuthDebugger, { AuthDebuggerProps } from "../AuthDebugger";
1111
import { TooltipProvider } from "@/components/ui/tooltip";
12+
import { SESSION_KEYS } from "@/lib/constants";
1213

13-
// Mock OAuth data that matches the schemas
1414
const mockOAuthTokens = {
1515
access_token: "test_access_token",
1616
token_type: "Bearer",
@@ -49,6 +49,7 @@ import {
4949
startAuthorization,
5050
exchangeAuthorization,
5151
} from "@modelcontextprotocol/sdk/client/auth.js";
52+
import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
5253

5354
// Type the mocked functions properly
5455
const mockDiscoverOAuthMetadata = discoverOAuthMetadata as jest.MockedFunction<
@@ -64,7 +65,6 @@ const mockExchangeAuthorization = exchangeAuthorization as jest.MockedFunction<
6465
typeof exchangeAuthorization
6566
>;
6667

67-
// Mock Session Storage
6868
const sessionStorageMock = {
6969
getItem: jest.fn(),
7070
setItem: jest.fn(),
@@ -75,7 +75,6 @@ Object.defineProperty(window, "sessionStorage", {
7575
value: sessionStorageMock,
7676
});
7777

78-
// Mock the location.origin
7978
Object.defineProperty(window, "location", {
8079
value: {
8180
origin: "http://localhost:3000",
@@ -108,12 +107,19 @@ describe("AuthDebugger", () => {
108107
jest.clearAllMocks();
109108
sessionStorageMock.getItem.mockReturnValue(null);
110109

111-
// Set up mock implementations
112110
mockDiscoverOAuthMetadata.mockResolvedValue(mockOAuthMetadata);
113111
mockRegisterClient.mockResolvedValue(mockOAuthClientInfo);
114-
mockStartAuthorization.mockResolvedValue({
115-
authorizationUrl: new URL("https://oauth.example.com/authorize"),
116-
codeVerifier: "test_verifier",
112+
mockStartAuthorization.mockImplementation(async (_sseUrl, options) => {
113+
const authUrl = new URL("https://oauth.example.com/authorize");
114+
115+
if (options.scope) {
116+
authUrl.searchParams.set("scope", options.scope);
117+
}
118+
119+
return {
120+
authorizationUrl: authUrl,
121+
codeVerifier: "test_verifier",
122+
};
117123
});
118124
mockExchangeAuthorization.mockResolvedValue(mockOAuthTokens);
119125
});
@@ -300,5 +306,76 @@ describe("AuthDebugger", () => {
300306
"https://example.com",
301307
);
302308
});
309+
310+
// Setup helper for OAuth authorization tests
311+
const setupAuthorizationUrlTest = async (metadata: OAuthMetadata) => {
312+
const updateAuthState = jest.fn();
313+
314+
// Mock the session storage to return metadata
315+
sessionStorageMock.getItem.mockImplementation((key) => {
316+
if (key === `[https://example.com] ${SESSION_KEYS.SERVER_METADATA}`) {
317+
return JSON.stringify(metadata);
318+
}
319+
if (
320+
key === `[https://example.com] ${SESSION_KEYS.CLIENT_INFORMATION}`
321+
) {
322+
return JSON.stringify(mockOAuthClientInfo);
323+
}
324+
return null;
325+
});
326+
327+
await act(async () => {
328+
renderAuthDebugger({
329+
updateAuthState,
330+
authState: {
331+
...defaultAuthState,
332+
isInitiatingAuth: false,
333+
oauthStep: "client_registration",
334+
oauthMetadata: metadata,
335+
oauthClientInfo: mockOAuthClientInfo,
336+
},
337+
});
338+
});
339+
340+
// Click Continue to trigger authorization
341+
await act(async () => {
342+
fireEvent.click(screen.getByText("Continue"));
343+
});
344+
345+
return updateAuthState;
346+
};
347+
348+
it("should include scope in authorization URL when scopes_supported is present", async () => {
349+
const metadataWithScopes = {
350+
...mockOAuthMetadata,
351+
scopes_supported: ["read", "write", "admin"],
352+
};
353+
354+
const updateAuthState =
355+
await setupAuthorizationUrlTest(metadataWithScopes);
356+
357+
// Wait for the updateAuthState to be called
358+
await waitFor(() => {
359+
expect(updateAuthState).toHaveBeenCalledWith(
360+
expect.objectContaining({
361+
authorizationUrl: expect.stringContaining("scope="),
362+
}),
363+
);
364+
});
365+
});
366+
367+
it("should not include scope in authorization URL when scopes_supported is not present", async () => {
368+
const updateAuthState =
369+
await setupAuthorizationUrlTest(mockOAuthMetadata);
370+
371+
// Wait for the updateAuthState to be called
372+
await waitFor(() => {
373+
expect(updateAuthState).toHaveBeenCalledWith(
374+
expect.objectContaining({
375+
authorizationUrl: expect.not.stringContaining("scope="),
376+
}),
377+
);
378+
});
379+
});
303380
});
304381
});

0 commit comments

Comments
 (0)