-
Notifications
You must be signed in to change notification settings - Fork 13
Dynamic user agent #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e2fd8c3
3bd652a
9da5776
5cb7895
665cee5
2d6992a
c403388
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,256 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import CONFIG from "../../config"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Mock the config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.mock("../../config", () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CLIENT_SETTINGS: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
USER_AGENT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MCP_USER_AGENT: "mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
describe("get-agent", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let originalCwd: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let originalError: typeof Error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Store original values | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
originalCwd = process.cwd(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
originalError = global.Error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
afterEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Restore original values | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue(originalCwd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = originalError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.clearAllMocks(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+13
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Restore the real process.cwd; don’t replace it with a mock You’re saving the cwd value (string) and then replacing process.cwd with a mock that returns that string. This leaks a mocked function into global state across the suite. Apply this minimal fix (and prefer spy-based mocking in tests): - let originalCwd: string;
+ let originalCwdFn: typeof process.cwd;
let originalError: typeof Error;
beforeEach(() => {
// Store original values
- originalCwd = process.cwd();
+ originalCwdFn = process.cwd;
originalError = global.Error;
});
afterEach(() => {
// Restore original values
- process.cwd = jest.fn().mockReturnValue(originalCwd);
+ process.cwd = originalCwdFn;
global.Error = originalError;
- jest.clearAllMocks();
+ jest.restoreAllMocks();
}); Follow-up (recommended): replace all direct assignments to process.cwd in tests with jest.spyOn(process, "cwd").mockReturnValue(...). 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
describe("getDynamicUserAgent", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT by default", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Import after mocking to get fresh module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return MCP_USER_AGENT when cwd contains 'mailtrap-mcp' and not in node_modules", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/path/to/mailtrap-mcp"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Clear module cache and re-import to get fresh module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.MCP_USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT when cwd contains 'mailtrap-mcp' but is in node_modules", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.fn() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.mockReturnValue("/path/to/node_modules/mailtrap-mcp"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT when process.cwd() throws an error", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockImplementation(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error("Permission denied"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return MCP_USER_AGENT when call stack contains 'mailtrap-mcp' and not from node_modules/mailtrap", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = jest.fn().mockImplementation(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stack: ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Error: Test error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Object.<anonymous> (/path/to/mailtrap-mcp/index.js:10:1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Module._compile (internal/modules/cjs/loader.js:1063:30) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/path/to/regular-app"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.MCP_USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT when call stack contains 'mailtrap-mcp' but is from node_modules/mailtrap", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = jest.fn().mockImplementation(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stack: ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Error: Test error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Object.<anonymous> (/path/to/node_modules/mailtrap/index.js:10:1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Module._compile (internal/modules/cjs/loader.js:1063:30) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/path/to/regular-app"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT when call stack does not contain 'mailtrap-mcp'", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = jest.fn().mockImplementation(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stack: ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Error: Test error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Object.<anonymous> (/path/to/regular-app/index.js:10:1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
at Module._compile (internal/modules/cjs/loader.js:1063:30) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/path/to/regular-app"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should return USER_AGENT when call stack is undefined", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = jest.fn().mockImplementation(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stack: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/path/to/regular-app"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should handle edge cases gracefully", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// All undefined/null cases | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockImplementation(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error("Permission denied"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
global.Error = jest.fn().mockImplementation(() => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stack: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
describe("real-world scenarios", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should detect MCP context when running from MCP working directory", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.fn() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.mockReturnValue("/Users/user/projects/mailtrap-mcp"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.MCP_USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should not detect MCP context when used as a regular npm package", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
process.cwd = jest.fn().mockReturnValue("/Users/user/projects/my-app"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: getDynamicUserAgent } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/get-agent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = getDynamicUserAgent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(result).toBe(CONFIG.CLIENT_SETTINGS.USER_AGENT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
describe("integration with MailtrapClient", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
it("should be used by MailtrapClient for User-Agent header", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Mock the full config for MailtrapClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.doMock("../../config", () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CLIENT_SETTINGS: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SENDING_ENDPOINT: "https://send.api.mailtrap.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
BULK_ENDPOINT: "https://bulk.api.mailtrap.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TESTING_ENDPOINT: "https://sandbox.api.mailtrap.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GENERAL_ENDPOINT: "https://mailtrap.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
USER_AGENT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"mailtrap-nodejs (https://github.com/railsware/mailtrap-nodejs)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MCP_USER_AGENT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"mailtrap-mcp (https://github.com/railsware/mailtrap-mcp)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MAX_REDIRECTS: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TIMEOUT: 10000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ERRORS: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FILENAME_REQUIRED: "Filename is required.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CONTENT_REQUIRED: "Content is required.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SUBJECT_REQUIRED: "Subject is required.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FROM_REQUIRED: "From is required.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SENDING_FAILED: "Sending failed.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NO_DATA_ERROR: "No Data.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TEST_INBOX_ID_MISSING: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"testInboxId is missing, testing API will not work.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ACCOUNT_ID_MISSING: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"accountId is missing, some features of testing API may not work properly.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
BULK_SANDBOX_INCOMPATIBLE: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"Bulk mode is not applicable for sandbox API.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TRANSPORT_SETTINGS: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NAME: "MailtrapTransport", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Clear module cache and re-import | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
jest.resetModules(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { default: MailtrapClient } = jest.requireActual( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"../../lib/MailtrapClient" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Create a client instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const client = new MailtrapClient({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
token: "test-token", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// The User-Agent should be set in the axios instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// We can't easily test the internal axios instance, but we can verify | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// that the function is called during client creation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expect(client).toBeDefined(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import CONFIG from "../config"; | ||
|
||
const { USER_AGENT, MCP_USER_AGENT } = CONFIG.CLIENT_SETTINGS; | ||
|
||
/** | ||
* Checks if the main module filename indicates MCP context. | ||
* @returns true if main module contains "mailtrap-mcp" and is not in node_modules | ||
*/ | ||
function isMainModuleMCP(): boolean { | ||
const mainFile = require?.main?.filename; | ||
|
||
return !!( | ||
mainFile && | ||
mainFile.includes("mailtrap-mcp") && | ||
!mainFile.includes("node_modules") | ||
); | ||
} | ||
Comment on lines
+9
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ESM ReferenceError risk from using
Apply this diff: function isMainModuleMCP(): boolean {
- const mainFile = require?.main?.filename;
-
- return !!(
- mainFile &&
- mainFile.includes("mailtrap-mcp") &&
- !mainFile.includes("node_modules")
- );
+ const mainFile =
+ (typeof require !== "undefined" ? require.main?.filename : undefined) ??
+ process.argv?.[1];
+ if (!mainFile) return false;
+ const lower = mainFile.toLowerCase();
+ return lower.includes("mailtrap-mcp") && !lower.includes("node_modules");
} 🤖 Prompt for AI Agents
|
||
|
||
/** | ||
* Checks if running in MCP runtime context (Claude Desktop). | ||
* @returns true if main module is from MCP runtime | ||
*/ | ||
function isMCPRuntimeContext(): boolean { | ||
const mainFile = require?.main?.filename; | ||
|
||
return !!( | ||
mainFile && | ||
(mainFile.includes("mcp-runtime") || | ||
mainFile.includes("nodeHost.js") || | ||
mainFile.includes("Claude.app")) | ||
); | ||
} | ||
|
||
/** | ||
* Checks if the current working directory indicates MCP context. | ||
* @returns true if cwd contains "mailtrap-mcp" and is not in node_modules | ||
*/ | ||
function isWorkingDirectoryMCP(): boolean { | ||
try { | ||
const cwd = process.cwd(); | ||
return cwd.includes("mailtrap-mcp") && !cwd.includes("node_modules"); | ||
} catch { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Checks if the call stack indicates MCP context. | ||
* @returns true if stack contains "mailtrap-mcp" and is not from node_modules/mailtrap | ||
*/ | ||
function isCallStackMCP(): boolean { | ||
const { stack } = new Error(); | ||
|
||
return !!( | ||
stack && | ||
stack.includes("mailtrap-mcp") && | ||
!stack.includes("node_modules/mailtrap") | ||
); | ||
} | ||
|
||
/** | ||
* Gets the appropriate User-Agent string based on the current context. | ||
* @returns The User-Agent string for the current context | ||
*/ | ||
function getDynamicUserAgent(): string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you ask me, bringing the MCP concern to the SDK project is a violation of the SRP 🙂 I would approach this differently and allow specifying the user agent directly via a configuration setting of the SDK client. And, override this setting in the MCP server NPM package. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if allowing users to override user agent is not a problem, then its simple solution. I thought that we don't want to enable users to change the user agent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yanchuk WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @vittorius, it's cleaner to allow to override user agent, and it might be useful for other purposes (for the users), too. |
||
const isMailtrapMCPContext = | ||
isMainModuleMCP() || | ||
isWorkingDirectoryMCP() || | ||
isCallStackMCP() || | ||
isMCPRuntimeContext(); | ||
|
||
return isMailtrapMCPContext ? MCP_USER_AGENT : USER_AGENT; | ||
} | ||
|
||
export default getDynamicUserAgent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mock the default export shape of config to avoid interop breakage
The test imports CONFIG as a default export, but the mock returns a plain object. Under ts-jest/Babel interop, default may be undefined. Return { __esModule: true, default: ... }.
Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents