test: add tests for proxy auth header in config endpoint
- Added tests for App.tsx config endpoint to verify X-MCP-Proxy-Auth header usage - Added tests for getMCPProxyAuthToken utility function - Tests cover: - Sending correct proxy auth header when token is configured - Not sending header when token is empty - Using custom header names (for future flexibility) - Handling config endpoint errors gracefully - All tests passing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
37cff101b1
commit
62402b49c4
2 changed files with 312 additions and 0 deletions
240
client/src/__tests__/App.config.test.tsx
Normal file
240
client/src/__tests__/App.config.test.tsx
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
import React from "react";
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
import App from "../App";
|
||||
import { DEFAULT_INSPECTOR_CONFIG } from "../lib/constants";
|
||||
|
||||
// Mock auth dependencies first
|
||||
jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({
|
||||
auth: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../lib/oauth-state-machine", () => ({
|
||||
OAuthStateMachine: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../lib/auth", () => ({
|
||||
InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
||||
tokens: jest.fn().mockResolvedValue(null),
|
||||
clear: jest.fn(),
|
||||
})),
|
||||
DebugInspectorOAuthClientProvider: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock the config utils
|
||||
jest.mock("../utils/configUtils", () => ({
|
||||
...jest.requireActual("../utils/configUtils"),
|
||||
getMCPProxyAddress: jest.fn(() => "http://localhost:6277"),
|
||||
getMCPProxyAuthToken: jest.fn((config) => ({
|
||||
token: config.MCP_PROXY_AUTH_TOKEN.value,
|
||||
header: "X-MCP-Proxy-Auth",
|
||||
})),
|
||||
getInitialTransportType: jest.fn(() => "stdio"),
|
||||
getInitialSseUrl: jest.fn(() => "http://localhost:3001/sse"),
|
||||
getInitialCommand: jest.fn(() => "mcp-server-everything"),
|
||||
getInitialArgs: jest.fn(() => ""),
|
||||
initializeInspectorConfig: jest.fn(() => DEFAULT_INSPECTOR_CONFIG),
|
||||
saveInspectorConfig: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock other dependencies
|
||||
jest.mock("../lib/hooks/useConnection", () => ({
|
||||
useConnection: () => ({
|
||||
connectionStatus: "disconnected",
|
||||
serverCapabilities: null,
|
||||
mcpClient: null,
|
||||
requestHistory: [],
|
||||
makeRequest: jest.fn(),
|
||||
sendNotification: jest.fn(),
|
||||
handleCompletion: jest.fn(),
|
||||
completionsSupported: false,
|
||||
connect: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock("../lib/hooks/useDraggablePane", () => ({
|
||||
useDraggablePane: () => ({
|
||||
height: 300,
|
||||
handleDragStart: jest.fn(),
|
||||
}),
|
||||
useDraggableSidebar: () => ({
|
||||
width: 320,
|
||||
isDragging: false,
|
||||
handleDragStart: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock("../components/Sidebar", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>Sidebar</div>,
|
||||
}));
|
||||
|
||||
// Mock fetch
|
||||
global.fetch = jest.fn();
|
||||
|
||||
describe("App - Config Endpoint", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
defaultEnvironment: { TEST_ENV: "test" },
|
||||
defaultCommand: "test-command",
|
||||
defaultArgs: "test-args",
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Reset getMCPProxyAuthToken to default behavior
|
||||
const { getMCPProxyAuthToken } = require("../utils/configUtils");
|
||||
getMCPProxyAuthToken.mockImplementation((config) => ({
|
||||
token: config.MCP_PROXY_AUTH_TOKEN.value,
|
||||
header: "X-MCP-Proxy-Auth",
|
||||
}));
|
||||
});
|
||||
|
||||
test("sends X-MCP-Proxy-Auth header when fetching config with proxy auth token", async () => {
|
||||
const mockConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-proxy-token",
|
||||
},
|
||||
};
|
||||
|
||||
// Mock initializeInspectorConfig to return our test config
|
||||
const { initializeInspectorConfig } = require("../utils/configUtils");
|
||||
initializeInspectorConfig.mockReturnValue(mockConfig);
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"http://localhost:6277/config",
|
||||
{
|
||||
headers: {
|
||||
"X-MCP-Proxy-Auth": "Bearer test-proxy-token",
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("does not send auth header when proxy auth token is empty", async () => {
|
||||
const mockConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Mock initializeInspectorConfig to return our test config
|
||||
const { initializeInspectorConfig } = require("../utils/configUtils");
|
||||
initializeInspectorConfig.mockReturnValue(mockConfig);
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"http://localhost:6277/config",
|
||||
{
|
||||
headers: {},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("uses custom header name if getMCPProxyAuthToken returns different header", async () => {
|
||||
const mockConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-proxy-token",
|
||||
},
|
||||
};
|
||||
|
||||
// Mock to return a custom header name
|
||||
const { getMCPProxyAuthToken, initializeInspectorConfig } = require("../utils/configUtils");
|
||||
getMCPProxyAuthToken.mockReturnValue({
|
||||
token: "test-proxy-token",
|
||||
header: "X-Custom-Auth",
|
||||
});
|
||||
initializeInspectorConfig.mockReturnValue(mockConfig);
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"http://localhost:6277/config",
|
||||
{
|
||||
headers: {
|
||||
"X-Custom-Auth": "Bearer test-proxy-token",
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("config endpoint response updates app state", async () => {
|
||||
const mockConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-proxy-token",
|
||||
},
|
||||
};
|
||||
|
||||
const { initializeInspectorConfig } = require("../utils/configUtils");
|
||||
initializeInspectorConfig.mockReturnValue(mockConfig);
|
||||
|
||||
const { container } = render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Verify the fetch was called with correct parameters
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"http://localhost:6277/config",
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
"X-MCP-Proxy-Auth": "Bearer test-proxy-token",
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("handles config endpoint errors gracefully", async () => {
|
||||
const mockConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-proxy-token",
|
||||
},
|
||||
};
|
||||
|
||||
const { initializeInspectorConfig } = require("../utils/configUtils");
|
||||
initializeInspectorConfig.mockReturnValue(mockConfig);
|
||||
|
||||
// Mock fetch to reject
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error("Network error"));
|
||||
|
||||
// Spy on console.error
|
||||
const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
"Error fetching default environment:",
|
||||
expect.any(Error)
|
||||
);
|
||||
});
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
72
client/src/utils/__tests__/configUtils.test.ts
Normal file
72
client/src/utils/__tests__/configUtils.test.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { getMCPProxyAuthToken } from "../configUtils";
|
||||
import { DEFAULT_INSPECTOR_CONFIG } from "../../lib/constants";
|
||||
import { InspectorConfig } from "../../lib/configurationTypes";
|
||||
|
||||
describe("configUtils", () => {
|
||||
describe("getMCPProxyAuthToken", () => {
|
||||
test("returns token and default header name", () => {
|
||||
const config: InspectorConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-token-123",
|
||||
},
|
||||
};
|
||||
|
||||
const result = getMCPProxyAuthToken(config);
|
||||
|
||||
expect(result).toEqual({
|
||||
token: "test-token-123",
|
||||
header: "X-MCP-Proxy-Auth",
|
||||
});
|
||||
});
|
||||
|
||||
test("returns empty token when not configured", () => {
|
||||
const config: InspectorConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "",
|
||||
},
|
||||
};
|
||||
|
||||
const result = getMCPProxyAuthToken(config);
|
||||
|
||||
expect(result).toEqual({
|
||||
token: "",
|
||||
header: "X-MCP-Proxy-Auth",
|
||||
});
|
||||
});
|
||||
|
||||
test("always returns X-MCP-Proxy-Auth as header name", () => {
|
||||
const config: InspectorConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "any-token",
|
||||
},
|
||||
};
|
||||
|
||||
const result = getMCPProxyAuthToken(config);
|
||||
|
||||
expect(result.header).toBe("X-MCP-Proxy-Auth");
|
||||
});
|
||||
|
||||
test("handles null/undefined value gracefully", () => {
|
||||
const config: InspectorConfig = {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: null as any,
|
||||
},
|
||||
};
|
||||
|
||||
const result = getMCPProxyAuthToken(config);
|
||||
|
||||
expect(result).toEqual({
|
||||
token: null,
|
||||
header: "X-MCP-Proxy-Auth",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue