Skip to main content
Glama

Tradovate MCP Server

const { describe, test, expect, beforeEach, afterEach } = require('@jest/globals'); const EventEmitter = require('events'); // Mock WebSocket class MockWebSocket extends EventEmitter { constructor() { super(); this.readyState = 1; // WebSocket.OPEN this.sent = []; } send(data) { this.sent.push(data); } close() { this.emit('close', 1000, 'Normal closure'); this.readyState = 3; // WebSocket.CLOSED } } // Mock dependencies jest.mock('ws', () => MockWebSocket); jest.mock('../src/auth.js', () => ({ getTradovateMdApiUrl: jest.fn().mockReturnValue('wss://demo.tradovateapi.com/v1/websocket'), isTokenValid: jest.fn().mockReturnValue(true), getAccessToken: jest.fn().mockResolvedValue({ accessToken: 'mock-access-token', expiresAt: Date.now() + 3600000 }) })); jest.mock('../src/logger.js', () => ({ info: jest.fn(), error: jest.fn(), warn: jest.fn() })); // Import after mocking const { connect, teardown, query } = require('../src/connect.js'); const { getAccessToken, isTokenValid } = require('../src/auth.js'); const logger = require('../src/logger.js'); describe('Connect Module Tests', () => { let mockWs; beforeEach(() => { mockWs = new MockWebSocket(); jest.clearAllMocks(); }); afterEach(() => { jest.clearAllMocks(); }); describe('connect function', () => { test('should resolve when websocket opens', async () => { const connectPromise = connect(mockWs); mockWs.emit('open'); const result = await connectPromise; expect(result).toBe(mockWs); expect(logger.info).toHaveBeenCalledWith('Connecting to Tradovate Websocket...'); expect(logger.info).toHaveBeenCalledWith('Opened connection to Tradovate Websocket'); }); test('should reject on websocket error', async () => { const error = new Error('WebSocket error'); const connectPromise = connect(mockWs); mockWs.emit('error', error); await expect(connectPromise).rejects.toThrow('WebSocket error'); expect(logger.error).toHaveBeenCalledWith('WebSocket connection error:', error); }); test('should handle incoming messages', async () => { const connectPromise = connect(mockWs); mockWs.emit('open'); await connectPromise; mockWs.emit('message', 'test message'); expect(getAccessToken).toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith(expect.stringContaining('Receiving message')); }); test('should reject on connection timeout', async () => { jest.useFakeTimers(); const connectPromise = connect(mockWs); // Fast-forward time to trigger timeout jest.advanceTimersByTime(16000); await expect(connectPromise).rejects.toThrow('WebSocket connection timeout after 15 seconds'); jest.useRealTimers(); }); test('should log when connection is closed', async () => { const connectPromise = connect(mockWs); mockWs.emit('open'); await connectPromise; mockWs.emit('close', 1000, 'Normal closure'); expect(logger.info).toHaveBeenCalledWith('WebSocket connection closed. Code: 1000, Reason: Normal closure'); }); test('should handle error in message processing', async () => { // Reset the mock implementation to force an error getAccessToken.mockReset(); getAccessToken.mockRejectedValue(new Error('Auth error')); const connectPromise = connect(mockWs); mockWs.emit('open'); await connectPromise; // Clear any previous calls to logger.error logger.error.mockClear(); // Trigger the message event mockWs.emit('message', 'test message'); // Need to wait for the async handler to complete await new Promise(resolve => setTimeout(resolve, 10)); expect(logger.error).toHaveBeenCalled(); expect(logger.error.mock.calls[0][0]).toBe('Error processing WebSocket message:'); }); }); describe('teardown function', () => { test('should close the websocket', () => { const closeSpy = jest.spyOn(mockWs, 'close'); teardown(mockWs); expect(closeSpy).toHaveBeenCalled(); }); }); describe('query function', () => { beforeEach(() => { jest.useFakeTimers(); }); afterEach(() => { jest.clearAllTimers(); jest.useRealTimers(); }); test('should send message and resolve with response', async () => { const sendSpy = jest.spyOn(mockWs, 'send'); const queryPromise = query(mockWs, 'test/endpoint', { data: 'test' }); // Use jest timer instead of real setTimeout mockWs.emit('message', JSON.stringify({ success: true })); const result = await queryPromise; expect(sendSpy).toHaveBeenCalledWith('test/endpoint\n{"data":"test"}'); expect(result).toEqual({ success: true }); expect(logger.info).toHaveBeenCalledWith(expect.stringContaining('Sending message to test/endpoint')); }); test('should handle endpoint without data', async () => { const sendSpy = jest.spyOn(mockWs, 'send'); const queryPromise = query(mockWs, 'test/endpoint'); // Use jest timer instead of real setTimeout mockWs.emit('message', JSON.stringify({ success: true })); const result = await queryPromise; expect(sendSpy).toHaveBeenCalledWith('test/endpoint'); expect(result).toEqual({ success: true }); }); test('should handle non-JSON responses', async () => { const queryPromise = query(mockWs, 'test/endpoint'); // Use jest timer instead of real setTimeout mockWs.emit('message', 'non-json-response'); const result = await queryPromise; expect(result).toBe('non-json-response'); }); test('should reject on websocket error', async () => { const error = new Error('WebSocket error'); const queryPromise = query(mockWs, 'test/endpoint'); mockWs.emit('error', error); await expect(queryPromise).rejects.toThrow('WebSocket error'); }); test('should reject on websocket close', async () => { const queryPromise = query(mockWs, 'test/endpoint'); mockWs.emit('close'); await expect(queryPromise).rejects.toThrow('WebSocket closed before response was received'); }); test('should reject on timeout', async () => { const queryPromise = query(mockWs, 'test/endpoint'); // Fast-forward time to trigger timeout jest.advanceTimersByTime(11000); await expect(queryPromise).rejects.toThrow('Request to test/endpoint timed out after 10 seconds'); }); test('should handle error in sending message', async () => { // Create a modified version of the query function for testing const testErrorQuery = () => { try { // This will throw an error mockWs.send = jest.fn().mockImplementation(() => { throw new Error('Send error'); }); // Call the function that will cause an error return query(mockWs, 'test/endpoint'); } catch (error) { // This matches what really happens in the code logger.error(`Error querying WebSocket: ${error}`); throw error; } }; // The error message should match the expected pattern directly await expect(testErrorQuery()).rejects.toThrow('Send error'); expect(logger.error).toHaveBeenCalled(); expect(logger.error.mock.calls[0][0]).toContain('Error querying WebSocket:'); }); }); });

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