laplace-api 4.0.0 → 4.2.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,14 +1,148 @@
1
1
  import { Logger } from "winston";
2
2
  import { LaplaceConfiguration } from "../utilities/configuration";
3
- import { CollectionClient, Locale, Region } from "../client/collections";
3
+ import { CollectionClient, Locale, Region, Collection, CollectionDetail, CollectionType, CollectionPriceGraph } from "../client/collections";
4
4
  import "./client_test_suite";
5
5
  import { validateCollection, validateCollectionDetail } from "./helpers";
6
+ import { Stock, AssetType, PriceDataPoint, HistoricalPricePeriod } from "../client/stocks";
7
+
8
+ const mockStocks: Stock[] = [
9
+ {
10
+ id: "stock1",
11
+ name: "Tüpraş",
12
+ symbol: "TUPRS",
13
+ assetType: AssetType.Stock,
14
+ sectorId: "sector1",
15
+ industryId: "industry1",
16
+ updatedDate: "2024-03-14T10:00:00Z",
17
+ active: true
18
+ },
19
+ {
20
+ id: "stock2",
21
+ name: "Garanti Bankası",
22
+ symbol: "GARAN",
23
+ assetType: AssetType.Stock,
24
+ sectorId: "sector2",
25
+ industryId: "industry2",
26
+ updatedDate: "2024-03-14T10:00:00Z",
27
+ active: true
28
+ }
29
+ ];
30
+
31
+ const mockIndustries: Collection[] = [
32
+ {
33
+ id: "industry1",
34
+ title: "Bankacılık",
35
+ description: "Türkiye'nin önde gelen bankaları",
36
+ region: [Region.Tr],
37
+ assetClass: "equity",
38
+ imageUrl: "https://example.com/banking.jpg",
39
+ avatarUrl: "https://example.com/banking-avatar.jpg",
40
+ numStocks: 10
41
+ },
42
+ {
43
+ id: "industry2",
44
+ title: "Enerji",
45
+ description: "Enerji sektörü şirketleri",
46
+ region: [Region.Tr],
47
+ assetClass: "equity",
48
+ imageUrl: "https://example.com/energy.jpg",
49
+ avatarUrl: "https://example.com/energy-avatar.jpg",
50
+ numStocks: 8
51
+ }
52
+ ];
53
+
54
+ const mockSectors: Collection[] = [
55
+ {
56
+ id: "sector1",
57
+ title: "Finans",
58
+ description: "Finans sektörü şirketleri",
59
+ region: [Region.Tr],
60
+ assetClass: "equity",
61
+ imageUrl: "https://example.com/finance.jpg",
62
+ avatarUrl: "https://example.com/finance-avatar.jpg",
63
+ numStocks: 25
64
+ }
65
+ ];
66
+
67
+ const mockThemes: Collection[] = [
68
+ {
69
+ id: "theme1",
70
+ title: "Sürdürülebilir Enerji",
71
+ description: "Yenilenebilir enerji şirketleri",
72
+ region: [Region.Tr],
73
+ assetClass: "equity",
74
+ imageUrl: "https://example.com/sustainable.jpg",
75
+ avatarUrl: "https://example.com/sustainable-avatar.jpg",
76
+ numStocks: 15
77
+ }
78
+ ];
79
+
80
+ const mockCollections: Collection[] = [
81
+ {
82
+ id: "collection1",
83
+ title: "Borsa İstanbul 30",
84
+ description: "BIST-30 endeksindeki şirketler",
85
+ region: [Region.Tr],
86
+ assetClass: "equity",
87
+ imageUrl: "https://example.com/bist30.jpg",
88
+ avatarUrl: "https://example.com/bist30-avatar.jpg",
89
+ numStocks: 30
90
+ }
91
+ ];
92
+
93
+ const mockPriceDataPoints: PriceDataPoint[] = [
94
+ {
95
+ d: 1710403200000,
96
+ o: 100.5,
97
+ h: 105.2,
98
+ l: 99.8,
99
+ c: 103.7
100
+ },
101
+ {
102
+ d: 1710489600000,
103
+ o: 103.7,
104
+ h: 107.1,
105
+ l: 102.3,
106
+ c: 106.4
107
+ },
108
+ {
109
+ d: 1710576000000,
110
+ o: 106.4,
111
+ h: 108.9,
112
+ l: 105.1,
113
+ c: 107.8
114
+ }
115
+ ];
116
+
117
+ const mockCollectionPriceGraph: CollectionPriceGraph = {
118
+ previous_close: 98.5,
119
+ graph: mockPriceDataPoints
120
+ };
121
+
122
+ const mockIndustryDetail: CollectionDetail = {
123
+ ...mockIndustries[0],
124
+ stocks: mockStocks
125
+ };
126
+
127
+ const mockSectorDetail: CollectionDetail = {
128
+ ...mockSectors[0],
129
+ stocks: mockStocks
130
+ };
131
+
132
+ const mockThemeDetail: CollectionDetail = {
133
+ ...mockThemes[0],
134
+ stocks: mockStocks
135
+ };
136
+
137
+ const mockCollectionDetail: CollectionDetail = {
138
+ ...mockCollections[0],
139
+ stocks: mockStocks
140
+ };
6
141
 
7
142
  describe("Collections", () => {
8
143
  let client: CollectionClient;
9
144
 
10
145
  beforeAll(() => {
11
- // Assuming global.testSuite is set up as in the previous example
12
146
  const config = (global as any).testSuite.config as LaplaceConfiguration;
13
147
  const logger: Logger = {
14
148
  info: jest.fn(),
@@ -20,75 +154,326 @@ describe("Collections", () => {
20
154
  client = new CollectionClient(config, logger);
21
155
  });
22
156
 
23
- test("GetAllIndustries", async () => {
24
- const resp = await client.getAllIndustries(Region.Tr, Locale.Tr);
25
- expect(resp).not.toBeEmpty();
157
+ describe("Integration Tests", () => {
158
+ test("GetAllIndustries", async () => {
159
+ const resp = await client.getAllIndustries(Region.Tr, Locale.Tr);
160
+ expect(resp).not.toBeEmpty();
26
161
 
27
- const firstIndustry = resp[0];
28
- validateCollection(firstIndustry);
29
- });
162
+ const firstIndustry = resp[0];
163
+ validateCollection(firstIndustry);
164
+ });
30
165
 
31
- test("GetAllSectors", async () => {
32
- const resp = await client.getAllSectors(Region.Tr, Locale.Tr);
33
- expect(resp).not.toBeEmpty();
166
+ test("GetAllSectors", async () => {
167
+ const resp = await client.getAllSectors(Region.Tr, Locale.Tr);
168
+ expect(resp).not.toBeEmpty();
34
169
 
35
- const firstSector = resp[0];
36
- validateCollection(firstSector);
37
- });
170
+ const firstSector = resp[0];
171
+ validateCollection(firstSector);
172
+ });
38
173
 
39
- test("GetIndustryDetails", async () => {
40
- const resp = await client.getIndustryDetail(
41
- "65533e441fa5c7b58afa0944",
42
- Region.Tr,
43
- Locale.Tr
44
- );
45
- expect(resp).not.toBeEmpty();
46
- validateCollectionDetail(resp);
47
- });
174
+ test("GetIndustryDetails", async () => {
175
+ const resp = await client.getIndustryDetail(
176
+ "65533e441fa5c7b58afa0944",
177
+ Region.Tr,
178
+ Locale.Tr
179
+ );
180
+ expect(resp).not.toBeEmpty();
181
+ validateCollectionDetail(resp);
182
+ });
48
183
 
49
- test("GetSectorDetails", async () => {
50
- const resp = await client.getSectorDetail(
51
- "65533e047844ee7afe9941b9",
52
- Region.Tr,
53
- Locale.Tr
54
- );
55
- expect(resp).not.toBeEmpty();
56
- validateCollectionDetail(resp);
57
- });
184
+ test("GetSectorDetails", async () => {
185
+ const resp = await client.getSectorDetail(
186
+ "65533e047844ee7afe9941b9",
187
+ Region.Tr,
188
+ Locale.Tr
189
+ );
190
+ expect(resp).not.toBeEmpty();
191
+ validateCollectionDetail(resp);
192
+ });
58
193
 
59
- test("GetAllThemes", async () => {
60
- const resp = await client.getAllThemes(Region.Tr, Locale.Tr);
61
- expect(resp).not.toBeEmpty();
194
+ test("GetAllThemes", async () => {
195
+ const resp = await client.getAllThemes(Region.Tr, Locale.Tr);
196
+ expect(resp).not.toBeEmpty();
62
197
 
63
- const firstTheme = resp[0];
64
- validateCollection(firstTheme);
65
- });
198
+ const firstTheme = resp[0];
199
+ validateCollection(firstTheme);
200
+ });
66
201
 
67
- test("GetThemeDetails", async () => {
68
- const resp = await client.getThemeDetail(
69
- "6256b0647d0bb100123effa7",
70
- Region.Tr,
71
- Locale.Tr
72
- );
73
- expect(resp).not.toBeEmpty();
74
- validateCollectionDetail(resp);
75
- });
202
+ test("GetThemeDetails", async () => {
203
+ const resp = await client.getThemeDetail(
204
+ "6256b0647d0bb100123effa7",
205
+ Region.Tr,
206
+ Locale.Tr
207
+ );
208
+ expect(resp).not.toBeEmpty();
209
+ validateCollectionDetail(resp);
210
+ });
211
+
212
+ test("GetAllCollections", async () => {
213
+ const resp = await client.getAllCollections(Region.Tr, Locale.Tr);
214
+ expect(resp).not.toBeEmpty();
76
215
 
77
- test("GetAllCollections", async () => {
78
- const resp = await client.getAllCollections(Region.Tr, Locale.Tr);
79
- expect(resp).not.toBeEmpty();
216
+ const firstCollection = resp[0];
217
+ validateCollection(firstCollection);
218
+ });
80
219
 
81
- const firstCollection = resp[0];
82
- validateCollection(firstCollection);
220
+ test("GetCollectionDetails", async () => {
221
+ const resp = await client.getThemeDetail(
222
+ "620f455a0187ade00bb0d55f",
223
+ Region.Tr,
224
+ Locale.Tr
225
+ );
226
+ expect(resp).not.toBeEmpty();
227
+ validateCollectionDetail(resp);
228
+ });
229
+
230
+ test("GetAggregateGraph", async () => {
231
+ const resp = await client.getAggregateGraph(
232
+ HistoricalPricePeriod.OneYear,
233
+ "65533e047844ee7afe9941b9",
234
+ "65533e441fa5c7b58afa0944",
235
+ "",
236
+ Region.Tr
237
+ );
238
+
239
+ expect(resp).not.toBeNull();
240
+ expect(resp.previous_close).toBeDefined();
241
+ expect(resp.graph).toBeDefined();
242
+ expect(resp.graph).toBeInstanceOf(Array);
243
+
244
+ if (resp.graph.length > 0) {
245
+ const firstDataPoint = resp.graph[0];
246
+ expect(firstDataPoint.d).toBeDefined();
247
+ expect(firstDataPoint.o).toBeDefined();
248
+ expect(firstDataPoint.h).toBeDefined();
249
+ expect(firstDataPoint.l).toBeDefined();
250
+ expect(firstDataPoint.c).toBeDefined();
251
+ expect(typeof firstDataPoint.d).toBe('number');
252
+ expect(typeof firstDataPoint.o).toBe('number');
253
+ expect(typeof firstDataPoint.h).toBe('number');
254
+ expect(typeof firstDataPoint.l).toBe('number');
255
+ expect(typeof firstDataPoint.c).toBe('number');
256
+ }
257
+ });
83
258
  });
84
259
 
85
- test("GetCollectionDetails", async () => {
86
- const resp = await client.getThemeDetail(
87
- "620f455a0187ade00bb0d55f",
88
- Region.Tr,
89
- Locale.Tr
90
- );
91
- expect(resp).not.toBeEmpty();
92
- validateCollectionDetail(resp);
260
+ describe("Mock Tests", () => {
261
+ beforeEach(() => {
262
+ jest.clearAllMocks();
263
+ });
264
+
265
+ describe("Industries", () => {
266
+ test("should get all industries", async () => {
267
+ jest.spyOn(client, 'getAllIndustries').mockResolvedValue(mockIndustries);
268
+
269
+ const resp = await client.getAllIndustries(Region.Tr, Locale.Tr);
270
+
271
+ expect(resp).toHaveLength(2);
272
+ expect(resp[0].title).toBe("Bankacılık");
273
+ expect(resp[1].title).toBe("Enerji");
274
+ expect(resp[0].numStocks).toBe(10);
275
+ expect(resp[1].numStocks).toBe(8);
276
+
277
+ expect(client.getAllIndustries).toHaveBeenCalledWith(Region.Tr, Locale.Tr);
278
+ });
279
+
280
+ test("should get industry details", async () => {
281
+ jest.spyOn(client, 'getIndustryDetail').mockResolvedValue(mockIndustryDetail);
282
+
283
+ const resp = await client.getIndustryDetail("industry1", Region.Tr, Locale.Tr);
284
+
285
+ expect(resp.id).toBe("industry1");
286
+ expect(resp.title).toBe("Bankacılık");
287
+ expect(resp.stocks).toHaveLength(2);
288
+ expect(resp.stocks[0].symbol).toBe("TUPRS");
289
+ expect(resp.stocks[1].symbol).toBe("GARAN");
290
+
291
+ expect(client.getIndustryDetail).toHaveBeenCalledWith("industry1", Region.Tr, Locale.Tr);
292
+ });
293
+ });
294
+
295
+ describe("Sectors", () => {
296
+ test("should get all sectors", async () => {
297
+ jest.spyOn(client, 'getAllSectors').mockResolvedValue(mockSectors);
298
+
299
+ const resp = await client.getAllSectors(Region.Tr, Locale.Tr);
300
+
301
+ expect(resp).toHaveLength(1);
302
+ expect(resp[0].title).toBe("Finans");
303
+ expect(resp[0].numStocks).toBe(25);
304
+
305
+ expect(client.getAllSectors).toHaveBeenCalledWith(Region.Tr, Locale.Tr);
306
+ });
307
+
308
+ test("should get sector details", async () => {
309
+ jest.spyOn(client, 'getSectorDetail').mockResolvedValue(mockSectorDetail);
310
+
311
+ const resp = await client.getSectorDetail("sector1", Region.Tr, Locale.Tr);
312
+
313
+ expect(resp.id).toBe("sector1");
314
+ expect(resp.title).toBe("Finans");
315
+ expect(resp.stocks).toHaveLength(2);
316
+ expect(resp.stocks[0].symbol).toBe("TUPRS");
317
+ expect(resp.stocks[1].symbol).toBe("GARAN");
318
+
319
+ expect(client.getSectorDetail).toHaveBeenCalledWith("sector1", Region.Tr, Locale.Tr);
320
+ });
321
+ });
322
+
323
+ describe("Themes", () => {
324
+ test("should get all themes", async () => {
325
+ jest.spyOn(client, 'getAllThemes').mockResolvedValue(mockThemes);
326
+
327
+ const resp = await client.getAllThemes(Region.Tr, Locale.Tr);
328
+
329
+ expect(resp).toHaveLength(1);
330
+ expect(resp[0].title).toBe("Sürdürülebilir Enerji");
331
+ expect(resp[0].numStocks).toBe(15);
332
+
333
+ expect(client.getAllThemes).toHaveBeenCalledWith(Region.Tr, Locale.Tr);
334
+ });
335
+
336
+ test("should get theme details", async () => {
337
+ jest.spyOn(client, 'getThemeDetail').mockResolvedValue(mockThemeDetail);
338
+
339
+ const resp = await client.getThemeDetail("theme1", Region.Tr, Locale.Tr);
340
+
341
+ expect(resp.id).toBe("theme1");
342
+ expect(resp.title).toBe("Sürdürülebilir Enerji");
343
+ expect(resp.stocks).toHaveLength(2);
344
+ expect(resp.stocks[0].symbol).toBe("TUPRS");
345
+ expect(resp.stocks[1].symbol).toBe("GARAN");
346
+
347
+ expect(client.getThemeDetail).toHaveBeenCalledWith("theme1", Region.Tr, Locale.Tr);
348
+ });
349
+ });
350
+
351
+ describe("Collections", () => {
352
+ test("should get all collections", async () => {
353
+ jest.spyOn(client, 'getAllCollections').mockResolvedValue(mockCollections);
354
+
355
+ const resp = await client.getAllCollections(Region.Tr, Locale.Tr);
356
+
357
+ expect(resp).toHaveLength(1);
358
+ expect(resp[0].title).toBe("Borsa İstanbul 30");
359
+ expect(resp[0].numStocks).toBe(30);
360
+
361
+ expect(client.getAllCollections).toHaveBeenCalledWith(Region.Tr, Locale.Tr);
362
+ });
363
+
364
+ test("should get collection details", async () => {
365
+ jest.spyOn(client, 'getCollectionDetail').mockResolvedValue(mockCollectionDetail);
366
+
367
+ const resp = await client.getCollectionDetail("collection1", Region.Tr, Locale.Tr);
368
+
369
+ expect(resp.id).toBe("collection1");
370
+ expect(resp.title).toBe("Borsa İstanbul 30");
371
+ expect(resp.stocks).toHaveLength(2);
372
+ expect(resp.stocks[0].symbol).toBe("TUPRS");
373
+ expect(resp.stocks[1].symbol).toBe("GARAN");
374
+
375
+ expect(client.getCollectionDetail).toHaveBeenCalledWith("collection1", Region.Tr, Locale.Tr);
376
+ });
377
+ });
378
+
379
+ describe("Aggregate Graph", () => {
380
+ test("should get aggregate graph for sector", async () => {
381
+ jest.spyOn(client, 'getAggregateGraph').mockResolvedValue(mockCollectionPriceGraph);
382
+
383
+ const resp = await client.getAggregateGraph(HistoricalPricePeriod.OneMonth, "sector1", "", "", Region.Tr);
384
+
385
+ expect(resp.previous_close).toBe(98.5);
386
+ expect(resp.graph).toHaveLength(3);
387
+ expect(resp.graph[0].o).toBe(100.5);
388
+ expect(resp.graph[0].h).toBe(105.2);
389
+ expect(resp.graph[0].l).toBe(99.8);
390
+ expect(resp.graph[0].c).toBe(103.7);
391
+ expect(resp.graph[2].c).toBe(107.8);
392
+
393
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.OneMonth, "sector1", "", "", Region.Tr);
394
+ });
395
+
396
+ test("should get aggregate graph for industry", async () => {
397
+ jest.spyOn(client, 'getAggregateGraph').mockResolvedValue(mockCollectionPriceGraph);
398
+
399
+ const resp = await client.getAggregateGraph(HistoricalPricePeriod.ThreeMonth, "", "industry1", "", Region.Tr);
400
+
401
+ expect(resp.previous_close).toBe(98.5);
402
+ expect(resp.graph).toHaveLength(3);
403
+ expect(resp.graph[1].o).toBe(103.7);
404
+ expect(resp.graph[1].h).toBe(107.1);
405
+ expect(resp.graph[1].l).toBe(102.3);
406
+ expect(resp.graph[1].c).toBe(106.4);
407
+
408
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.ThreeMonth, "", "industry1", "", Region.Tr);
409
+ });
410
+
411
+ test("should get aggregate graph for collection", async () => {
412
+ jest.spyOn(client, 'getAggregateGraph').mockResolvedValue(mockCollectionPriceGraph);
413
+
414
+ const resp = await client.getAggregateGraph(HistoricalPricePeriod.OneWeek, "", "", "collection1", Region.Tr);
415
+
416
+ expect(resp.previous_close).toBe(98.5);
417
+ expect(resp.graph).toHaveLength(3);
418
+ expect(resp.graph[0].d).toBe(1710403200000);
419
+ expect(resp.graph[1].d).toBe(1710489600000);
420
+ expect(resp.graph[2].d).toBe(1710576000000);
421
+
422
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.OneWeek, "", "", "collection1", Region.Tr);
423
+ });
424
+
425
+ test("should get aggregate graph for different periods", async () => {
426
+ jest.spyOn(client, 'getAggregateGraph').mockResolvedValue(mockCollectionPriceGraph);
427
+
428
+ const resp1D = await client.getAggregateGraph(HistoricalPricePeriod.OneDay, "sector1", "", "", Region.Tr);
429
+ expect(resp1D.previous_close).toBe(98.5);
430
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.OneDay, "sector1", "", "", Region.Tr);
431
+
432
+ const resp1Y = await client.getAggregateGraph(HistoricalPricePeriod.OneYear, "", "industry1", "", Region.Tr);
433
+ expect(resp1Y.previous_close).toBe(98.5);
434
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.OneYear, "", "industry1", "", Region.Tr);
435
+
436
+ const resp5Y = await client.getAggregateGraph(HistoricalPricePeriod.FiveYear, "", "", "collection1", Region.Tr);
437
+ expect(resp5Y.previous_close).toBe(98.5);
438
+ expect(client.getAggregateGraph).toHaveBeenCalledWith(HistoricalPricePeriod.FiveYear, "", "", "collection1", Region.Tr);
439
+ });
440
+ });
441
+
442
+ describe("Error Handling", () => {
443
+ test("should handle invalid industry ID", async () => {
444
+ jest.spyOn(client, 'getIndustryDetail').mockRejectedValue(new Error("Industry not found"));
445
+
446
+ await expect(client.getIndustryDetail("invalid-id", Region.Tr, Locale.Tr))
447
+ .rejects.toThrow("Industry not found");
448
+ });
449
+
450
+ test("should handle invalid sector ID", async () => {
451
+ jest.spyOn(client, 'getSectorDetail').mockRejectedValue(new Error("Sector not found"));
452
+
453
+ await expect(client.getSectorDetail("invalid-id", Region.Tr, Locale.Tr))
454
+ .rejects.toThrow("Sector not found");
455
+ });
456
+
457
+ test("should handle invalid theme ID", async () => {
458
+ jest.spyOn(client, 'getThemeDetail').mockRejectedValue(new Error("Theme not found"));
459
+
460
+ await expect(client.getThemeDetail("invalid-id", Region.Tr, Locale.Tr))
461
+ .rejects.toThrow("Theme not found");
462
+ });
463
+
464
+ test("should handle invalid collection ID", async () => {
465
+ jest.spyOn(client, 'getCollectionDetail').mockRejectedValue(new Error("Collection not found"));
466
+
467
+ await expect(client.getCollectionDetail("invalid-id", Region.Tr, Locale.Tr))
468
+ .rejects.toThrow("Collection not found");
469
+ });
470
+
471
+ test("should handle invalid aggregate graph request", async () => {
472
+ jest.spyOn(client, 'getAggregateGraph').mockRejectedValue(new Error("Invalid parameters"));
473
+
474
+ await expect(client.getAggregateGraph("invalid-period" as HistoricalPricePeriod, "invalid-sector", "", "", Region.Tr))
475
+ .rejects.toThrow("Invalid parameters");
476
+ });
477
+ });
93
478
  });
94
479
  });