laplace-api 3.1.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,115 +1,475 @@
1
1
  import { Logger } from "winston";
2
2
  import { LaplaceConfiguration } from "../utilities/configuration";
3
- import { Region } from "../client/collections";
4
3
  import "./client_test_suite";
5
- import { LivePriceClient } from "../client/live-price";
6
- import {
7
- BISTStockLiveData,
8
- LivePriceFeed,
9
- LivePriceWebSocketClient,
10
- USStockLiveData,
11
- } from "../client/live-price-web-socket";
4
+ import { BISTStockStreamData, LivePriceClient, OrderbookLiveData } from "../client/live-price";
12
5
 
13
6
  describe("LivePrice", () => {
14
- let livePriceUrlClient: LivePriceClient;
15
- let url: string;
16
- let ws: LivePriceWebSocketClient;
7
+ let client: LivePriceClient;
8
+ let config: LaplaceConfiguration;
9
+ let logger: Logger;
10
+ let activeConnections: any[] = [];
11
+ let activeTimeouts: NodeJS.Timeout[] = [];
17
12
 
18
13
  const TEST_CONSTANTS = {
19
- JEST_TIMEOUT: 30000,
20
- MAIN_TIMEOUT: 25000,
14
+ JEST_TIMEOUT: 15000,
15
+ MAIN_TIMEOUT: 10000,
21
16
  };
22
17
 
23
18
  beforeAll(async () => {
24
- const config = (global as any).testSuite.config as LaplaceConfiguration;
25
- const logger: Logger = {
19
+ config = (global as any).testSuite.config as LaplaceConfiguration;
20
+ logger = {
26
21
  info: jest.fn(),
27
22
  error: jest.fn(),
28
23
  warn: jest.fn(),
29
24
  debug: jest.fn(),
30
25
  } as unknown as Logger;
31
26
 
32
- livePriceUrlClient = new LivePriceClient(config, logger);
33
- url = await livePriceUrlClient.getWebSocketUrl("2459", [
34
- LivePriceFeed.LiveBist,
35
- ]);
27
+ client = new LivePriceClient(config, logger);
28
+ });
36
29
 
37
- ws = new LivePriceWebSocketClient({
38
- enableLogging: true,
39
- });
30
+ afterEach(async () => {
31
+ // Clear all active timeouts
32
+ for (const timeout of activeTimeouts) {
33
+ clearTimeout(timeout);
34
+ }
35
+ activeTimeouts = [];
40
36
 
41
- await ws.connect(url);
37
+ // Clean up all active connections
38
+ for (const connection of activeConnections) {
39
+ try {
40
+ connection.close();
41
+ } catch (error) {
42
+ console.log("Error closing connection:", error);
43
+ }
44
+ }
45
+ activeConnections = [];
42
46
  });
43
47
 
44
48
  afterAll(async () => {
45
- try {
46
- await ws.close();
47
- } catch (error) {
48
- console.error("Error closing websocket connection", error);
49
+ // Final cleanup
50
+ for (const timeout of activeTimeouts) {
51
+ clearTimeout(timeout);
52
+ }
53
+ for (const connection of activeConnections) {
54
+ try {
55
+ connection.close();
56
+ } catch (error) {
57
+ console.log("Error closing connection in afterAll:", error);
58
+ }
49
59
  }
50
60
  });
51
61
 
52
- describe("BIST Live Price Tests", () => {
53
- const symbols = ["TUPRS", "SASA", "THYAO", "GARAN", "YKBNK"];
62
+ describe("GetLivePriceForBIST", () => {
63
+ it(
64
+ "should receive BIST live price data",
65
+ async () => {
66
+ const symbols = ["AKBNK"];
67
+ let receivedData: BISTStockStreamData | null = null;
68
+ let receivedError: Error | null = null;
69
+
70
+ const lc = client.getLivePriceForBIST(symbols);
71
+ activeConnections.push(lc);
72
+
73
+ try {
74
+ const receiveChan = lc.receive();
75
+
76
+ // Set a timeout to avoid hanging
77
+ const timeoutPromise = new Promise<void>((_, reject) => {
78
+ const timeout = setTimeout(
79
+ () => reject(new Error("Timeout waiting for data")),
80
+ TEST_CONSTANTS.MAIN_TIMEOUT
81
+ );
82
+ activeTimeouts.push(timeout);
83
+ });
84
+
85
+ const dataPromise = (async () => {
86
+ try {
87
+ for await (const data of receiveChan) {
88
+ receivedData = data;
89
+ break; // Get first data and exit
90
+ }
91
+ } catch (error) {
92
+ console.log("Error in data stream:", error);
93
+ }
94
+ })();
95
+
96
+ await Promise.race([dataPromise, timeoutPromise]);
54
97
 
98
+ if (receivedData) {
99
+ const tempReceivedData = (receivedData as BISTStockStreamData).d;
100
+ console.log("Received BIST data:", tempReceivedData);
101
+ expect(tempReceivedData.s).toBeDefined();
102
+ expect(typeof tempReceivedData.s).toBe("string");
103
+ expect(typeof tempReceivedData.p).toBe("number");
104
+ expect(typeof tempReceivedData.ch).toBe("number");
105
+ expect(typeof tempReceivedData.d).toBe("number");
106
+ } else {
107
+ console.log("Timeout waiting for BIST data");
108
+ }
109
+ } catch (error) {
110
+ receivedError = error as Error;
111
+ console.log("Received error:", receivedError.message);
112
+ } finally {
113
+ lc.close();
114
+ }
115
+ },
116
+ TEST_CONSTANTS.JEST_TIMEOUT
117
+ );
118
+ });
119
+
120
+ describe("GetLivePriceForUS", () => {
55
121
  it(
56
- "should receive data for initial and updated symbols",
122
+ "should receive US live price data",
57
123
  async () => {
58
- const receivedData: BISTStockLiveData[] = [];
59
-
60
- let unsubscribe: (() => void) | null =
61
- ws.subscribe<LivePriceFeed.LiveBist>(
62
- symbols,
63
- LivePriceFeed.LiveBist,
64
- (data) => {
65
- console.log("RECEIVED DATA", data);
66
- receivedData.push(data);
124
+ const symbols = ["AAPL"];
125
+ let receivedData: any = null;
126
+ let receivedError: Error | null = null;
127
+
128
+ const lc = client.getLivePriceForUS(symbols);
129
+ activeConnections.push(lc);
130
+
131
+ try {
132
+ const receiveChan = lc.receive();
133
+
134
+ // Set a timeout to avoid hanging
135
+ const timeoutPromise = new Promise<void>((_, reject) => {
136
+ const timeout = setTimeout(
137
+ () => reject(new Error("Timeout waiting for data")),
138
+ TEST_CONSTANTS.MAIN_TIMEOUT
139
+ );
140
+ activeTimeouts.push(timeout);
141
+ });
142
+
143
+ const dataPromise = (async () => {
144
+ try {
145
+ for await (const data of receiveChan) {
146
+ receivedData = data;
147
+ break; // Get first data and exit
148
+ }
149
+ } catch (error) {
150
+ console.log("Error in data stream:", error);
67
151
  }
68
- );
152
+ })();
69
153
 
70
- await new Promise((resolve) => setTimeout(resolve, 20000));
154
+ await Promise.race([dataPromise, timeoutPromise]);
71
155
 
72
- for (const symbol of symbols) {
73
- const symbolData = receivedData.filter(
74
- (data) => data.symbol === symbol
75
- );
76
- expect(symbolData.length).toBeGreaterThan(0);
156
+ if (receivedData) {
157
+ console.log("Received US data:", receivedData);
158
+ expect(receivedData.s).toBeDefined();
159
+ expect(typeof receivedData.s).toBe("string");
160
+ expect(typeof receivedData.p).toBe("number");
161
+ expect(typeof receivedData.d).toBe("number");
162
+ } else {
163
+ console.log("Timeout waiting for US data");
164
+ }
165
+ } catch (error) {
166
+ receivedError = error as Error;
167
+ console.log("Received error:", receivedError.message);
168
+ } finally {
169
+ lc.close();
77
170
  }
171
+ },
172
+ TEST_CONSTANTS.JEST_TIMEOUT
173
+ );
174
+ });
175
+
176
+ describe("LivePriceSubscribe", () => {
177
+ it(
178
+ "should handle subscription changes",
179
+ async () => {
180
+ const initialSymbols = ["AKBNK"];
181
+ const newSymbols = ["TUPRS", "ASELS"];
182
+ const receivedData: string[] = [];
183
+ let switchOccurred = false;
184
+
185
+ const lc = client.getLivePriceForBIST(initialSymbols);
186
+ activeConnections.push(lc);
187
+
188
+ try {
189
+ const receiveChan = lc.receive();
190
+
191
+ // Start receiving data
192
+ const dataPromise = (async () => {
193
+ try {
194
+ for await (const data of receiveChan) {
195
+ receivedData.push(data.d.s);
196
+
197
+ // Switch symbols after 5 seconds
198
+ if (!switchOccurred && receivedData.length > 0) {
199
+ const switchTimeout = setTimeout(async () => {
200
+ try {
201
+ await lc.subscribe(newSymbols);
202
+ receivedData.push("SWITCH");
203
+ switchOccurred = true;
204
+
205
+ // Close after another 5 seconds
206
+ const closeTimeout = setTimeout(() => {
207
+ lc.close();
208
+ }, 5000);
209
+ activeTimeouts.push(closeTimeout);
210
+ } catch (error) {
211
+ console.error("Error switching symbols:", error);
212
+ }
213
+ }, 5000);
214
+ activeTimeouts.push(switchTimeout);
215
+ }
216
+ }
217
+ } catch (error) {
218
+ console.log("Error in subscription test:", error);
219
+ }
220
+ })();
78
221
 
79
- unsubscribe();
222
+ // Set overall timeout
223
+ const timeoutPromise = new Promise<void>((_, reject) => {
224
+ const timeout = setTimeout(
225
+ () => reject(new Error("Test timeout")),
226
+ TEST_CONSTANTS.JEST_TIMEOUT
227
+ );
228
+ activeTimeouts.push(timeout);
229
+ });
230
+
231
+ await Promise.race([dataPromise, timeoutPromise]);
232
+
233
+ // Verify we received data
234
+ expect(receivedData.length).toBeGreaterThan(0);
235
+
236
+ const switchIndex = receivedData.indexOf("SWITCH");
237
+ if (switchIndex > 0) {
238
+ const beforeSwitch = receivedData.slice(0, switchIndex);
239
+ expect(beforeSwitch.some((symbol) => symbol === "AKBNK")).toBe(
240
+ true
241
+ );
242
+ }
243
+
244
+ if (switchIndex >= 0 && switchIndex < receivedData.length - 1) {
245
+ const afterSwitch = receivedData.slice(switchIndex + 1);
246
+ expect(afterSwitch.some((symbol) => symbol === "TUPRS")).toBe(true);
247
+ expect(afterSwitch.some((symbol) => symbol === "ASELS")).toBe(true);
248
+ }
249
+ } catch (error) {
250
+ console.log("Test error:", error);
251
+ } finally {
252
+ lc.close();
253
+ }
80
254
  },
81
255
  TEST_CONSTANTS.JEST_TIMEOUT
82
256
  );
83
257
  });
84
258
 
85
- describe("US Live Price Tests", () => {
86
- const symbols = ["AAPL"];
259
+ describe("LivePriceClose", () => {
260
+ it(
261
+ "should close connection properly",
262
+ async () => {
263
+ const symbols = ["AKBNK"];
264
+ const lc = client.getLivePriceForBIST(symbols);
265
+ activeConnections.push(lc);
266
+
267
+ try {
268
+ // Close immediately
269
+ lc.close();
270
+
271
+ // Try to receive data after close
272
+ const receiveChan = lc.receive();
273
+ let receivedAfterClose = false;
274
+
275
+ try {
276
+ for await (const data of receiveChan) {
277
+ receivedAfterClose = true;
278
+ break;
279
+ }
280
+ } catch (error) {
281
+ // Expected to throw after close
282
+ console.log("Expected error after close:", error);
283
+ }
87
284
 
285
+ // Should not receive data after close
286
+ expect(receivedAfterClose).toBe(false);
287
+ } catch (error) {
288
+ console.error("Close test error:", error);
289
+ }
290
+ },
291
+ TEST_CONSTANTS.JEST_TIMEOUT
292
+ );
293
+ });
294
+
295
+ describe("GetDelayedPriceForBIST", () => {
88
296
  it(
89
- "should receive data for initial and updated symbols for us",
297
+ "should receive BIST delayed price data",
90
298
  async () => {
91
- const receivedData: USStockLiveData[] = [];
92
-
93
- let unsubscribe: (() => void) | null =
94
- ws.subscribe<LivePriceFeed.LiveUs>(
95
- symbols,
96
- LivePriceFeed.LiveUs,
97
- (data) => {
98
- console.log("RECEIVED DATA FOR US", data);
99
- receivedData.push(data);
299
+ const symbols = ["AKBNK"];
300
+ let receivedData: BISTStockStreamData | null = null;
301
+ let receivedError: Error | null = null;
302
+
303
+ const lc = client.getDelayedPriceForBIST(symbols);
304
+ activeConnections.push(lc);
305
+
306
+ try {
307
+ const receiveChan = lc.receive();
308
+
309
+ const timeoutPromise = new Promise<void>((_, reject) => {
310
+ const timeout = setTimeout(
311
+ () => reject(new Error("Timeout waiting for delayed data")),
312
+ TEST_CONSTANTS.MAIN_TIMEOUT
313
+ );
314
+ activeTimeouts.push(timeout);
315
+ });
316
+
317
+ const dataPromise = (async () => {
318
+ try {
319
+ for await (const data of receiveChan) {
320
+ receivedData = data;
321
+ break;
322
+ }
323
+ } catch (error) {
324
+ console.log("Error in delayed data stream:", error);
100
325
  }
101
- );
326
+ })();
102
327
 
103
- await new Promise((resolve) => setTimeout(resolve, 20000));
328
+ await Promise.race([dataPromise, timeoutPromise]);
104
329
 
105
- for (const symbol of symbols) {
106
- const symbolData = receivedData.filter(
107
- (data) => data.symbol === symbol
108
- );
109
- expect(symbolData.length).toBeGreaterThan(0);
330
+ if (receivedData != null) {
331
+ const tempReceivedData = (receivedData as BISTStockStreamData).d;
332
+ console.log("Received BIST delayed data:", tempReceivedData);
333
+ expect(tempReceivedData.s).toBeDefined();
334
+ expect(typeof tempReceivedData.s).toBe("string");
335
+ expect(typeof tempReceivedData.p).toBe("number");
336
+ expect(typeof tempReceivedData.ch).toBe("number");
337
+ expect(typeof tempReceivedData.d).toBe("number");
338
+ } else {
339
+ console.log("Timeout waiting for BIST delayed data");
340
+ }
341
+ } catch (error) {
342
+ receivedError = error as Error;
343
+ console.log("Received delayed error:", receivedError.message);
344
+ } finally {
345
+ lc.close();
110
346
  }
347
+ },
348
+ TEST_CONSTANTS.JEST_TIMEOUT
349
+ );
350
+ });
351
+
352
+ describe("GetOrderbookForBIST", () => {
353
+ it(
354
+ "should receive BIST orderbook data",
355
+ async () => {
356
+ const symbols = ["AKBNK"];
357
+ let receivedData: OrderbookLiveData | null = null;
358
+ let receivedError: Error | null = null;
359
+
360
+ const lc = client.getOrderbookForBIST(symbols);
361
+ activeConnections.push(lc);
362
+
363
+ try {
364
+ const receiveChan = lc.receive();
365
+
366
+ const timeoutPromise = new Promise<void>((_, reject) => {
367
+ const timeout = setTimeout(
368
+ () => reject(new Error("Timeout waiting for orderbook data")),
369
+ TEST_CONSTANTS.MAIN_TIMEOUT
370
+ );
371
+ activeTimeouts.push(timeout);
372
+ });
373
+
374
+ const dataPromise = (async () => {
375
+ try {
376
+ for await (const data of receiveChan) {
377
+ console.log(data);
378
+ receivedData = data;
379
+ break;
380
+ }
381
+ } catch (error) {
382
+ console.log("Error in orderbook data stream:", error);
383
+ }
384
+ })();
385
+
386
+ await Promise.race([dataPromise, timeoutPromise]);
111
387
 
112
- unsubscribe();
388
+ if (receivedData != null) {
389
+ const tempReceivedData = receivedData as OrderbookLiveData;
390
+ console.log("Received BIST orderbook data:", tempReceivedData);
391
+ expect(tempReceivedData.symbol).toBeDefined();
392
+ expect(typeof tempReceivedData.symbol).toBe("string");
393
+
394
+ if (tempReceivedData.updated != null) {
395
+ expect(Array.isArray(tempReceivedData.updated)).toBe(true);
396
+
397
+ if (tempReceivedData.updated.length > 0) {
398
+ const firsData = tempReceivedData.updated[0];
399
+ console.log("updated first data:", firsData)
400
+ expect(typeof firsData.level).toBe("number");
401
+ expect(typeof firsData.vol).toBe("number");
402
+ expect(typeof firsData.orders).toBe("number");
403
+ expect(typeof firsData.p).toBe("number");
404
+ expect(typeof firsData.side).toBe("string");
405
+ }
406
+ }
407
+
408
+ if (tempReceivedData.deleted != null) {
409
+ expect(Array.isArray(tempReceivedData.deleted)).toBe(true);
410
+
411
+ if (tempReceivedData.deleted.length > 0) {
412
+ const firsData = tempReceivedData.deleted[0];
413
+ console.log("deleted first data:", firsData)
414
+ expect(typeof firsData.level).toBe("number");
415
+ expect(typeof firsData.side).toBe("string");
416
+ }
417
+ }
418
+ } else {
419
+ console.log("Timeout waiting for BIST orderbook data");
420
+ }
421
+ } catch (error) {
422
+ receivedError = error as Error;
423
+ console.log("Received orderbook error:", receivedError.message);
424
+ } finally {
425
+ lc.close();
426
+ }
427
+ },
428
+ TEST_CONSTANTS.JEST_TIMEOUT
429
+ );
430
+ });
431
+
432
+ describe("Client Methods", () => {
433
+ it(
434
+ "should work with client methods",
435
+ async () => {
436
+ const symbols = ["THYAO"];
437
+ let receivedData: any = null;
438
+
439
+ const lc = client.getLivePriceForBIST(symbols);
440
+ activeConnections.push(lc);
441
+
442
+ try {
443
+ const receiveChan = lc.receive();
444
+
445
+ const timeoutPromise = new Promise<void>((_, reject) => {
446
+ const timeout = setTimeout(
447
+ () => reject(new Error("Timeout")),
448
+ TEST_CONSTANTS.MAIN_TIMEOUT
449
+ );
450
+ activeTimeouts.push(timeout);
451
+ });
452
+
453
+ const dataPromise = (async () => {
454
+ try {
455
+ for await (const data of receiveChan) {
456
+ receivedData = data;
457
+ break;
458
+ }
459
+ } catch (error) {
460
+ console.log("Error in client methods test:", error);
461
+ }
462
+ })();
463
+
464
+ await Promise.race([dataPromise, timeoutPromise]);
465
+
466
+ if (receivedData) {
467
+ expect(receivedData.s).toBeDefined();
468
+ expect(typeof receivedData.s).toBe("string");
469
+ }
470
+ } finally {
471
+ lc.close();
472
+ }
113
473
  },
114
474
  TEST_CONSTANTS.JEST_TIMEOUT
115
475
  );