laplace-api 4.7.0 → 5.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.
@@ -14,91 +14,151 @@ import {
14
14
  } from "../client/broker";
15
15
  import { AssetType, AssetClass } from "../client/stocks";
16
16
  import { PaginatedResponse } from "../client/capital_increase";
17
+ import { AxiosInstance } from "axios";
17
18
 
18
- const mockBroker: Broker = {
19
- id: 1001,
20
- symbol: "BIYKR",
21
- name: "BİYİKLI YATIRIM",
22
- longName: "Bıyıklı Yatırım Menkul Değerler A.Ş.",
23
- logo: "https://example.com/biykr.png"
24
- };
25
-
26
- const mockBroker2: Broker = {
27
- id: 1002,
28
- symbol: "GEDIK",
29
- name: "GEDİK YATIRIM",
30
- longName: "Gedik Yatırım Menkul Değerler A.Ş.",
31
- logo: "https://example.com/gedik.png"
32
- };
33
-
34
- const mockBroker3: Broker = {
35
- id: 1001,
36
- symbol: "BIYKR",
37
- name: "BİYİKLI YATIRIM",
38
- longName: "Bıyıklı Yatırım Menkul Değerler A.Ş.",
39
- logo: "https://example.com/biykr.png",
40
- supportedAssetClasses: [AssetClass.Equity]
41
- };
42
-
43
- const mockBroker4: Broker = {
44
- id: 1002,
45
- symbol: "GEDIK",
46
- name: "GEDİK YATIRIM",
47
- longName: "Gedik Yatırım Menkul Değerler A.Ş.",
48
- logo: "https://example.com/gedik.png",
49
- supportedAssetClasses: [AssetClass.Equity]
19
+ const fxGetMarketBrokers = {
20
+ recordCount: 2,
21
+ items: [
22
+ {
23
+ broker: {
24
+ id: 1,
25
+ symbol: "BIDZY",
26
+ name: "DENIZ YATIRIM",
27
+ longName: "DENIZ YATIRIM MENKUL KIYMETLER A.S.",
28
+ logo: "https://finfree-storage.s3.eu-central-1.amazonaws.com/brokers/BIDZY.svg",
29
+ },
30
+ netAmount: 2500000.0,
31
+ totalAmount: 10000000.0,
32
+ totalVolume: 200000,
33
+ totalBuyAmount: 6250000.0,
34
+ totalBuyVolume: 100000,
35
+ totalSellAmount: 3750000.0,
36
+ totalSellVolume: 100000,
37
+ },
38
+ ],
39
+ totalStats: {
40
+ netAmount: 4000000.0,
41
+ totalAmount: 17500000.0,
42
+ totalVolume: 350000,
43
+ totalBuyAmount: 10750000.0,
44
+ totalBuyVolume: 175000,
45
+ totalSellAmount: 6750000.0,
46
+ totalSellVolume: 175000,
47
+ },
50
48
  };
51
49
 
52
- const mockStock: BrokerStock = {
53
- id: "61dd0d6f0ec2114146342fd0",
54
- symbol: "TUPRS",
55
- name: "Tüpraş",
56
- assetType: AssetType.Stock,
57
- assetClass: AssetClass.Equity
58
- };
59
50
 
60
- const mockStock2: BrokerStock = {
61
- id: "61dd0d6f0ec2114146342fd1",
62
- symbol: "GARAN",
63
- name: "Garanti Bankası",
64
- assetType: AssetType.Stock,
65
- assetClass: AssetClass.Equity
51
+ const fxGetMarketStocks = {
52
+ recordCount: 2,
53
+ items: [
54
+ {
55
+ stock: {
56
+ id: "61dd0d670ec2114146342fa5",
57
+ name: "SASA Polyester",
58
+ symbol: "SASA",
59
+ assetType: AssetType.Stock,
60
+ assetClass: AssetClass.Equity,
61
+ },
62
+ averageCost: 2.91,
63
+ netAmount: 1000000.0,
64
+ totalAmount: 5000000.0,
65
+ totalVolume: 100000,
66
+ totalBuyAmount: 3000000.0,
67
+ totalBuyVolume: 50000,
68
+ totalSellAmount: 2000000.0,
69
+ totalSellVolume: 50000,
70
+ },
71
+ ],
72
+ totalStats: {
73
+ averageCost: 2.91,
74
+ netAmount: 1750000.0,
75
+ totalAmount: 8750000.0,
76
+ totalVolume: 175000,
77
+ totalBuyAmount: 5250000.0,
78
+ totalBuyVolume: 87500,
79
+ totalSellAmount: 3500000.0,
80
+ totalSellVolume: 87500,
81
+ },
66
82
  };
67
83
 
68
- const mockBrokerStats: BrokerStats = {
69
- totalBuyAmount: 1000000,
70
- totalSellAmount: 800000,
71
- netAmount: 200000,
72
- totalBuyVolume: 50000,
73
- totalSellVolume: 40000,
74
- totalVolume: 90000,
75
- totalAmount: 1800000,
76
- averageCost: 20.5
77
- };
78
84
 
79
- const mockBrokerItem: BrokerItem = {
80
- ...mockBrokerStats,
81
- broker: mockBroker,
82
- stock: mockStock
85
+ const fxGetBrokersByStock = {
86
+ recordCount: 2,
87
+ items: [
88
+ {
89
+ broker: {
90
+ id: 1,
91
+ symbol: "BIMLB",
92
+ name: "BIMLB",
93
+ longName: "BIM Yatırım Menkul Değerler A.Ş.",
94
+ logo: "https://finfree-storage.s3.eu-central-1.amazonaws.com/broker-logos/bimlb.png",
95
+ },
96
+ averageCost: 2.91,
97
+ netAmount: 500000.0,
98
+ totalAmount: 2000000.0,
99
+ totalVolume: 40000,
100
+ totalBuyAmount: 1250000.0,
101
+ totalBuyVolume: 20000,
102
+ totalSellAmount: 750000.0,
103
+ totalSellVolume: 20000,
104
+ },
105
+ ],
106
+ totalStats: {
107
+ averageCost: 2.91,
108
+ netAmount: 800000.0,
109
+ totalAmount: 3500000.0,
110
+ totalVolume: 70000,
111
+ totalBuyAmount: 2150000.0,
112
+ totalBuyVolume: 35000,
113
+ totalSellAmount: 1350000.0,
114
+ totalSellVolume: 35000,
115
+ },
83
116
  };
84
117
 
85
- const mockBrokerItem2: BrokerItem = {
86
- ...mockBrokerStats,
87
- totalBuyAmount: 900000,
88
- totalSellAmount: 700000,
89
- broker: mockBroker2,
90
- stock: mockStock2
91
- };
92
118
 
93
- const mockBrokerList: BrokerList = {
119
+ const fxGetStocksByBroker = {
94
120
  recordCount: 2,
95
- items: [mockBrokerItem, mockBrokerItem2],
96
- totalStats: mockBrokerStats
121
+ items: [
122
+ {
123
+ stock: {
124
+ id: "61dd0d670ec2114146342fa5",
125
+ name: "SASA Polyester",
126
+ symbol: "SASA",
127
+ assetType: AssetType.Stock,
128
+ assetClass: AssetClass.Equity,
129
+ },
130
+ netAmount: 500000.0,
131
+ totalAmount: 2000000.0,
132
+ totalVolume: 40000,
133
+ totalBuyAmount: 1250000.0,
134
+ totalBuyVolume: 20000,
135
+ totalSellAmount: 750000.0,
136
+ totalSellVolume: 20000,
137
+ },
138
+ ],
139
+ totalStats: {
140
+ netAmount: 800000.0,
141
+ totalAmount: 3500000.0,
142
+ totalVolume: 70000,
143
+ totalBuyAmount: 2150000.0,
144
+ totalBuyVolume: 35000,
145
+ totalSellAmount: 1350000.0,
146
+ totalSellVolume: 35000,
147
+ },
97
148
  };
98
149
 
99
- const mockBrokersPaginatedResponse: PaginatedResponse<Broker> = {
100
- recordCount: 2,
101
- items: [mockBroker3, mockBroker4]
150
+ const fxGetBrokers = {
151
+ recordCount: 239,
152
+ items: [
153
+ {
154
+ id: 1,
155
+ symbol: "BIDZY",
156
+ name: "DENIZ YATIRIM",
157
+ longName: "DENIZ YATIRIM MENKUL KIYMETLER A.S.",
158
+ logo: "https://finfree-storage.s3.eu-central-1.amazonaws.com/brokers/BIDZY.svg",
159
+ supportedAssetClasses: [AssetClass.Equity],
160
+ },
161
+ ],
102
162
  };
103
163
 
104
164
  describe("BrokerClient", () => {
@@ -118,57 +178,57 @@ describe("BrokerClient", () => {
118
178
  });
119
179
 
120
180
  const region = Region.Tr;
121
- const fromDate = "2025-05-20";
122
- const toDate = "2025-05-28";
181
+ const fromDate = "2025-06-01";
182
+ const toDate = "2025-06-30";
123
183
 
124
184
  describe("Integration Tests", () => {
125
- test("getMarketBrokers returns valid and fully typed data", async () => {
185
+ jest.setTimeout(60_000);
186
+ test("getMarketBrokers returns valid data", async () => {
126
187
  const response = await brokerClient.getMarketBrokers(
127
188
  Region.Tr,
128
189
  BrokerSort.TotalVolume,
129
190
  SortDirection.Desc,
130
191
  "2025-05-27",
131
192
  "2025-05-28",
132
- 0,
133
- 5
193
+ 5,
194
+ 0
134
195
  );
135
196
 
136
197
  expect(response).toBeDefined();
137
198
  expect(typeof response.recordCount).toBe("number");
199
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
138
200
 
139
201
  const stats = response.totalStats;
140
- expect(stats).toMatchObject<BrokerStats>({
141
- totalBuyAmount: expect.any(Number),
142
- totalSellAmount: expect.any(Number),
143
- netAmount: expect.any(Number),
144
- totalBuyVolume: expect.any(Number),
145
- totalSellVolume: expect.any(Number),
146
- totalVolume: expect.any(Number),
147
- totalAmount: expect.any(Number),
148
- });
202
+ expect(typeof stats.totalBuyAmount).toBe("number");
203
+ expect(typeof stats.totalSellAmount).toBe("number");
204
+ expect(typeof stats.netAmount).toBe("number");
205
+ expect(typeof stats.totalBuyVolume).toBe("number");
206
+ expect(typeof stats.totalSellVolume).toBe("number");
207
+ expect(typeof stats.totalVolume).toBe("number");
208
+ expect(typeof stats.totalAmount).toBe("number");
149
209
 
150
210
  expect(Array.isArray(response.items)).toBe(true);
151
- expect(response.items.length).toBeGreaterThan(0);
152
-
153
- for (const item of response.items) {
154
- expect(item).toMatchObject<BrokerStats>({
155
- totalBuyAmount: expect.any(Number),
156
- totalSellAmount: expect.any(Number),
157
- netAmount: expect.any(Number),
158
- totalBuyVolume: expect.any(Number),
159
- totalSellVolume: expect.any(Number),
160
- totalVolume: expect.any(Number),
161
- totalAmount: expect.any(Number),
162
- });
211
+
212
+ if (response.items.length > 0) {
213
+ const item = response.items[0];
214
+
215
+ expect(typeof item.totalBuyAmount).toBe("number");
216
+ expect(typeof item.totalSellAmount).toBe("number");
217
+ expect(typeof item.netAmount).toBe("number");
218
+ expect(typeof item.totalBuyVolume).toBe("number");
219
+ expect(typeof item.totalSellVolume).toBe("number");
220
+ expect(typeof item.totalVolume).toBe("number");
221
+ expect(typeof item.totalAmount).toBe("number");
163
222
 
164
223
  if (item.broker) {
165
- expect(item.broker).toMatchObject({
166
- id: expect.any(Number),
167
- symbol: expect.any(String),
168
- name: expect.any(String),
169
- longName: expect.any(String),
170
- logo: expect.any(String),
171
- });
224
+ expect(typeof item.broker.id).toBe("number");
225
+ expect(typeof item.broker.symbol).toBe("string");
226
+ expect(typeof item.broker.name).toBe("string");
227
+ expect(typeof item.broker.longName).toBe("string");
228
+ // python tarafında logo None olabilir kuralı vardı -> burada da izin ver
229
+ expect(
230
+ typeof item.broker.logo === "string" || item.broker.logo == null
231
+ ).toBe(true);
172
232
  }
173
233
  }
174
234
  });
@@ -180,45 +240,47 @@ describe("BrokerClient", () => {
180
240
  SortDirection.Desc,
181
241
  fromDate,
182
242
  toDate,
183
- 0,
184
- 5
243
+ 5,
244
+ 0
185
245
  );
186
246
 
187
247
  expect(response).toBeDefined();
188
248
  expect(typeof response.recordCount).toBe("number");
249
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
189
250
 
190
251
  const stats = response.totalStats;
191
- expect(stats).toMatchObject<BrokerStats>({
192
- totalBuyAmount: expect.any(Number),
193
- totalSellAmount: expect.any(Number),
194
- netAmount: expect.any(Number),
195
- totalBuyVolume: expect.any(Number),
196
- totalSellVolume: expect.any(Number),
197
- totalVolume: expect.any(Number),
198
- totalAmount: expect.any(Number),
199
- });
252
+ expect(typeof stats.totalBuyAmount).toBe("number");
253
+ expect(typeof stats.totalSellAmount).toBe("number");
254
+ expect(typeof stats.netAmount).toBe("number");
255
+ expect(typeof stats.totalBuyVolume).toBe("number");
256
+ expect(typeof stats.totalSellVolume).toBe("number");
257
+ expect(typeof stats.totalVolume).toBe("number");
258
+ expect(typeof stats.totalAmount).toBe("number");
200
259
 
201
260
  expect(Array.isArray(response.items)).toBe(true);
202
261
 
203
- for (const item of response.items) {
204
- expect(item).toMatchObject<BrokerStats>({
205
- totalBuyAmount: expect.any(Number),
206
- totalSellAmount: expect.any(Number),
207
- netAmount: expect.any(Number),
208
- totalBuyVolume: expect.any(Number),
209
- totalSellVolume: expect.any(Number),
210
- totalVolume: expect.any(Number),
211
- totalAmount: expect.any(Number),
212
- });
262
+ if (response.items.length > 0) {
263
+ const item = response.items[0];
264
+
265
+ expect(typeof item.totalBuyAmount).toBe("number");
266
+ expect(typeof item.totalSellAmount).toBe("number");
267
+ expect(typeof item.netAmount).toBe("number");
268
+ expect(typeof item.totalBuyVolume).toBe("number");
269
+ expect(typeof item.totalSellVolume).toBe("number");
270
+ expect(typeof item.totalVolume).toBe("number");
271
+ expect(typeof item.totalAmount).toBe("number");
213
272
 
214
273
  if (item.stock) {
215
- expect(item.stock).toMatchObject({
216
- id: expect.any(String),
217
- symbol: expect.any(String),
218
- name: expect.any(String),
219
- assetType: expect.any(String),
220
- assetClass: expect.any(String),
221
- });
274
+ expect(typeof item.stock.id).toBe("string");
275
+ expect(typeof item.stock.symbol).toBe("string");
276
+ expect(typeof item.stock.name).toBe("string");
277
+ expect(typeof item.stock.assetType).toBe("string");
278
+ expect(typeof item.stock.assetClass).toBe("string");
279
+ }
280
+
281
+ // bu endpoint averageCost döndürüyorsa kontrol et (python’da market stock list’te vardı)
282
+ if ("averageCost" in item) {
283
+ expect(typeof (item as any).averageCost).toBe("number");
222
284
  }
223
285
  }
224
286
  });
@@ -231,49 +293,48 @@ describe("BrokerClient", () => {
231
293
  SortDirection.Desc,
232
294
  fromDate,
233
295
  toDate,
234
- 0,
235
- 5
296
+ 5,
297
+ 0
236
298
  );
237
299
 
238
300
  expect(response).toBeDefined();
239
301
  expect(typeof response.recordCount).toBe("number");
302
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
240
303
 
241
304
  const stats = response.totalStats;
242
- expect(stats).toMatchObject<BrokerStats>({
243
- totalBuyAmount: expect.any(Number),
244
- totalSellAmount: expect.any(Number),
245
- netAmount: expect.any(Number),
246
- totalBuyVolume: expect.any(Number),
247
- totalSellVolume: expect.any(Number),
248
- totalVolume: expect.any(Number),
249
- totalAmount: expect.any(Number),
250
- });
305
+ expect(typeof stats.totalBuyAmount).toBe("number");
306
+ expect(typeof stats.totalSellAmount).toBe("number");
307
+ expect(typeof stats.netAmount).toBe("number");
308
+ expect(typeof stats.totalBuyVolume).toBe("number");
309
+ expect(typeof stats.totalSellVolume).toBe("number");
310
+ expect(typeof stats.totalVolume).toBe("number");
311
+ expect(typeof stats.totalAmount).toBe("number");
251
312
 
252
313
  expect(Array.isArray(response.items)).toBe(true);
253
314
 
254
- for (const item of response.items) {
255
- expect(item).toMatchObject<BrokerStats>({
256
- totalBuyAmount: expect.any(Number),
257
- totalSellAmount: expect.any(Number),
258
- netAmount: expect.any(Number),
259
- totalBuyVolume: expect.any(Number),
260
- totalSellVolume: expect.any(Number),
261
- totalVolume: expect.any(Number),
262
- totalAmount: expect.any(Number),
263
- });
315
+ if (response.items.length > 0) {
316
+ const item = response.items[0];
317
+
318
+ expect(typeof item.totalBuyAmount).toBe("number");
319
+ expect(typeof item.totalSellAmount).toBe("number");
320
+ expect(typeof item.netAmount).toBe("number");
321
+ expect(typeof item.totalBuyVolume).toBe("number");
322
+ expect(typeof item.totalSellVolume).toBe("number");
323
+ expect(typeof item.totalVolume).toBe("number");
324
+ expect(typeof item.totalAmount).toBe("number");
264
325
 
265
326
  if (item.broker) {
266
- expect(item.broker).toMatchObject({
267
- id: expect.any(Number),
268
- symbol: expect.any(String),
269
- name: expect.any(String),
270
- longName: expect.any(String),
271
- logo: expect.any(String),
272
- });
327
+ expect(typeof item.broker.id).toBe("number");
328
+ expect(typeof item.broker.symbol).toBe("string");
329
+ expect(typeof item.broker.name).toBe("string");
330
+ expect(typeof item.broker.longName).toBe("string");
331
+ expect(
332
+ typeof item.broker.logo === "string" || item.broker.logo == null
333
+ ).toBe(true);
273
334
  }
274
335
 
275
336
  if ("averageCost" in item) {
276
- expect(item.averageCost).toEqual(expect.any(Number));
337
+ expect(typeof (item as any).averageCost).toBe("number");
277
338
  }
278
339
  }
279
340
  });
@@ -286,45 +347,46 @@ describe("BrokerClient", () => {
286
347
  SortDirection.Desc,
287
348
  fromDate,
288
349
  toDate,
289
- 0,
290
- 5
350
+ 5,
351
+ 0
291
352
  );
292
353
 
293
354
  expect(response).toBeDefined();
294
355
  expect(typeof response.recordCount).toBe("number");
356
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
295
357
 
296
358
  const stats = response.totalStats;
297
- expect(stats).toMatchObject<BrokerStats>({
298
- totalBuyAmount: expect.any(Number),
299
- totalSellAmount: expect.any(Number),
300
- netAmount: expect.any(Number),
301
- totalBuyVolume: expect.any(Number),
302
- totalSellVolume: expect.any(Number),
303
- totalVolume: expect.any(Number),
304
- totalAmount: expect.any(Number),
305
- });
359
+ expect(typeof stats.totalBuyAmount).toBe("number");
360
+ expect(typeof stats.totalSellAmount).toBe("number");
361
+ expect(typeof stats.netAmount).toBe("number");
362
+ expect(typeof stats.totalBuyVolume).toBe("number");
363
+ expect(typeof stats.totalSellVolume).toBe("number");
364
+ expect(typeof stats.totalVolume).toBe("number");
365
+ expect(typeof stats.totalAmount).toBe("number");
306
366
 
307
367
  expect(Array.isArray(response.items)).toBe(true);
308
368
 
309
- for (const item of response.items) {
310
- expect(item).toMatchObject<BrokerStats>({
311
- totalBuyAmount: expect.any(Number),
312
- totalSellAmount: expect.any(Number),
313
- netAmount: expect.any(Number),
314
- totalBuyVolume: expect.any(Number),
315
- totalSellVolume: expect.any(Number),
316
- totalVolume: expect.any(Number),
317
- totalAmount: expect.any(Number),
318
- });
369
+ if (response.items.length > 0) {
370
+ const item = response.items[0];
371
+
372
+ expect(typeof item.totalBuyAmount).toBe("number");
373
+ expect(typeof item.totalSellAmount).toBe("number");
374
+ expect(typeof item.netAmount).toBe("number");
375
+ expect(typeof item.totalBuyVolume).toBe("number");
376
+ expect(typeof item.totalSellVolume).toBe("number");
377
+ expect(typeof item.totalVolume).toBe("number");
378
+ expect(typeof item.totalAmount).toBe("number");
319
379
 
320
380
  if (item.stock) {
321
- expect(item.stock).toMatchObject({
322
- id: expect.any(String),
323
- symbol: expect.any(String),
324
- name: expect.any(String),
325
- assetType: expect.any(String),
326
- assetClass: expect.any(String),
327
- });
381
+ expect(typeof item.stock.id).toBe("string");
382
+ expect(typeof item.stock.symbol).toBe("string");
383
+ expect(typeof item.stock.name).toBe("string");
384
+ expect(typeof item.stock.assetType).toBe("string");
385
+ expect(typeof item.stock.assetClass).toBe("string");
386
+ }
387
+
388
+ if ("averageCost" in item) {
389
+ expect(typeof (item as any).averageCost).toBe("number");
328
390
  }
329
391
  }
330
392
  });
@@ -332,342 +394,407 @@ describe("BrokerClient", () => {
332
394
  test("getBrokers with assetClass parameter", async () => {
333
395
  const response = await brokerClient.getBrokers(
334
396
  Region.Tr,
335
- 0,
336
397
  10,
398
+ 0,
337
399
  AssetClass.Equity
338
400
  );
339
401
 
340
402
  expect(response).toBeDefined();
341
403
  expect(typeof response.recordCount).toBe("number");
404
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
342
405
  expect(Array.isArray(response.items)).toBe(true);
343
406
 
344
- for (const broker of response.items) {
345
- expect(broker).toMatchObject({
346
- id: expect.any(Number),
347
- symbol: expect.any(String),
348
- name: expect.any(String),
349
- longName: expect.any(String),
350
- logo: expect.any(String),
351
- });
407
+ if (response.items.length > 0) {
408
+ const broker = response.items[0];
409
+
410
+ expect(typeof broker.id).toBe("number");
411
+ expect(typeof broker.symbol).toBe("string");
412
+ expect(typeof broker.name).toBe("string");
413
+ expect(typeof broker.longName).toBe("string");
414
+ expect(typeof broker.logo === "string" || broker.logo == null).toBe(
415
+ true
416
+ );
417
+
352
418
  expect(Array.isArray(broker.supportedAssetClasses)).toBe(true);
353
419
  expect(broker.supportedAssetClasses).toEqual([AssetClass.Equity]);
354
420
  }
355
421
  });
356
422
 
357
423
  test("getBrokers without assetClass parameter", async () => {
358
- const response = await brokerClient.getBrokers(
359
- Region.Tr,
360
- 0,
361
- 10
362
- );
424
+ const response = await brokerClient.getBrokers(Region.Tr, 10, 0);
363
425
 
364
426
  expect(response).toBeDefined();
365
427
  expect(typeof response.recordCount).toBe("number");
428
+ expect(response.recordCount).toBeGreaterThanOrEqual(0);
366
429
  expect(Array.isArray(response.items)).toBe(true);
367
430
 
368
- for (const broker of response.items) {
369
- expect(broker).toMatchObject({
370
- id: expect.any(Number),
371
- symbol: expect.any(String),
372
- name: expect.any(String),
373
- longName: expect.any(String),
374
- logo: expect.any(String),
375
- });
431
+ if (response.items.length > 0) {
432
+ const broker = response.items[0];
433
+
434
+ expect(typeof broker.id).toBe("number");
435
+ expect(typeof broker.symbol).toBe("string");
436
+ expect(typeof broker.name).toBe("string");
437
+ expect(typeof broker.longName).toBe("string");
438
+ expect(typeof broker.logo === "string" || broker.logo == null).toBe(
439
+ true
440
+ );
376
441
  }
377
442
  });
378
443
  });
379
444
 
380
445
  describe("Mock Tests", () => {
446
+ const region = Region.Tr;
447
+ const fromDate = "2025-06-01";
448
+ const toDate = "2025-06-30";
449
+ const page = 0;
450
+ const size = 5;
451
+
452
+ let brokerClient: BrokerClient;
453
+ let cli: { request: jest.Mock };
454
+
381
455
  beforeEach(() => {
382
- jest.clearAllMocks();
456
+ cli = {
457
+ request: jest.fn(),
458
+ };
459
+
460
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
461
+ const logger: Logger = {
462
+ info: jest.fn(),
463
+ error: jest.fn(),
464
+ warn: jest.fn(),
465
+ debug: jest.fn(),
466
+ } as unknown as Logger;
467
+
468
+ // IMPORTANT: Client ctor artık cli kabul ediyor
469
+ brokerClient = new BrokerClient(config, logger, cli as any);
383
470
  });
384
-
471
+
385
472
  describe("getMarketBrokers", () => {
386
- test("should return market brokers with stats", async () => {
387
- jest.spyOn(brokerClient, 'getMarketBrokers').mockResolvedValue(mockBrokerList);
388
-
389
- const response = await brokerClient.getMarketBrokers(
390
- Region.Tr,
473
+ test("should call correct endpoint/params and read values", async () => {
474
+ cli.request.mockResolvedValueOnce({ data: fxGetMarketBrokers });
475
+
476
+ const res = await brokerClient.getMarketBrokers(
477
+ region,
391
478
  BrokerSort.TotalVolume,
392
479
  SortDirection.Desc,
393
480
  fromDate,
394
481
  toDate,
395
- 0,
396
- 5
482
+ size,
483
+ page
397
484
  );
398
-
399
- expect(response.recordCount).toBe(2);
400
- expect(response.items).toHaveLength(2);
401
- expect(response.totalStats).toEqual(mockBrokerStats);
402
-
403
- const firstItem = response.items[0];
404
- expect(firstItem.broker?.symbol).toBe("BIYKR");
405
- expect(firstItem.totalBuyAmount).toBe(1000000);
406
- expect(firstItem.totalSellAmount).toBe(800000);
407
-
408
- expect(brokerClient.getMarketBrokers).toHaveBeenCalledWith(
409
- Region.Tr,
410
- BrokerSort.TotalVolume,
411
- SortDirection.Desc,
485
+
486
+ // request shape
487
+ expect(cli.request).toHaveBeenCalledTimes(1);
488
+ const call = cli.request.mock.calls[0][0];
489
+
490
+ expect(call.method).toBe("GET");
491
+ expect(call.url).toBe("/api/v1/brokers/market");
492
+ expect(call.params).toEqual({
493
+ region,
494
+ sortBy: BrokerSort.TotalVolume,
495
+ sortDirection: SortDirection.Desc,
412
496
  fromDate,
413
497
  toDate,
414
- 0,
415
- 5
416
- );
417
- });
418
-
419
- test("should handle empty response", async () => {
420
- const emptyResponse: BrokerList = {
421
- recordCount: 0,
422
- items: [],
423
- totalStats: {
424
- totalBuyAmount: 0,
425
- totalSellAmount: 0,
426
- netAmount: 0,
427
- totalBuyVolume: 0,
428
- totalSellVolume: 0,
429
- totalVolume: 0,
430
- totalAmount: 0
431
- }
432
- };
433
- jest.spyOn(brokerClient, 'getMarketBrokers').mockResolvedValue(emptyResponse);
434
-
435
- const response = await brokerClient.getMarketBrokers(
436
- Region.Tr,
437
- BrokerSort.TotalVolume,
438
- SortDirection.Desc,
439
- fromDate,
440
- toDate,
441
- 0,
442
- 5
498
+ page,
499
+ size,
500
+ });
501
+
502
+ // response values
503
+ expect(res.recordCount).toBe(2);
504
+ expect(res.items).toHaveLength(1);
505
+
506
+ expect(res.totalStats.netAmount).toBe(4000000.0);
507
+ expect(res.totalStats.totalAmount).toBe(17500000.0);
508
+ expect(res.totalStats.totalVolume).toBe(350000);
509
+ expect(res.totalStats.totalBuyAmount).toBe(10750000.0);
510
+ expect(res.totalStats.totalBuyVolume).toBe(175000);
511
+ expect(res.totalStats.totalSellAmount).toBe(6750000.0);
512
+ expect(res.totalStats.totalSellVolume).toBe(175000);
513
+
514
+ const item = res.items[0];
515
+ expect(item.broker?.id).toBe(1);
516
+ expect(item.broker?.symbol).toBe("BIDZY");
517
+ expect(item.broker?.name).toBe("DENIZ YATIRIM");
518
+ expect(item.broker?.longName).toBe("DENIZ YATIRIM MENKUL KIYMETLER A.S.");
519
+ expect(item.broker?.logo).toBe(
520
+ "https://finfree-storage.s3.eu-central-1.amazonaws.com/brokers/BIDZY.svg"
443
521
  );
444
-
445
- expect(response.recordCount).toBe(0);
446
- expect(response.items).toHaveLength(0);
447
- expect(response.totalStats.totalAmount).toBe(0);
522
+
523
+ expect(item.netAmount).toBe(2500000.0);
524
+ expect(item.totalAmount).toBe(10000000.0);
525
+ expect(item.totalVolume).toBe(200000);
526
+ expect(item.totalBuyAmount).toBe(6250000.0);
527
+ expect(item.totalBuyVolume).toBe(100000);
528
+ expect(item.totalSellAmount).toBe(3750000.0);
529
+ expect(item.totalSellVolume).toBe(100000);
448
530
  });
449
531
  });
450
-
532
+
451
533
  describe("getMarketStocks", () => {
452
- test("should return market stocks with stats", async () => {
453
- jest.spyOn(brokerClient, 'getMarketStocks').mockResolvedValue(mockBrokerList);
454
-
455
- const response = await brokerClient.getMarketStocks(
456
- Region.Tr,
534
+ test("should call correct endpoint/params and read values", async () => {
535
+ cli.request.mockResolvedValueOnce({ data: fxGetMarketStocks });
536
+
537
+ const res = await brokerClient.getMarketStocks(
538
+ region,
457
539
  BrokerSort.TotalVolume,
458
540
  SortDirection.Desc,
459
541
  fromDate,
460
542
  toDate,
461
- 0,
462
- 5
543
+ size,
544
+ page
463
545
  );
464
-
465
- expect(response.recordCount).toBe(2);
466
- expect(response.items).toHaveLength(2);
467
- expect(response.totalStats).toEqual(mockBrokerStats);
468
-
469
- const firstItem = response.items[0];
470
- expect(firstItem.stock?.symbol).toBe("TUPRS");
471
- expect(firstItem.stock?.assetType).toBe(AssetType.Stock);
472
- expect(firstItem.stock?.assetClass).toBe(AssetClass.Equity);
473
-
474
- expect(brokerClient.getMarketStocks).toHaveBeenCalledWith(
475
- Region.Tr,
476
- BrokerSort.TotalVolume,
477
- SortDirection.Desc,
546
+
547
+ // request shape
548
+ expect(cli.request).toHaveBeenCalledTimes(1);
549
+ const call = cli.request.mock.calls[0][0];
550
+
551
+ expect(call.method).toBe("GET");
552
+ expect(call.url).toBe("/api/v1/brokers/market/stock");
553
+ expect(call.params).toEqual({
554
+ region,
555
+ sortBy: BrokerSort.TotalVolume,
556
+ sortDirection: SortDirection.Desc,
478
557
  fromDate,
479
558
  toDate,
480
- 0,
481
- 5
482
- );
559
+ page,
560
+ size,
561
+ });
562
+
563
+ // response values
564
+ expect(res.recordCount).toBe(2);
565
+ expect(res.items).toHaveLength(1);
566
+
567
+ expect(res.totalStats.averageCost).toBe(2.91);
568
+ expect(res.totalStats.netAmount).toBe(1750000.0);
569
+ expect(res.totalStats.totalAmount).toBe(8750000.0);
570
+ expect(res.totalStats.totalVolume).toBe(175000);
571
+ expect(res.totalStats.totalBuyAmount).toBe(5250000.0);
572
+ expect(res.totalStats.totalBuyVolume).toBe(87500);
573
+ expect(res.totalStats.totalSellAmount).toBe(3500000.0);
574
+ expect(res.totalStats.totalSellVolume).toBe(87500);
575
+
576
+ const item = res.items[0];
577
+ expect(item.stock?.id).toBe("61dd0d670ec2114146342fa5");
578
+ expect(item.stock?.name).toBe("SASA Polyester");
579
+ expect(item.stock?.symbol).toBe("SASA");
580
+ expect(item.stock?.assetType).toBe(AssetType.Stock);
581
+ expect(item.stock?.assetClass).toBe(AssetClass.Equity);
582
+
583
+ expect(item.averageCost).toBe(2.91);
584
+ expect(item.netAmount).toBe(1000000.0);
585
+ expect(item.totalAmount).toBe(5000000.0);
586
+ expect(item.totalVolume).toBe(100000);
587
+ expect(item.totalBuyAmount).toBe(3000000.0);
588
+ expect(item.totalBuyVolume).toBe(50000);
589
+ expect(item.totalSellAmount).toBe(2000000.0);
590
+ expect(item.totalSellVolume).toBe(50000);
483
591
  });
484
592
  });
485
-
593
+
486
594
  describe("getBrokersByStock", () => {
487
- test("should return brokers for specific stock", async () => {
488
- jest.spyOn(brokerClient, 'getBrokersByStock').mockResolvedValue(mockBrokerList);
489
-
490
- const response = await brokerClient.getBrokersByStock(
491
- "TUPRS",
492
- Region.Tr,
595
+ test("should call correct endpoint/params and read values", async () => {
596
+ cli.request.mockResolvedValueOnce({ data: fxGetBrokersByStock });
597
+
598
+ const res = await brokerClient.getBrokersByStock(
599
+ "SASA",
600
+ region,
493
601
  BrokerSort.TotalVolume,
494
602
  SortDirection.Desc,
495
603
  fromDate,
496
604
  toDate,
497
- 0,
498
- 5
605
+ size,
606
+ page
499
607
  );
500
-
501
- expect(response.recordCount).toBe(2);
502
- expect(response.items).toHaveLength(2);
503
- expect(response.totalStats).toEqual(mockBrokerStats);
504
-
505
- const firstItem = response.items[0];
506
- expect(firstItem.broker?.symbol).toBe("BIYKR");
507
- expect(firstItem.averageCost).toBe(20.5);
508
-
509
- expect(brokerClient.getBrokersByStock).toHaveBeenCalledWith(
510
- "TUPRS",
511
- Region.Tr,
512
- BrokerSort.TotalVolume,
513
- SortDirection.Desc,
608
+
609
+ // request shape
610
+ expect(cli.request).toHaveBeenCalledTimes(1);
611
+ const call = cli.request.mock.calls[0][0];
612
+
613
+ expect(call.method).toBe("GET");
614
+ expect(call.url).toBe("/api/v1/brokers/SASA");
615
+ expect(call.params).toEqual({
616
+ region,
617
+ sortBy: BrokerSort.TotalVolume,
618
+ sortDirection: SortDirection.Desc,
514
619
  fromDate,
515
620
  toDate,
516
- 0,
517
- 5
621
+ page,
622
+ size,
623
+ });
624
+
625
+ // response values
626
+ expect(res.recordCount).toBe(2);
627
+ expect(res.items).toHaveLength(1);
628
+
629
+ expect(res.totalStats.averageCost).toBe(2.91);
630
+ expect(res.totalStats.netAmount).toBe(800000.0);
631
+ expect(res.totalStats.totalAmount).toBe(3500000.0);
632
+ expect(res.totalStats.totalVolume).toBe(70000);
633
+ expect(res.totalStats.totalBuyAmount).toBe(2150000.0);
634
+ expect(res.totalStats.totalBuyVolume).toBe(35000);
635
+ expect(res.totalStats.totalSellAmount).toBe(1350000.0);
636
+ expect(res.totalStats.totalSellVolume).toBe(35000);
637
+
638
+ const item = res.items[0];
639
+ expect(item.broker?.id).toBe(1);
640
+ expect(item.broker?.symbol).toBe("BIMLB");
641
+ expect(item.broker?.name).toBe("BIMLB");
642
+ expect(item.broker?.longName).toBe("BIM Yatırım Menkul Değerler A.Ş.");
643
+ expect(item.broker?.logo).toBe(
644
+ "https://finfree-storage.s3.eu-central-1.amazonaws.com/broker-logos/bimlb.png"
518
645
  );
646
+
647
+ expect(item.averageCost).toBe(2.91);
648
+ expect(item.netAmount).toBe(500000.0);
649
+ expect(item.totalAmount).toBe(2000000.0);
650
+ expect(item.totalVolume).toBe(40000);
651
+ expect(item.totalBuyAmount).toBe(1250000.0);
652
+ expect(item.totalBuyVolume).toBe(20000);
653
+ expect(item.totalSellAmount).toBe(750000.0);
654
+ expect(item.totalSellVolume).toBe(20000);
519
655
  });
520
-
521
- test("should handle invalid stock symbol", async () => {
522
- jest.spyOn(brokerClient, 'getBrokersByStock').mockRejectedValue(new Error("Invalid stock symbol"));
523
-
524
- await expect(brokerClient.getBrokersByStock(
525
- "INVALID",
526
- Region.Tr,
527
- BrokerSort.TotalVolume,
528
- SortDirection.Desc,
529
- fromDate,
530
- toDate,
531
- 0,
532
- 5
533
- )).rejects.toThrow("Invalid stock symbol");
656
+
657
+ test("should bubble up request error", async () => {
658
+ cli.request.mockRejectedValueOnce(new Error("Invalid stock symbol"));
659
+
660
+ await expect(
661
+ brokerClient.getBrokersByStock(
662
+ "INVALID",
663
+ region,
664
+ BrokerSort.TotalVolume,
665
+ SortDirection.Desc,
666
+ fromDate,
667
+ toDate,
668
+ size,
669
+ page
670
+ )
671
+ ).rejects.toThrow("Invalid stock symbol");
534
672
  });
535
673
  });
536
-
674
+
537
675
  describe("getStocksByBroker", () => {
538
- test("should return stocks for specific broker", async () => {
539
- jest.spyOn(brokerClient, 'getStocksByBroker').mockResolvedValue(mockBrokerList);
540
-
541
- const response = await brokerClient.getStocksByBroker(
542
- "BIYKR",
543
- Region.Tr,
676
+ test("should call correct endpoint/params and read values", async () => {
677
+ cli.request.mockResolvedValueOnce({ data: fxGetStocksByBroker });
678
+
679
+ const res = await brokerClient.getStocksByBroker(
680
+ "BIMLB",
681
+ region,
544
682
  BrokerSort.TotalVolume,
545
683
  SortDirection.Desc,
546
684
  fromDate,
547
685
  toDate,
548
- 0,
549
- 5
686
+ size,
687
+ page
550
688
  );
551
-
552
- expect(response.recordCount).toBe(2);
553
- expect(response.items).toHaveLength(2);
554
- expect(response.totalStats).toEqual(mockBrokerStats);
555
-
556
- const firstItem = response.items[0];
557
- expect(firstItem.stock?.symbol).toBe("TUPRS");
558
- expect(firstItem.stock?.assetType).toBe(AssetType.Stock);
559
-
560
- expect(brokerClient.getStocksByBroker).toHaveBeenCalledWith(
561
- "BIYKR",
562
- Region.Tr,
563
- BrokerSort.TotalVolume,
564
- SortDirection.Desc,
689
+
690
+ // request shape
691
+ expect(cli.request).toHaveBeenCalledTimes(1);
692
+ const call = cli.request.mock.calls[0][0];
693
+
694
+ expect(call.method).toBe("GET");
695
+ expect(call.url).toBe("/api/v1/brokers/stock/BIMLB");
696
+ expect(call.params).toEqual({
697
+ region,
698
+ sortBy: BrokerSort.TotalVolume,
699
+ sortDirection: SortDirection.Desc,
565
700
  fromDate,
566
701
  toDate,
567
- 0,
568
- 5
569
- );
702
+ page,
703
+ size,
704
+ });
705
+
706
+ // response values
707
+ expect(res.recordCount).toBe(2);
708
+ expect(res.items).toHaveLength(1);
709
+
710
+ expect(res.totalStats.netAmount).toBe(800000.0);
711
+ expect(res.totalStats.totalAmount).toBe(3500000.0);
712
+ expect(res.totalStats.totalVolume).toBe(70000);
713
+ expect(res.totalStats.totalBuyAmount).toBe(2150000.0);
714
+ expect(res.totalStats.totalBuyVolume).toBe(35000);
715
+ expect(res.totalStats.totalSellAmount).toBe(1350000.0);
716
+ expect(res.totalStats.totalSellVolume).toBe(35000);
717
+
718
+ const item = res.items[0];
719
+ expect(item.stock?.id).toBe("61dd0d670ec2114146342fa5");
720
+ expect(item.stock?.name).toBe("SASA Polyester");
721
+ expect(item.stock?.symbol).toBe("SASA");
722
+ expect(item.stock?.assetType).toBe(AssetType.Stock);
723
+ expect(item.stock?.assetClass).toBe(AssetClass.Equity);
724
+
725
+ expect(item.netAmount).toBe(500000.0);
726
+ expect(item.totalAmount).toBe(2000000.0);
727
+ expect(item.totalVolume).toBe(40000);
728
+ expect(item.totalBuyAmount).toBe(1250000.0);
729
+ expect(item.totalBuyVolume).toBe(20000);
730
+ expect(item.totalSellAmount).toBe(750000.0);
731
+ expect(item.totalSellVolume).toBe(20000);
570
732
  });
571
-
572
- test("should handle invalid broker symbol", async () => {
573
- jest.spyOn(brokerClient, 'getStocksByBroker').mockRejectedValue(new Error("Invalid broker symbol"));
574
-
575
- await expect(brokerClient.getStocksByBroker(
576
- "INVALID",
577
- Region.Tr,
578
- BrokerSort.TotalVolume,
579
- SortDirection.Desc,
580
- fromDate,
581
- toDate,
582
- 0,
583
- 5
584
- )).rejects.toThrow("Invalid broker symbol");
733
+
734
+ test("should bubble up request error", async () => {
735
+ cli.request.mockRejectedValueOnce(new Error("Invalid broker symbol"));
736
+
737
+ await expect(
738
+ brokerClient.getStocksByBroker(
739
+ "INVALID",
740
+ region,
741
+ BrokerSort.TotalVolume,
742
+ SortDirection.Desc,
743
+ fromDate,
744
+ toDate,
745
+ size,
746
+ page
747
+ )
748
+ ).rejects.toThrow("Invalid broker symbol");
585
749
  });
586
750
  });
587
-
751
+
588
752
  describe("getBrokers", () => {
589
- test("should return paginated broker list", async () => {
590
- jest.spyOn(brokerClient, 'getBrokers').mockResolvedValue(mockBrokersPaginatedResponse);
591
-
592
- const response = await brokerClient.getBrokers(
593
- Region.Tr,
594
- 0,
595
- 5,
596
- AssetClass.Equity
597
- );
598
-
599
- expect(response.recordCount).toBe(2);
600
- expect(response.items).toHaveLength(2);
601
-
602
- const firstBroker = response.items[0];
603
- expect(firstBroker.id).toBe(1001);
604
- expect(firstBroker.symbol).toBe("BIYKR");
605
- expect(firstBroker.name).toBe("BİYİKLI YATIRIM");
606
- expect(firstBroker.longName).toBe("Bıyıklı Yatırım Menkul Değerler A.Ş.");
607
- expect(firstBroker.logo).toBe("https://example.com/biykr.png");
608
- expect(firstBroker.supportedAssetClasses).toEqual([AssetClass.Equity]);
609
-
610
- expect(brokerClient.getBrokers).toHaveBeenCalledWith(
611
- Region.Tr,
612
- 0,
613
- 5,
614
- AssetClass.Equity
615
- );
616
- });
617
-
618
- test("should handle getBrokers without assetClass parameter", async () => {
619
- jest.spyOn(brokerClient, 'getBrokers').mockResolvedValue(mockBrokersPaginatedResponse);
620
-
621
- const response = await brokerClient.getBrokers(
622
- Region.Tr,
623
- 0,
624
- 5
625
- );
626
-
627
- expect(response.recordCount).toBe(2);
628
- expect(response.items).toHaveLength(2);
629
-
630
- const firstBroker = response.items[0];
631
- expect(firstBroker.id).toBe(1001);
632
- expect(firstBroker.symbol).toBe("BIYKR");
633
- expect(firstBroker.name).toBe("BİYİKLI YATIRIM");
634
- expect(firstBroker.longName).toBe("Bıyıklı Yatırım Menkul Değerler A.Ş.");
635
- expect(firstBroker.logo).toBe("https://example.com/biykr.png");
636
-
637
- expect(brokerClient.getBrokers).toHaveBeenCalledWith(
638
- Region.Tr,
639
- 0,
640
- 5
641
- );
642
- });
643
-
644
- test("should handle empty brokers response", async () => {
645
- const emptyResponse: PaginatedResponse<Broker> = {
646
- recordCount: 0,
647
- items: []
648
- };
649
- jest.spyOn(brokerClient, 'getBrokers').mockResolvedValue(emptyResponse);
650
-
651
- const response = await brokerClient.getBrokers(
652
- Region.Tr,
653
- 0,
654
- 10
753
+ test("should call correct endpoint/params and read values", async () => {
754
+ cli.request.mockResolvedValueOnce({ data: fxGetBrokers });
755
+
756
+ const res = await brokerClient.getBrokers(region, 10, 0);
757
+
758
+ // request shape
759
+ expect(cli.request).toHaveBeenCalledTimes(1);
760
+ const call = cli.request.mock.calls[0][0];
761
+
762
+ expect(call.method).toBe("GET");
763
+ expect(call.url).toBe("/api/v1/brokers");
764
+ expect(call.params).toEqual({
765
+ region,
766
+ size: 10,
767
+ page: 0,
768
+ });
769
+
770
+ // response values
771
+ expect(res.recordCount).toBe(239);
772
+ expect(res.items).toHaveLength(1);
773
+
774
+ const broker = res.items[0];
775
+ expect(broker.id).toBe(1);
776
+ expect(broker.symbol).toBe("BIDZY");
777
+ expect(broker.name).toBe("DENIZ YATIRIM");
778
+ expect(broker.longName).toBe("DENIZ YATIRIM MENKUL KIYMETLER A.S.");
779
+ expect(broker.logo).toBe(
780
+ "https://finfree-storage.s3.eu-central-1.amazonaws.com/brokers/BIDZY.svg"
655
781
  );
656
-
657
- expect(response.recordCount).toBe(0);
658
- expect(response.items).toHaveLength(0);
782
+ expect(broker.supportedAssetClasses).toEqual([AssetClass.Equity]);
659
783
  });
660
-
661
- test("should handle getBrokers error", async () => {
662
- jest.spyOn(brokerClient, 'getBrokers').mockRejectedValue(new Error("Unsupported asset class"));
663
-
664
- await expect(brokerClient.getBrokers(
665
- Region.Tr,
666
- 0,
667
- 10,
668
- AssetClass.Crypto
669
- )).rejects.toThrow("Unsupported asset class");
784
+
785
+ test("should include assetClass when provided", async () => {
786
+ cli.request.mockResolvedValueOnce({ data: fxGetBrokers });
787
+
788
+ await brokerClient.getBrokers(region, 10, 0, AssetClass.Equity);
789
+
790
+ const call = cli.request.mock.calls[0][0];
791
+ expect(call.params).toEqual({
792
+ region,
793
+ size: 10,
794
+ page: 0,
795
+ assetClass: AssetClass.Equity,
796
+ });
670
797
  });
671
798
  });
672
799
  });
673
- });
800
+ });