UNPKG

laplace-api

Version:

Client library for Laplace API for the US stock market and BIST (Istanbul stock market) fundamental financial data.

535 lines (465 loc) 17.2 kB
import { Logger } from "winston"; import { LaplaceConfiguration } from "../utilities/configuration"; import "./client_test_suite"; import { BISTBidAskStreamData, BISTStockStreamData, LivePriceClient, OrderbookLiveData } from "../client/live-price"; describe("LivePrice", () => { let client: LivePriceClient; let config: LaplaceConfiguration; let logger: Logger; let activeConnections: any[] = []; let activeTimeouts: NodeJS.Timeout[] = []; const TEST_CONSTANTS = { JEST_TIMEOUT: 15000, MAIN_TIMEOUT: 10000, }; beforeAll(async () => { config = (global as any).testSuite.config as LaplaceConfiguration; logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn(), } as unknown as Logger; client = new LivePriceClient(config, logger); }); afterEach(async () => { // Clear all active timeouts for (const timeout of activeTimeouts) { clearTimeout(timeout); } activeTimeouts = []; // Clean up all active connections for (const connection of activeConnections) { try { connection.close(); } catch (error) { console.log("Error closing connection:", error); } } activeConnections = []; }); afterAll(async () => { // Final cleanup for (const timeout of activeTimeouts) { clearTimeout(timeout); } for (const connection of activeConnections) { try { connection.close(); } catch (error) { console.log("Error closing connection in afterAll:", error); } } }); describe("GetLivePriceForBIST", () => { it( "should receive BIST live price data", async () => { const symbols = ["AKBNK"]; let receivedData: BISTStockStreamData | null = null; let receivedError: Error | null = null; const lc = client.getLivePriceForBIST(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); // Set a timeout to avoid hanging const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout waiting for data")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData = data; break; // Get first data and exit } } catch (error) { console.log("Error in data stream:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData) { const tempReceivedData = (receivedData as BISTStockStreamData).d; console.log("Received BIST data:", tempReceivedData); expect(tempReceivedData.s).toBeDefined(); expect(typeof tempReceivedData.s).toBe("string"); expect(typeof tempReceivedData.p).toBe("number"); expect(typeof tempReceivedData.ch).toBe("number"); expect(typeof tempReceivedData.d).toBe("number"); } else { console.log("Timeout waiting for BIST data"); } } catch (error) { receivedError = error as Error; console.log("Received error:", receivedError.message); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("GetLivePriceForUS", () => { it( "should receive US live price data", async () => { const symbols = ["AAPL"]; let receivedData: any = null; let receivedError: Error | null = null; const lc = client.getLivePriceForUS(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); // Set a timeout to avoid hanging const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout waiting for data")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData = data; break; // Get first data and exit } } catch (error) { console.log("Error in data stream:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData) { console.log("Received US data:", receivedData); expect(receivedData.s).toBeDefined(); expect(typeof receivedData.s).toBe("string"); expect(typeof receivedData.p).toBe("number"); expect(typeof receivedData.d).toBe("number"); } else { console.log("Timeout waiting for US data"); } } catch (error) { receivedError = error as Error; console.log("Received error:", receivedError.message); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("LivePriceSubscribe", () => { it( "should handle subscription changes", async () => { const initialSymbols = ["AKBNK"]; const newSymbols = ["TUPRS", "ASELS"]; const receivedData: string[] = []; let switchOccurred = false; const lc = client.getLivePriceForBIST(initialSymbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); // Start receiving data const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData.push(data.d.s); // Switch symbols after 5 seconds if (!switchOccurred && receivedData.length > 0) { const switchTimeout = setTimeout(async () => { try { await lc.subscribe(newSymbols); receivedData.push("SWITCH"); switchOccurred = true; // Close after another 5 seconds const closeTimeout = setTimeout(() => { lc.close(); }, 5000); activeTimeouts.push(closeTimeout); } catch (error) { console.error("Error switching symbols:", error); } }, 5000); activeTimeouts.push(switchTimeout); } } } catch (error) { console.log("Error in subscription test:", error); } })(); // Set overall timeout const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Test timeout")), TEST_CONSTANTS.JEST_TIMEOUT ); activeTimeouts.push(timeout); }); await Promise.race([dataPromise, timeoutPromise]); // Verify we received data expect(receivedData.length).toBeGreaterThan(0); const switchIndex = receivedData.indexOf("SWITCH"); if (switchIndex > 0) { const beforeSwitch = receivedData.slice(0, switchIndex); expect(beforeSwitch.some((symbol) => symbol === "AKBNK")).toBe( true ); } if (switchIndex >= 0 && switchIndex < receivedData.length - 1) { const afterSwitch = receivedData.slice(switchIndex + 1); expect(afterSwitch.some((symbol) => symbol === "TUPRS")).toBe(true); expect(afterSwitch.some((symbol) => symbol === "ASELS")).toBe(true); } } catch (error) { console.log("Test error:", error); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("LivePriceClose", () => { it( "should close connection properly", async () => { const symbols = ["AKBNK"]; const lc = client.getLivePriceForBIST(symbols); activeConnections.push(lc); try { // Close immediately lc.close(); // Try to receive data after close const receiveChan = lc.receive(); let receivedAfterClose = false; try { for await (const data of receiveChan) { receivedAfterClose = true; break; } } catch (error) { // Expected to throw after close console.log("Expected error after close:", error); } // Should not receive data after close expect(receivedAfterClose).toBe(false); } catch (error) { console.error("Close test error:", error); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("GetDelayedPriceForBIST", () => { it( "should receive BIST delayed price data", async () => { const symbols = ["AKBNK"]; let receivedData: BISTStockStreamData | null = null; let receivedError: Error | null = null; const lc = client.getDelayedPriceForBIST(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout waiting for delayed data")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData = data; break; } } catch (error) { console.log("Error in delayed data stream:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData != null) { const tempReceivedData = (receivedData as BISTStockStreamData).d; console.log("Received BIST delayed data:", tempReceivedData); expect(tempReceivedData.s).toBeDefined(); expect(typeof tempReceivedData.s).toBe("string"); expect(typeof tempReceivedData.p).toBe("number"); expect(typeof tempReceivedData.ch).toBe("number"); expect(typeof tempReceivedData.d).toBe("number"); } else { console.log("Timeout waiting for BIST delayed data"); } } catch (error) { receivedError = error as Error; console.log("Received delayed error:", receivedError.message); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("GetOrderbookForBIST", () => { it( "should receive BIST orderbook data", async () => { const symbols = ["AKBNK"]; let receivedData: OrderbookLiveData | null = null; let receivedError: Error | null = null; const lc = client.getOrderbookForBIST(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout waiting for orderbook data")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { console.log(data); receivedData = data; break; } } catch (error) { console.log("Error in orderbook data stream:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData != null) { const tempReceivedData = receivedData as OrderbookLiveData; console.log("Received BIST orderbook data:", tempReceivedData); expect(tempReceivedData.symbol).toBeDefined(); expect(typeof tempReceivedData.symbol).toBe("string"); if (tempReceivedData.updated != null) { expect(Array.isArray(tempReceivedData.updated)).toBe(true); if (tempReceivedData.updated.length > 0) { const firsData = tempReceivedData.updated[0]; console.log("updated first data:", firsData) expect(typeof firsData.level).toBe("number"); expect(typeof firsData.vol).toBe("number"); expect(typeof firsData.orders).toBe("number"); expect(typeof firsData.p).toBe("number"); expect(typeof firsData.side).toBe("string"); } } if (tempReceivedData.deleted != null) { expect(Array.isArray(tempReceivedData.deleted)).toBe(true); if (tempReceivedData.deleted.length > 0) { const firsData = tempReceivedData.deleted[0]; console.log("deleted first data:", firsData) expect(typeof firsData.level).toBe("number"); expect(typeof firsData.side).toBe("string"); } } } else { console.log("Timeout waiting for BIST orderbook data"); } } catch (error) { receivedError = error as Error; console.log("Received orderbook error:", receivedError.message); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("Client Methods", () => { it( "should work with client methods", async () => { const symbols = ["THYAO"]; let receivedData: any = null; const lc = client.getLivePriceForBIST(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData = data; break; } } catch (error) { console.log("Error in client methods test:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData) { expect(receivedData.s).toBeDefined(); expect(typeof receivedData.s).toBe("string"); } } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); describe("GetBidAskForBIST", () => { it( "should receive BIST bid/ask data", async () => { const symbols = ["AKBNK"]; let receivedData: BISTBidAskStreamData | null = null; let receivedError: Error | null = null; const lc = client.getBidAskForBIST(symbols); activeConnections.push(lc); try { const receiveChan = lc.receive(); const timeoutPromise = new Promise<void>((_, reject) => { const timeout = setTimeout( () => reject(new Error("Timeout waiting for bid/ask data")), TEST_CONSTANTS.MAIN_TIMEOUT ); activeTimeouts.push(timeout); }); const dataPromise = (async () => { try { for await (const data of receiveChan) { receivedData = data; break; } } catch (error) { console.log("Error in bid/ask data stream:", error); } })(); await Promise.race([dataPromise, timeoutPromise]); if (receivedData != null) { const tempReceivedData = (receivedData as BISTBidAskStreamData).d; console.log("Received BIST bid/ask data:", tempReceivedData); expect(tempReceivedData.s).toBeDefined(); expect(typeof tempReceivedData.s).toBe("string"); expect(typeof tempReceivedData.d).toBe("string"); expect(typeof tempReceivedData.ask).toBe("number"); expect(typeof tempReceivedData.bid).toBe("number"); } else { console.log("Timeout waiting for BIST bid/ask data"); } } catch (error) { receivedError = error as Error; console.log("Received bid/ask error:", receivedError.message); } finally { lc.close(); } }, TEST_CONSTANTS.JEST_TIMEOUT ); }); });