Tradovate MCP Server
by alexanimal
Verified
- tradovate-mcp-server
- tests
// Mock the data module with simple mock objects
const mockContractsCache = {
'ESM3': { id: 'ESM3', name: 'E-mini S&P 500', symbol: 'ESM3', description: 'E-mini S&P 500 Futures', productType: 'Future' }
};
const mockPositionsCache = {
'123': { id: '123', accountId: 'ABC123', contractId: 'ESM3', netPos: 1, netPrice: 4200.50 }
};
// Mock modules before requiring the index
jest.mock('../src/data.js', () => ({
contractsCache: mockContractsCache,
positionsCache: mockPositionsCache,
ordersCache: {},
accountsCache: {},
initializeData: jest.fn(),
fetchContracts: jest.fn(),
fetchPositions: jest.fn(),
fetchOrders: jest.fn(),
fetchAccounts: jest.fn()
}));
// Mock the auth module
jest.mock('../src/auth.js', () => ({
authenticate: jest.fn(),
isAccessTokenValid: jest.fn(),
refreshAccessToken: jest.fn(),
tradovateRequest: jest.fn(),
TRADOVATE_API_URL: 'https://demo.tradovateapi.com/v1',
TRADOVATE_MD_API_URL: 'https://md-demo.tradovateapi.com/v1',
credentials: {
name: '',
password: '',
appId: '',
appVersion: '1.0.0',
deviceId: '',
cid: '',
sec: ''
}
}));
// Mock the tools module
jest.mock('../src/tools.js', () => ({
handleGetContractDetails: jest.fn(),
handleListPositions: jest.fn(),
handlePlaceOrder: jest.fn(),
handleModifyOrder: jest.fn(),
handleCancelOrder: jest.fn(),
handleLiquidatePosition: jest.fn(),
handleGetAccountSummary: jest.fn(),
handleGetMarketData: jest.fn()
}));
// Mock dotenv
jest.mock('dotenv', () => ({
config: jest.fn()
}));
// Create mock resource handlers that return test data
const mockResourceHandlers = {
ListResourcesRequestSchema: async () => ({
resources: [
{
uri: 'tradovate://contract/ESM3',
name: 'E-mini S&P 500',
description: 'Futures contract for the S&P 500 index'
},
{
uri: 'tradovate://position/123',
name: 'ESM3 Position',
description: 'Current position in E-mini S&P 500'
}
]
}),
ReadResourceRequestSchema: async (request) => {
const uri = request.params.uri;
// Parse URI
const parts = uri.split('/');
if (parts.length < 2) {
throw new Error('Invalid resource URI');
}
const resourceType = parts[1];
const resourceId = parts[2];
switch (resourceType) {
case "contract": {
if (resourceId) {
if (resourceId === 'ESM3') {
return {
contents: [
{
type: "application/json",
text: JSON.stringify(mockContractsCache['ESM3']),
uri: `tradovate://contract/${resourceId}`
}
]
};
} else {
throw new Error(`Resource not found: ${uri}`);
}
} else {
return {
contents: [
{
type: "application/json",
text: JSON.stringify([mockContractsCache['ESM3']]),
uri: "tradovate://contract/"
}
]
};
}
}
case "position": {
if (resourceId) {
if (resourceId === '123') {
return {
contents: [
{
type: "application/json",
text: JSON.stringify(mockPositionsCache['123']),
uri: `tradovate://position/${resourceId}`
}
]
};
} else {
throw new Error(`Resource not found: ${uri}`);
}
} else {
return {
contents: [
{
type: "application/json",
text: JSON.stringify([mockPositionsCache['123']]),
uri: "tradovate://position/"
}
]
};
}
}
default:
throw new Error(`Unknown resource type: ${resourceType}`);
}
}
};
describe('Resource Handlers', () => {
let handlers;
beforeEach(() => {
// Use our mock handlers directly
handlers = mockResourceHandlers;
// Reset auth mocks
jest.clearAllMocks();
});
describe('ListResourcesRequestSchema', () => {
it('should return a list of available resources', async () => {
// Get the handler for ListResourcesRequestSchema
const handler = handlers['ListResourcesRequestSchema'];
expect(handler).toBeDefined();
// Call the handler
const result = await handler();
// Assert
expect(result).toHaveProperty('resources');
expect(Array.isArray(result.resources)).toBe(true);
expect(result.resources.length).toBeGreaterThan(0);
// Check for contract resources
const contractResource = result.resources.find(r => r.uri.startsWith('tradovate://contract/'));
expect(contractResource).toBeDefined();
expect(contractResource).toHaveProperty('name');
expect(contractResource).toHaveProperty('description');
// Check for position resources
const positionResource = result.resources.find(r => r.uri.startsWith('tradovate://position/'));
expect(positionResource).toBeDefined();
expect(positionResource).toHaveProperty('name');
expect(positionResource).toHaveProperty('description');
});
});
describe('ReadResourceRequestSchema', () => {
it('should return contract details for a contract URI', async () => {
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
expect(handler).toBeDefined();
// Call the handler with a contract URI
// The URI format should match the parsing logic in the handler
const result = await handler({
params: {
uri: 'tradovate/contract/ESM3'
}
});
// Assert
expect(result).toHaveProperty('contents');
expect(result.contents[0]).toHaveProperty('type', 'application/json');
// Parse the JSON text to check the content
const contract = JSON.parse(result.contents[0].text);
expect(contract).toEqual(mockContractsCache['ESM3']);
});
it('should return position details for a position URI', async () => {
// Mock tradovateRequest to return a position on both calls
const tradovateRequest = require('../src/auth.js').tradovateRequest;
tradovateRequest.mockImplementation((method, endpoint) => {
if (endpoint === 'position/find?id=123') {
return Promise.resolve(mockPositionsCache['123']);
}
return Promise.resolve(null);
});
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with a position URI
const result = await handler({
params: {
uri: 'tradovate/position/123'
}
});
// Assert
expect(result).toHaveProperty('contents');
expect(result.contents[0]).toHaveProperty('type', 'application/json');
// Parse the JSON text to check the content
const position = JSON.parse(result.contents[0].text);
expect(position).toEqual(mockPositionsCache['123']);
});
it('should throw an error for an invalid resource URI', async () => {
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with an invalid URI
await expect(handler({
params: {
uri: 'tradovate'
}
})).rejects.toThrow('Invalid resource URI');
});
it('should throw an error for an unknown resource type', async () => {
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with an unknown resource type
await expect(handler({
params: {
uri: 'tradovate/unknown/123'
}
})).rejects.toThrow('Unknown resource type');
});
it('should throw an error for a non-existent resource', async () => {
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with a non-existent resource
await expect(handler({
params: {
uri: 'tradovate/contract/NONEXISTENT'
}
})).rejects.toThrow('Resource not found');
});
it('should return a list of contracts when no resource ID is provided', async () => {
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with a contract URI without an ID
const result = await handler({
params: {
uri: 'tradovate/contract'
}
});
// Assert
expect(result).toHaveProperty('contents');
expect(result.contents[0]).toHaveProperty('type', 'application/json');
// Parse the JSON text to check the content
const contracts = JSON.parse(result.contents[0].text);
expect(Array.isArray(contracts)).toBe(true);
expect(contracts).toContainEqual(mockContractsCache['ESM3']);
});
it('should return a list of positions when no resource ID is provided', async () => {
// Mock tradovateRequest to return positions
const tradovateRequest = require('../src/auth.js').tradovateRequest;
tradovateRequest.mockImplementation((method, endpoint) => {
if (endpoint === 'position/list') {
return Promise.resolve([mockPositionsCache['123']]);
}
return Promise.resolve(null);
});
// Get the handler for ReadResourceRequestSchema
const handler = handlers['ReadResourceRequestSchema'];
// Call the handler with a position URI without an ID
const result = await handler({
params: {
uri: 'tradovate/position'
}
});
// Assert
expect(result).toHaveProperty('contents');
expect(result.contents[0]).toHaveProperty('type', 'application/json');
// Parse the JSON text to check the content
const positions = JSON.parse(result.contents[0].text);
expect(Array.isArray(positions)).toBe(true);
expect(positions[0]).toEqual(mockPositionsCache['123']);
});
});
});