Skip to main content
Glama

Interactive Brokers MCP Server

by code-rabi
flex-query-client.test.ts12.8 kB
// test/flex-query-client.test.ts import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { FlexQueryClient } from '../src/flex-query-client.js'; import axios from 'axios'; // Mock axios vi.mock('axios'); describe('FlexQueryClient', () => { let client: FlexQueryClient; const mockToken = 'test-token-123'; const mockQueryId = '123456'; const mockReferenceCode = 'ref-code-789'; beforeEach(() => { vi.clearAllMocks(); client = new FlexQueryClient({ token: mockToken }); }); afterEach(() => { vi.restoreAllMocks(); }); describe('constructor', () => { it('should initialize with token', () => { expect(client).toBeDefined(); }); it('should create axios client with timeout', () => { expect(axios.create).toHaveBeenCalledWith({ timeout: 60000, }); }); }); describe('sendRequest', () => { it('should successfully send request and return reference code', async () => { const mockResponse = { data: `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Success</Status> <ReferenceCode>${mockReferenceCode}</ReferenceCode> <Url>https://example.com/statement</Url> </FlexStatementResponse>`, }; const mockGet = vi.fn().mockResolvedValue(mockResponse); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.sendRequest(mockQueryId); expect(mockGet).toHaveBeenCalledWith( 'https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.SendRequest', { params: { t: mockToken, q: mockQueryId, v: '3', }, } ); expect(result).toEqual({ referenceCode: mockReferenceCode, url: 'https://example.com/statement', }); }); it('should return error on failed request', async () => { const mockResponse = { data: `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Fail</Status> <ErrorCode>1001</ErrorCode> <ErrorMessage>Invalid token</ErrorMessage> </FlexStatementResponse>`, }; const mockGet = vi.fn().mockResolvedValue(mockResponse); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.sendRequest(mockQueryId); expect(result).toEqual({ error: 'Invalid token', errorCode: '1001', }); }); it('should handle network errors', async () => { const mockError = new Error('Network error'); Object.defineProperty(mockError, 'isAxiosError', { value: true }); const mockGet = vi.fn().mockRejectedValue(mockError); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); // Check the axios.isAxiosError before the test vi.spyOn(axios, 'isAxiosError').mockReturnValue(true); await expect(client.sendRequest(mockQueryId)).rejects.toThrow( 'Failed to send flex query request: Network error' ); }); it('should handle unexpected response format', async () => { const mockResponse = { data: `<?xml version="1.0" encoding="UTF-8"?> <UnexpectedFormat> <Data>Something</Data> </UnexpectedFormat>`, }; const mockGet = vi.fn().mockResolvedValue(mockResponse); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); await expect(client.sendRequest(mockQueryId)).rejects.toThrow( 'Unexpected response format from Flex Query service' ); }); }); describe('getStatement', () => { it('should successfully get statement data', async () => { const mockStatementData = `<?xml version="1.0" encoding="UTF-8"?> <FlexQueryResponse queryName="Test Query" type="AF"> <FlexStatements count="1"> <FlexStatement accountId="U12345" fromDate="2023-01-01" toDate="2023-12-31"> <AccountInformation> <EquitySummaryByReportDateInBase accountId="U12345" acctAlias="Test" /> </AccountInformation> </FlexStatement> </FlexStatements> </FlexQueryResponse>`; const mockResponse = { data: mockStatementData, }; const mockGet = vi.fn().mockResolvedValue(mockResponse); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.getStatement(mockReferenceCode); expect(mockGet).toHaveBeenCalledWith( 'https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.GetStatement', { params: { t: mockToken, q: mockReferenceCode, v: '3', }, } ); expect(result).toEqual({ data: mockStatementData, }); }); it('should return error when statement not ready', async () => { const mockResponse = { data: `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 02:00 PM EDT"> <Status>Fail</Status> <ErrorCode>1019</ErrorCode> <ErrorMessage>Statement generation in progress. Please try again shortly.</ErrorMessage> </FlexStatementResponse>`, }; const mockGet = vi.fn().mockResolvedValue(mockResponse); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.getStatement(mockReferenceCode); expect(result).toEqual({ error: 'Statement generation in progress. Please try again shortly.', errorCode: '1019', }); }); it('should handle network errors', async () => { const mockError = new Error('Network timeout'); Object.defineProperty(mockError, 'isAxiosError', { value: true }); const mockGet = vi.fn().mockRejectedValue(mockError); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); vi.spyOn(axios, 'isAxiosError').mockReturnValue(true); await expect(client.getStatement(mockReferenceCode)).rejects.toThrow( 'Failed to get flex statement: Network timeout' ); }); }); describe('executeQuery', () => { it('should execute query and wait for statement', async () => { const sendResponseXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Success</Status> <ReferenceCode>${mockReferenceCode}</ReferenceCode> <Url>https://example.com/statement</Url> </FlexStatementResponse>`; const statementXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexQueryResponse queryName="Test Query" type="AF"> <FlexStatements count="1"> <FlexStatement accountId="U12345" fromDate="2023-01-01" toDate="2023-12-31"> </FlexStatement> </FlexStatements> </FlexQueryResponse>`; const mockGet = vi.fn() .mockResolvedValueOnce({ data: sendResponseXml }) .mockResolvedValueOnce({ data: statementXml }); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.executeQuery(mockQueryId, 3, 100); expect(result).toEqual({ data: statementXml, }); expect(mockGet).toHaveBeenCalledTimes(2); }); it('should retry when statement is not ready', async () => { const sendResponseXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Success</Status> <ReferenceCode>${mockReferenceCode}</ReferenceCode> </FlexStatementResponse>`; const notReadyXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 02:00 PM EDT"> <Status>Fail</Status> <ErrorCode>1019</ErrorCode> <ErrorMessage>Statement generation in progress</ErrorMessage> </FlexStatementResponse>`; const statementXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexQueryResponse queryName="Test Query" type="AF"> <FlexStatements count="1"> <FlexStatement accountId="U12345" /> </FlexStatements> </FlexQueryResponse>`; const mockGet = vi.fn() .mockResolvedValueOnce({ data: sendResponseXml }) .mockResolvedValueOnce({ data: notReadyXml }) .mockResolvedValueOnce({ data: statementXml }); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.executeQuery(mockQueryId, 3, 100); expect(result).toEqual({ data: statementXml, }); expect(mockGet).toHaveBeenCalledTimes(3); }); it('should return error when sendRequest fails', async () => { const sendResponseXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Fail</Status> <ErrorCode>1001</ErrorCode> <ErrorMessage>Invalid query ID</ErrorMessage> </FlexStatementResponse>`; const mockGet = vi.fn().mockResolvedValueOnce({ data: sendResponseXml }); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.executeQuery(mockQueryId); expect(result).toEqual({ error: 'Invalid query ID', errorCode: '1001', }); }); it('should timeout after max retries', async () => { const sendResponseXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Success</Status> <ReferenceCode>${mockReferenceCode}</ReferenceCode> </FlexStatementResponse>`; const notReadyXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 02:00 PM EDT"> <Status>Fail</Status> <ErrorCode>1019</ErrorCode> <ErrorMessage>Statement generation in progress</ErrorMessage> </FlexStatementResponse>`; const mockGet = vi.fn() .mockResolvedValueOnce({ data: sendResponseXml }) .mockResolvedValue({ data: notReadyXml }); // Always return not ready (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.executeQuery(mockQueryId, 2, 100); expect(result).toEqual({ error: 'Statement not ready after 2 retries. Please try again later.', }); }); it('should return error when no reference code received', async () => { const sendResponseXml = `<?xml version="1.0" encoding="UTF-8"?> <FlexStatementResponse timestamp="26 August, 2023 01:59 PM EDT"> <Status>Success</Status> </FlexStatementResponse>`; const mockGet = vi.fn().mockResolvedValueOnce({ data: sendResponseXml }); (axios.create as any).mockReturnValue({ get: mockGet }); client = new FlexQueryClient({ token: mockToken }); const result = await client.executeQuery(mockQueryId); expect(result).toEqual({ error: 'No reference code received from flex query service', }); }); }); describe('parseStatement', () => { it('should parse XML statement into JSON', async () => { const xmlData = `<?xml version="1.0" encoding="UTF-8"?> <FlexQueryResponse queryName="Test Query" type="AF"> <FlexStatements count="1"> <FlexStatement accountId="U12345" fromDate="2023-01-01" toDate="2023-12-31"> <AccountInformation currency="USD" /> </FlexStatement> </FlexStatements> </FlexQueryResponse>`; const result = await client.parseStatement(xmlData); expect(result).toBeDefined(); expect(result.FlexQueryResponse).toBeDefined(); expect(result.FlexQueryResponse.queryName).toBe('Test Query'); expect(result.FlexQueryResponse.type).toBe('AF'); }); it('should handle parsing errors', async () => { const invalidXml = 'This is not valid XML'; await expect(client.parseStatement(invalidXml)).rejects.toThrow( 'Failed to parse flex statement XML' ); }); it('should parse XML with mergeAttrs option', async () => { const xmlData = `<?xml version="1.0" encoding="UTF-8"?> <FlexQueryResponse queryName="Test" type="AF"> <Data value="123" /> </FlexQueryResponse>`; const result = await client.parseStatement(xmlData); // mergeAttrs should merge attributes into the object expect(result.FlexQueryResponse.Data.value).toBe('123'); }); }); });

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/code-rabi/interactive-brokers-mcp'

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