Skip to main content
Glama

Tradovate MCP Server

const { describe, test, expect, beforeEach, afterEach, afterAll } = require('@jest/globals'); const EventEmitter = require('events'); // Import helper module with mock implementations const { MockWebSocket, createWebSocketMock, mockAuth, mockLogger, simulateWebSocketOpen, simulateWebSocketError, simulateWebSocketClose, simulateWebSocketMessage, simulateAuthentication } = require('./socket-helper'); // Mock the socket module dependencies before importing it jest.mock('../src/auth.js', () => mockAuth); jest.mock('../src/logger.js', () => mockLogger); // Mock the WebSocket implementation global.WebSocket = createWebSocketMock(); // Mock actual network calls in the connect and socket modules jest.mock('ws', () => createWebSocketMock()); // Create a simplified TradovateSocket mock for more deterministic tests const TradovateSocketMock = jest.fn().mockImplementation(({ debugLabel = 'test-socket' } = {}) => { let connected = false; let url = ''; const listeners = []; return { debugLabel, isConnected: () => connected, connect: jest.fn().mockImplementation((socketUrl, token) => { mockLogger.info(`Connecting to Tradovate WebSocket at ${socketUrl}...`); url = socketUrl; connected = true; return Promise.resolve(); }), disconnect: jest.fn().mockImplementation(() => { connected = false; mockLogger.info('Socket disconnected'); }), send: jest.fn().mockImplementation((options) => { if (!connected) { return Promise.reject(new Error('WebSocket is not connected')); } // Mock successful response return Promise.resolve({ s: 200, d: { result: 'success' } }); }), subscribe: jest.fn().mockImplementation((options) => { if (!connected) { return Promise.reject(new Error('WebSocket is not connected')); } if (!url.includes('md-demo.tradovateapi.com')) { return Promise.reject(new Error('Subscriptions are only available on market data sockets')); } return Promise.resolve({ id: 9999, symbol: options.symbol || 'ESM5' }); }), unsubscribe: jest.fn().mockImplementation((id) => { if (!connected) { return Promise.reject(new Error('WebSocket is not connected')); } return Promise.resolve(true); }), close: jest.fn().mockImplementation(() => { mockLogger.info(`Closing WebSocket connection...`); connected = false; url = ''; }), addListener: jest.fn().mockImplementation((listener) => { listeners.push(listener); return () => { const index = listeners.indexOf(listener); if (index !== -1) { listeners.splice(index, 1); } }; }) }; }); // Create factory functions that synchronously return socket mocks const createMarketDataSocketMock = jest.fn().mockImplementation(() => { const socket = new TradovateSocketMock({ debugLabel: 'md' }); socket.connect('wss://md-demo.tradovateapi.com/v1/websocket', 'test-token'); return socket; }); const createTradingSocketMock = jest.fn().mockImplementation((isLive = false) => { const url = isLive ? 'wss://live.tradovateapi.com/v1/websocket' : 'wss://demo.tradovateapi.com/v1/websocket'; const socket = new TradovateSocketMock({ debugLabel: isLive ? 'live' : 'demo' }); socket.connect(url, 'test-token'); return socket; }); // Mock the socket module exports directly for more reliable tests jest.mock('../src/socket.js', () => ({ TradovateSocket: TradovateSocketMock, createMarketDataSocket: createMarketDataSocketMock, createTradingSocket: createTradingSocketMock })); // Import the mocked module const socketModule = require('../src/socket.js'); describe('Socket Module Tests', () => { // Clean up before each test beforeEach(() => { jest.clearAllMocks(); }); // Clean up after all tests complete afterAll(() => { jest.useRealTimers(); }); describe('TradovateSocket class', () => { let socket; // Create a fresh socket instance for each test beforeEach(() => { socket = new TradovateSocketMock({ debugLabel: 'test-socket' }); }); test('should initialize with default properties', () => { expect(socket.isConnected()).toBe(false); }); test('should connect successfully', async () => { await socket.connect('wss://test.com', 'test-token'); expect(socket.isConnected()).toBe(true); expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Connecting to Tradovate WebSocket')); }); test('should send messages correctly', async () => { // First connect await socket.connect('wss://test.com', 'test-token'); expect(socket.isConnected()).toBe(true); // Test send method const sendOptions = { url: 'test/endpoint', body: { test: 'data' } }; const result = await socket.send(sendOptions); expect(result.s).toBe(200); expect(result.d).toEqual(expect.objectContaining({ result: 'success' })); }); test('should throw error when sending without connection', async () => { const sendOptions = { url: 'test/endpoint', body: { test: 'data' } }; await expect(socket.send(sendOptions)).rejects.toThrow('WebSocket is not connected'); }); test('should subscribe successfully', async () => { // Connect to market data URL await socket.connect('wss://md-demo.tradovateapi.com/v1/websocket', 'test-token'); // Test subscription const subscribeOptions = { symbol: 'ESM5', id: 12345 }; const subscription = await socket.subscribe(subscribeOptions); expect(subscription).toEqual(expect.objectContaining({ id: expect.any(Number), symbol: 'ESM5' })); }); test('should handle subscription with wrong URL', async () => { // Connect to trading URL instead of market data await socket.connect('wss://trading-demo.tradovateapi.com/v1/websocket', 'test-token'); // Test subscription - should fail on a trading socket const subscribeOptions = { symbol: 'ESM5', id: 12345 }; await expect(socket.subscribe(subscribeOptions)).rejects.toThrow(/market data/); }); test('should close the connection', async () => { // First connect await socket.connect('wss://test.com', 'test-token'); expect(socket.isConnected()).toBe(true); // Test close socket.close(); expect(socket.isConnected()).toBe(false); expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Closing WebSocket')); }); }); describe('createMarketDataSocket function', () => { test('should create and connect a market data socket', () => { const socket = socketModule.createMarketDataSocket(); expect(socket).toBeDefined(); expect(socket.isConnected()).toBe(true); expect(socket.debugLabel).toBe('md'); // Clean up socket.close(); }); }); describe('createTradingSocket function', () => { test('should create and connect a trading socket (demo)', () => { const socket = socketModule.createTradingSocket(false); expect(socket).toBeDefined(); expect(socket.isConnected()).toBe(true); expect(socket.debugLabel).toBe('demo'); // Clean up socket.close(); }); test('should create and connect a trading socket (live)', () => { const socket = socketModule.createTradingSocket(true); expect(socket).toBeDefined(); expect(socket.isConnected()).toBe(true); expect(socket.debugLabel).toBe('live'); // Clean up socket.close(); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alexanimal/tradovate-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server