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.
@@ -0,0 +1,317 @@
1
+ import { Logger } from "winston";
2
+ import { LaplaceConfiguration } from "../utilities/configuration";
3
+ import "./client_test_suite";
4
+ import {
5
+ FundsClient,
6
+ FundType,
7
+ HistoricalFundPricePeriod,
8
+ Fund,
9
+ FundStats,
10
+ FundDistribution,
11
+ FundAssetCategory,
12
+ FundHistoricalPrice,
13
+ FundContentType
14
+ } from "../client/funds";
15
+ import { Region } from "../client/collections";
16
+ import { AssetType } from "../client/stocks";
17
+
18
+ const mockFundsResponse: Fund[] = [
19
+ {
20
+ assetType: AssetType.Stock,
21
+ name: "Ak Portföy BIST 30 Endeksi Hisse Senedi Fonu",
22
+ symbol: "SPP",
23
+ active: true,
24
+ managementFee: 0.003,
25
+ riskLevel: 6,
26
+ fundType: FundType.STOCK_UMBRELLA_FUND,
27
+ ownerSymbol: "AKP"
28
+ },
29
+ {
30
+ assetType: AssetType.Stock,
31
+ name: "İş Portföy BIST 30 Endeksi Hisse Senedi Fonu",
32
+ symbol: "TI3",
33
+ active: true,
34
+ managementFee: 0.0035,
35
+ riskLevel: 6,
36
+ fundType: FundType.STOCK_UMBRELLA_FUND,
37
+ ownerSymbol: "IYP"
38
+ }
39
+ ];
40
+
41
+ const mockFundStatsResponse: FundStats = {
42
+ yearBeta: 0.95,
43
+ yearStdev: 0.12,
44
+ ytdReturn: 0.15,
45
+ yearMomentum: 0.08,
46
+ yearlyReturn: 0.25,
47
+ monthlyReturn: 0.03,
48
+ fiveYearReturn: 1.85,
49
+ sixMonthReturn: 0.18,
50
+ threeYearReturn: 0.95,
51
+ threeMonthReturn: 0.09
52
+ };
53
+
54
+ const mockFundDistributionResponse: FundDistribution = {
55
+ categories: [
56
+ {
57
+ category: FundAssetCategory.EQUITY,
58
+ percentage: 95.5,
59
+ assets: [
60
+ {
61
+ type: FundContentType.BIST_STOCK,
62
+ symbol: "GARAN",
63
+ wholePercentage: 9.8,
64
+ categoryPercentage: 10.26
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ category: FundAssetCategory.LIQUID_DEPOSIT,
70
+ percentage: 4.5
71
+ }
72
+ ]
73
+ };
74
+
75
+ const mockHistoricalPricesResponse: FundHistoricalPrice[] = [
76
+ {
77
+ price: 15.25,
78
+ aum: 1250000000,
79
+ date: "2024-03-14T10:00:00Z",
80
+ shareCount: 82000000,
81
+ investorCount: 25000
82
+ },
83
+ {
84
+ price: 15.15,
85
+ aum: 1245000000,
86
+ date: "2024-03-13T10:00:00Z",
87
+ shareCount: 82180000,
88
+ investorCount: 24950
89
+ }
90
+ ];
91
+
92
+ describe("Funds Client", () => {
93
+ let client: FundsClient;
94
+
95
+ beforeAll(() => {
96
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
97
+ const logger: Logger = {
98
+ info: jest.fn(),
99
+ error: jest.fn(),
100
+ warn: jest.fn(),
101
+ debug: jest.fn(),
102
+ } as unknown as Logger;
103
+
104
+ client = new FundsClient(config, logger);
105
+ });
106
+
107
+ describe("Integration Tests", () => {
108
+ describe("getFunds", () => {
109
+ test("should return funds list for TR region", async () => {
110
+ const resp = await client.getFunds(Region.Tr, 1, 10);
111
+
112
+ expect(resp).toBeDefined();
113
+ expect(Array.isArray(resp)).toBe(true);
114
+ expect(resp.length).toBeGreaterThan(0);
115
+
116
+ const fund = resp[0];
117
+ expect(typeof fund.symbol).toBe("string");
118
+ expect(typeof fund.name).toBe("string");
119
+ expect(typeof fund.assetType).toBe("string");
120
+ expect(typeof fund.managementFee).toBe("number");
121
+ expect(typeof fund.riskLevel).toBe("number");
122
+ expect(typeof fund.ownerSymbol).toBe("string");
123
+ expect(Object.values(FundType)).toContain(fund.fundType);
124
+ });
125
+
126
+ test("should handle pagination correctly", async () => {
127
+ const page1 = await client.getFunds(Region.Tr, 1, 5);
128
+ expect(page1.length).toBeLessThanOrEqual(5);
129
+ });
130
+ });
131
+
132
+ describe("getFundStats", () => {
133
+ test("should return fund statistics", async () => {
134
+ const resp = await client.getFundStats("SPP", Region.Tr);
135
+
136
+ expect(resp).toBeDefined();
137
+ expect(typeof resp.yearBeta).toBe("number");
138
+ expect(typeof resp.yearStdev).toBe("number");
139
+ expect(typeof resp.ytdReturn).toBe("number");
140
+ expect(typeof resp.yearMomentum).toBe("number");
141
+ expect(typeof resp.yearlyReturn).toBe("number");
142
+ expect(typeof resp.monthlyReturn).toBe("number");
143
+ expect(typeof resp.fiveYearReturn).toBe("number");
144
+ expect(typeof resp.sixMonthReturn).toBe("number");
145
+ expect(typeof resp.threeYearReturn).toBe("number");
146
+ expect(typeof resp.threeMonthReturn).toBe("number");
147
+ });
148
+
149
+ test("should handle invalid fund symbol", async () => {
150
+ await expect(
151
+ client.getFundStats("INVALID_FUND", Region.Tr)
152
+ ).rejects.toThrow();
153
+ });
154
+ });
155
+
156
+ describe("getFundDistribution", () => {
157
+ test("should return fund asset distribution", async () => {
158
+ const resp = await client.getFundDistribution("SPP", Region.Tr);
159
+
160
+ expect(resp).toBeDefined();
161
+ expect(Array.isArray(resp.categories)).toBe(true);
162
+
163
+ if (resp.categories.length > 0) {
164
+ const distribution = resp.categories[0];
165
+ expect(distribution.category).toBeDefined();
166
+ expect(typeof distribution.percentage).toBe("number");
167
+ }
168
+ });
169
+ });
170
+
171
+ describe("getHistoricalFundPrices", () => {
172
+ test("should return historical prices", async () => {
173
+ const resp = await client.getHistoricalFundPrices(
174
+ "SPP",
175
+ Region.Tr,
176
+ HistoricalFundPricePeriod.OneMonth
177
+ );
178
+
179
+ expect(resp).toBeDefined();
180
+ expect(Array.isArray(resp)).toBe(true);
181
+ expect(resp.length).toBeGreaterThan(0);
182
+
183
+ const pricePoint = resp[0];
184
+ expect(typeof pricePoint.price).toBe("number");
185
+ expect(typeof pricePoint.aum).toBe("number");
186
+ expect(pricePoint.date).toBeDefined();
187
+ expect(typeof pricePoint.shareCount).toBe("number");
188
+ expect(typeof pricePoint.investorCount).toBe("number");
189
+ });
190
+ });
191
+ });
192
+
193
+ describe("Mock Tests", () => {
194
+ beforeEach(() => {
195
+ jest.clearAllMocks();
196
+ });
197
+
198
+ describe("getFunds", () => {
199
+ test("should return funds list with mock data", async () => {
200
+ jest.spyOn(client, 'getFunds').mockResolvedValue(mockFundsResponse);
201
+
202
+ const resp = await client.getFunds(Region.Tr, 1, 10);
203
+
204
+ expect(resp).toHaveLength(2);
205
+
206
+ const firstFund = resp[0];
207
+ expect(firstFund.symbol).toBe("SPP");
208
+ expect(firstFund.name).toBe("Ak Portföy BIST 30 Endeksi Hisse Senedi Fonu");
209
+ expect(firstFund.assetType).toBe(AssetType.Stock);
210
+ expect(firstFund.managementFee).toBe(0.003);
211
+ expect(firstFund.riskLevel).toBe(6);
212
+ expect(firstFund.fundType).toBe(FundType.STOCK_UMBRELLA_FUND);
213
+ expect(firstFund.ownerSymbol).toBe("AKP");
214
+
215
+ expect(client.getFunds).toHaveBeenCalledWith(Region.Tr, 1, 10);
216
+ });
217
+
218
+ test("should handle pagination with mock data", async () => {
219
+ jest.spyOn(client, 'getFunds').mockResolvedValue([mockFundsResponse[0]]);
220
+
221
+ const resp = await client.getFunds(Region.Tr, 1, 1);
222
+
223
+ expect(resp).toHaveLength(1);
224
+ expect(resp[0].symbol).toBe("SPP");
225
+
226
+ expect(client.getFunds).toHaveBeenCalledWith(Region.Tr, 1, 1);
227
+ });
228
+ });
229
+
230
+ describe("getFundStats", () => {
231
+ test("should return fund statistics with mock data", async () => {
232
+ jest.spyOn(client, 'getFundStats').mockResolvedValue(mockFundStatsResponse);
233
+
234
+ const resp = await client.getFundStats("SPP", Region.Tr);
235
+
236
+ expect(resp.yearBeta).toBe(0.95);
237
+ expect(resp.yearStdev).toBe(0.12);
238
+ expect(resp.ytdReturn).toBe(0.15);
239
+ expect(resp.yearMomentum).toBe(0.08);
240
+ expect(resp.yearlyReturn).toBe(0.25);
241
+ expect(resp.monthlyReturn).toBe(0.03);
242
+ expect(resp.fiveYearReturn).toBe(1.85);
243
+ expect(resp.sixMonthReturn).toBe(0.18);
244
+ expect(resp.threeYearReturn).toBe(0.95);
245
+ expect(resp.threeMonthReturn).toBe(0.09);
246
+
247
+ expect(client.getFundStats).toHaveBeenCalledWith("SPP", Region.Tr);
248
+ });
249
+
250
+ test("should handle invalid fund symbol error", async () => {
251
+ jest.spyOn(client, 'getFundStats').mockRejectedValue(new Error("Fund not found"));
252
+
253
+ await expect(client.getFundStats("INVALID_FUND", Region.Tr))
254
+ .rejects.toThrow("Fund not found");
255
+ });
256
+ });
257
+
258
+ describe("getFundDistribution", () => {
259
+ test("should return fund distribution with mock data", async () => {
260
+ jest.spyOn(client, 'getFundDistribution').mockResolvedValue(mockFundDistributionResponse);
261
+
262
+ const resp = await client.getFundDistribution("SPP", Region.Tr);
263
+
264
+ expect(resp.categories).toHaveLength(2);
265
+
266
+ const equityCategory = resp.categories[0];
267
+ expect(equityCategory.category).toBe(FundAssetCategory.EQUITY);
268
+ expect(equityCategory.percentage).toBe(95.5);
269
+ expect(equityCategory.assets).toBeDefined();
270
+ expect(equityCategory.assets![0].symbol).toBe("GARAN");
271
+
272
+ const liquidCategory = resp.categories[1];
273
+ expect(liquidCategory.category).toBe(FundAssetCategory.LIQUID_DEPOSIT);
274
+ expect(liquidCategory.percentage).toBe(4.5);
275
+
276
+ expect(client.getFundDistribution).toHaveBeenCalledWith("SPP", Region.Tr);
277
+ });
278
+ });
279
+
280
+ describe("getHistoricalFundPrices", () => {
281
+ test("should return historical prices with mock data", async () => {
282
+ jest.spyOn(client, 'getHistoricalFundPrices').mockResolvedValue(mockHistoricalPricesResponse);
283
+
284
+ const resp = await client.getHistoricalFundPrices(
285
+ "SPP",
286
+ Region.Tr,
287
+ HistoricalFundPricePeriod.OneMonth
288
+ );
289
+
290
+ expect(resp).toHaveLength(2);
291
+
292
+ const firstPrice = resp[0];
293
+ expect(firstPrice.price).toBe(15.25);
294
+ expect(firstPrice.aum).toBe(1250000000);
295
+ expect(firstPrice.date).toBe("2024-03-14T10:00:00Z");
296
+ expect(firstPrice.shareCount).toBe(82000000);
297
+ expect(firstPrice.investorCount).toBe(25000);
298
+
299
+ expect(client.getHistoricalFundPrices).toHaveBeenCalledWith(
300
+ "SPP",
301
+ Region.Tr,
302
+ HistoricalFundPricePeriod.OneMonth
303
+ );
304
+ });
305
+
306
+ test("should handle invalid period error", async () => {
307
+ jest.spyOn(client, 'getHistoricalFundPrices').mockRejectedValue(new Error("Invalid period"));
308
+
309
+ await expect(client.getHistoricalFundPrices(
310
+ "SPP",
311
+ Region.Tr,
312
+ "INVALID_PERIOD" as HistoricalFundPricePeriod
313
+ )).rejects.toThrow("Invalid period");
314
+ });
315
+ });
316
+ });
317
+ });
@@ -0,0 +1,58 @@
1
+ import { Collection, CollectionDetail } from "../client/collections";
2
+
3
+ export const validateCollection = (collection: Collection) => {
4
+ expect(typeof collection.id).toBe("string");
5
+ expect(typeof collection.title).toBe("string");
6
+ expect(typeof collection.imageUrl).toBe("string");
7
+ expect(typeof collection.avatarUrl).toBe("string");
8
+ expect(typeof collection.numStocks).toBe("number");
9
+
10
+ if (collection.description !== undefined) {
11
+ expect(typeof collection.description).toBe("string");
12
+ }
13
+
14
+ if (collection.region !== undefined) {
15
+ expect(Array.isArray(collection.region)).toBe(true);
16
+ collection.region.forEach(region => {
17
+ expect(typeof region).toBe("string");
18
+ });
19
+ }
20
+
21
+ if (collection.assetClass !== undefined) {
22
+ expect(typeof collection.assetClass).toBe("string");
23
+ }
24
+
25
+ if (collection.image !== undefined) {
26
+ expect(typeof collection.image).toBe("string");
27
+ }
28
+
29
+ if (collection.order !== undefined) {
30
+ expect(typeof collection.order).toBe("number");
31
+ }
32
+
33
+ if (collection.status !== undefined) {
34
+ expect(typeof collection.status).toBe("string");
35
+ }
36
+
37
+ if (collection.metaData !== undefined) {
38
+ expect(typeof collection.metaData).toBe("object");
39
+ expect(collection.metaData).not.toBeNull();
40
+ }
41
+ };
42
+
43
+ export const validateCollectionDetail = (collectionDetail: CollectionDetail) => {
44
+ validateCollection(collectionDetail);
45
+
46
+ expect(Array.isArray(collectionDetail.stocks)).toBe(true);
47
+ expect(collectionDetail.stocks.length).toBeGreaterThan(0);
48
+
49
+ const firstStock = collectionDetail.stocks[0];
50
+ expect(typeof firstStock.id).toBe("string");
51
+ expect(typeof firstStock.assetType).toBe("string");
52
+ expect(typeof firstStock.name).toBe("string");
53
+ expect(typeof firstStock.symbol).toBe("string");
54
+ expect(typeof firstStock.sectorId).toBe("string");
55
+ expect(typeof firstStock.industryId).toBe("string");
56
+ expect(typeof firstStock.updatedDate).toBe("string");
57
+ expect(typeof firstStock.active).toBe("boolean");
58
+ };
@@ -0,0 +1,110 @@
1
+ import { Logger } from "winston";
2
+ import { LaplaceConfiguration } from "../utilities/configuration";
3
+ import "./client_test_suite";
4
+ import { KeyInsightClient, KeyInsight } from "../client/key-insights";
5
+ import { Region } from "../client/collections";
6
+
7
+ const mockTRKeyInsightResponse: KeyInsight = {
8
+ symbol: "TOASO",
9
+ insight: "Tofaş'ın net kârı, güçlü operasyonel performans ve yüksek ihracat gelirleri sayesinde geçen yılın aynı dönemine göre %85 artış gösterdi."
10
+ };
11
+
12
+ const mockUSKeyInsightResponse: KeyInsight = {
13
+ symbol: "AAPL",
14
+ insight: "Apple's revenue growth was driven by strong iPhone sales and continued expansion in services, with significant market share gains in emerging markets."
15
+ };
16
+
17
+ describe("Key Insight", () => {
18
+ let client: KeyInsightClient;
19
+
20
+ beforeAll(() => {
21
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
22
+ const logger: Logger = {
23
+ info: jest.fn(),
24
+ error: jest.fn(),
25
+ warn: jest.fn(),
26
+ debug: jest.fn(),
27
+ } as unknown as Logger;
28
+
29
+ client = new KeyInsightClient(config, logger);
30
+ });
31
+
32
+ describe("Integration Tests", () => {
33
+ describe("getKeyInsights", () => {
34
+ test("should return key insights for valid symbol", async () => {
35
+ const resp = await client.getKeyInsights("TOASO", Region.Tr);
36
+
37
+ expect(resp).toBeDefined();
38
+ expect(resp.insight).toBeDefined();
39
+ expect(resp.insight.length).toBeGreaterThan(0);
40
+ expect(resp.symbol).toBe("TOASO");
41
+ });
42
+
43
+ test("should handle invalid symbol gracefully", async () => {
44
+ await expect(
45
+ client.getKeyInsights("INVALID_SYMBOL", Region.Tr)
46
+ ).rejects.toThrow();
47
+ });
48
+
49
+ test("should work with US region", async () => {
50
+ const resp = await client.getKeyInsights("AAPL", Region.Us);
51
+ expect(resp).toBeDefined();
52
+ expect(resp.symbol).toBe("AAPL");
53
+ });
54
+
55
+ test("should handle empty symbol", async () => {
56
+ await expect(client.getKeyInsights("", Region.Tr)).rejects.toThrow();
57
+ });
58
+ });
59
+ });
60
+
61
+ describe("Mock Tests", () => {
62
+ beforeEach(() => {
63
+ jest.clearAllMocks();
64
+ });
65
+
66
+ describe("getKeyInsights", () => {
67
+ test("should return TR key insights with mock data", async () => {
68
+ jest.spyOn(client, 'getKeyInsights').mockResolvedValue(mockTRKeyInsightResponse);
69
+
70
+ const resp = await client.getKeyInsights("TOASO", Region.Tr);
71
+
72
+ expect(resp).toBeDefined();
73
+ expect(resp.symbol).toBe("TOASO");
74
+ expect(resp.insight).toBe(mockTRKeyInsightResponse.insight);
75
+
76
+ expect(client.getKeyInsights).toHaveBeenCalledWith("TOASO", Region.Tr);
77
+ });
78
+
79
+ test("should return US key insights with mock data", async () => {
80
+ jest.spyOn(client, 'getKeyInsights').mockResolvedValue(mockUSKeyInsightResponse);
81
+
82
+ const resp = await client.getKeyInsights("AAPL", Region.Us);
83
+
84
+ expect(resp).toBeDefined();
85
+ expect(resp.symbol).toBe("AAPL");
86
+ expect(resp.insight).toBe(mockUSKeyInsightResponse.insight);
87
+
88
+ expect(client.getKeyInsights).toHaveBeenCalledWith("AAPL", Region.Us);
89
+ });
90
+
91
+ test("should handle invalid symbol error", async () => {
92
+ jest.spyOn(client, 'getKeyInsights').mockRejectedValue(new Error("Symbol not found"));
93
+
94
+ await expect(client.getKeyInsights("INVALID_SYMBOL", Region.Tr))
95
+ .rejects.toThrow("Symbol not found");
96
+
97
+ expect(client.getKeyInsights).toHaveBeenCalledWith("INVALID_SYMBOL", Region.Tr);
98
+ });
99
+
100
+ test("should handle empty symbol error", async () => {
101
+ jest.spyOn(client, 'getKeyInsights').mockRejectedValue(new Error("Symbol cannot be empty"));
102
+
103
+ await expect(client.getKeyInsights("", Region.Tr))
104
+ .rejects.toThrow("Symbol cannot be empty");
105
+
106
+ expect(client.getKeyInsights).toHaveBeenCalledWith("", Region.Tr);
107
+ });
108
+ });
109
+ });
110
+ });