laplace-api 4.8.0 → 5.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.
- package/package.json +1 -1
- package/src/client/broker.ts +17 -17
- package/src/client/capital_increase.ts +9 -10
- package/src/client/client.ts +40 -40
- package/src/client/collections.ts +26 -14
- package/src/client/custom_theme.ts +15 -14
- package/src/client/financial_fundamentals.ts +29 -27
- package/src/client/financial_ratios.ts +29 -47
- package/src/client/funds.ts +10 -5
- package/src/client/key-insights.ts +1 -1
- package/src/client/live-price-web-socket.ts +3 -1
- package/src/client/live-price.ts +18 -44
- package/src/client/news.ts +106 -107
- package/src/client/search.ts +13 -9
- package/src/client/stocks.ts +42 -12
- package/src/test/broker.test.ts +580 -453
- package/src/test/capital_increase.test.ts +131 -82
- package/src/test/collections.test.ts +489 -268
- package/src/test/custom_theme.test.ts +250 -144
- package/src/test/financial_fundamentals.test.ts +171 -202
- package/src/test/financial_ratios.test.ts +222 -170
- package/src/test/funds.test.ts +231 -162
- package/src/test/helpers.ts +23 -27
- package/src/test/key-insight.test.ts +71 -36
- package/src/test/live-price.test.ts +135 -1
- package/src/test/news.test.ts +399 -169
- package/src/test/politician.test.ts +176 -187
- package/src/test/readme.test.ts +12 -13
- package/src/test/search.test.ts +144 -170
- package/src/test/stocks.test.ts +306 -370
- package/src/utilities/test.env +0 -2
package/src/test/news.test.ts
CHANGED
|
@@ -1,204 +1,434 @@
|
|
|
1
1
|
import { Logger } from "winston";
|
|
2
|
+
import axios from "axios";
|
|
2
3
|
import { LaplaceConfiguration } from "../utilities/configuration";
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
NewsType,
|
|
8
|
-
NewsOrderBy,
|
|
5
|
+
NewsClient,
|
|
6
|
+
NewsType,
|
|
7
|
+
NewsOrderBy,
|
|
9
8
|
} from "../client/news";
|
|
10
9
|
import "./client_test_suite";
|
|
11
10
|
import { Region, Locale } from "../client/collections";
|
|
12
11
|
import { SortDirection } from "../client/broker";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
|
|
13
|
+
const mockNewsHighlightsResponse = {
|
|
14
|
+
tech: [
|
|
15
|
+
"Alphabet ve Amazon'un desteğiyle Anthropic, 2026 başlarında Hindistan'ın Bengaluru kentinde bir ofis açacak."
|
|
16
|
+
],
|
|
17
|
+
other: [
|
|
18
|
+
"ABD Yüksek Mahkemesi, Epic Games'in davası kapsamında Google'ın Play uygulamalarındaki değişikliği engellemeyecek."
|
|
19
|
+
],
|
|
20
|
+
finance: [
|
|
21
|
+
"Fifth Third Bank, Comerica'yı 10,9 milyar dolara satın alacak ve böylece ABD'nin 9. en büyük bankası olacak."
|
|
22
|
+
],
|
|
23
|
+
consumer: [
|
|
24
|
+
"Tesla, rekabet ortamında pazar payını geri almak için daha ucuz Model Y ve Model 3'ü piyasaya sürdü; duyuru hisseleri etkiledi."
|
|
25
|
+
],
|
|
26
|
+
healthcare: [
|
|
27
|
+
"İlaç üreticileri, Amgen ve Novo Nordisk'in de dahil olduğu şekilde, Trump'ın ilaç fiyatlarını düşürme planıyla uyumlu olarak tele-sağlık satışlarını artırıyor."
|
|
28
|
+
],
|
|
29
|
+
energyAndUtilities: [
|
|
30
|
+
"ABD Enerji Bakanlığı, Stellantis ve GM'ye verilen 1,1 milyar dolarlık hibeleri iptal edebilir."
|
|
31
|
+
],
|
|
32
|
+
industrialsAndMaterials: [
|
|
33
|
+
"Boeing, bir grevi sona erdirmek için IAM Sendikası ile geçici bir anlaşmaya vardı; detaylar açıklanmadı."
|
|
34
|
+
]
|
|
23
35
|
};
|
|
24
36
|
|
|
25
|
-
const mockNewsResponse
|
|
37
|
+
const mockNewsResponse = {
|
|
38
|
+
items: [
|
|
26
39
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
name: "Example Publisher",
|
|
33
|
-
logoUrl: "https://example.com/logo.png"
|
|
34
|
-
},
|
|
35
|
-
relatedTickers: [
|
|
36
|
-
{
|
|
37
|
-
id: "1",
|
|
38
|
-
name: "Ticker 1",
|
|
39
|
-
symbol: "TCK1"
|
|
40
|
-
}
|
|
40
|
+
url: "https://www.reuters.com/business/energy/commonwealth-lng-wants-more-time-build-planned-export-facility-louisiana-2025-10-07/",
|
|
41
|
+
content: {
|
|
42
|
+
title: "Commonwealth LNG wants more time to build planned export facility in Louisiana",
|
|
43
|
+
content: [
|
|
44
|
+
"Commonwealth LNG has requested a four-year extension from federal regulators to construct & begin exporting liquefied natural gas..."
|
|
41
45
|
],
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
summary: [
|
|
47
|
+
"Commonwealth LNG has requested a four-year extension from federal regulators..."
|
|
48
|
+
],
|
|
49
|
+
description:
|
|
50
|
+
"Commonwealth LNG has asked federal regulators for a four-year extension...",
|
|
51
|
+
investorInsight:
|
|
52
|
+
"What it means for investors: The extension request could postpone..."
|
|
53
|
+
},
|
|
54
|
+
sectors: { name: "Energy", meanType: 9, newsCount: 1 },
|
|
55
|
+
tickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }],
|
|
56
|
+
imageUrl: "",
|
|
57
|
+
createdAt: "2025-10-07T17:10:01.560644Z",
|
|
58
|
+
publisher: { name: "Reuters", logoUrl: null },
|
|
59
|
+
timestamp: "2025-10-07T16:50:16Z",
|
|
60
|
+
categories: { name: "Sector News", newsCount: 1, categoryType: "StockSpesific" },
|
|
61
|
+
industries: { name: "Oil/Gas (Production and Exploration)", meanType: 78 },
|
|
62
|
+
publisherUrl: "Reuters",
|
|
63
|
+
qualityScore: 0,
|
|
64
|
+
relatedTickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }]
|
|
56
65
|
}
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
const mockPaginatedNewsResponse: PaginatedResponse<News> = {
|
|
60
|
-
recordCount: 2,
|
|
61
|
-
items: mockNewsResponse
|
|
66
|
+
],
|
|
67
|
+
recordCount: 352
|
|
62
68
|
};
|
|
63
69
|
|
|
64
|
-
describe("
|
|
70
|
+
describe("NewsClient", () => {
|
|
71
|
+
let client: NewsClient;
|
|
72
|
+
|
|
73
|
+
beforeAll(() => {
|
|
74
|
+
const config = (global as any).testSuite.config as LaplaceConfiguration;
|
|
75
|
+
const logger: Logger = {
|
|
76
|
+
info: jest.fn(),
|
|
77
|
+
error: jest.fn(),
|
|
78
|
+
warn: jest.fn(),
|
|
79
|
+
debug: jest.fn(),
|
|
80
|
+
} as unknown as Logger;
|
|
81
|
+
|
|
82
|
+
client = new NewsClient(config, logger);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("Integration Tests", () => {
|
|
86
|
+
jest.setTimeout(60_000);
|
|
87
|
+
|
|
88
|
+
test("getHighlights returns valid data", async () => {
|
|
89
|
+
const resp = await client.getHighlights(Region.Us, Locale.Tr);
|
|
90
|
+
|
|
91
|
+
expect(resp).toBeDefined();
|
|
92
|
+
|
|
93
|
+
expect(Array.isArray(resp.consumer)).toBe(true);
|
|
94
|
+
expect(Array.isArray(resp.energyAndUtilities)).toBe(true);
|
|
95
|
+
expect(Array.isArray(resp.finance)).toBe(true);
|
|
96
|
+
expect(Array.isArray(resp.healthcare)).toBe(true);
|
|
97
|
+
expect(Array.isArray(resp.industrialsAndMaterials)).toBe(true);
|
|
98
|
+
expect(Array.isArray(resp.tech)).toBe(true);
|
|
99
|
+
expect(Array.isArray(resp.other)).toBe(true);
|
|
100
|
+
|
|
101
|
+
const first = resp.tech?.[0];
|
|
102
|
+
if (first != null) expect(typeof first).toBe("string");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("getNews returns valid paginated data", async () => {
|
|
106
|
+
const resp = await client.getNews(
|
|
107
|
+
Region.Us,
|
|
108
|
+
Locale.Tr,
|
|
109
|
+
NewsType.BRIEFS,
|
|
110
|
+
0,
|
|
111
|
+
10,
|
|
112
|
+
NewsOrderBy.TIMESTAMP,
|
|
113
|
+
SortDirection.Desc
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(resp).toBeDefined();
|
|
117
|
+
expect(typeof resp.recordCount).toBe("number");
|
|
118
|
+
expect(resp.recordCount).toBeGreaterThanOrEqual(0);
|
|
119
|
+
expect(Array.isArray(resp.items)).toBe(true);
|
|
120
|
+
|
|
121
|
+
if (resp.items.length > 0) {
|
|
122
|
+
const n = resp.items[0];
|
|
123
|
+
|
|
124
|
+
expect(typeof n.url).toBe("string");
|
|
125
|
+
expect(typeof n.imageUrl).toBe("string");
|
|
126
|
+
expect(typeof n.timestamp).toBe("string");
|
|
127
|
+
expect(typeof n.publisherUrl).toBe("string");
|
|
128
|
+
expect(typeof n.qualityScore).toBe("number");
|
|
129
|
+
expect(typeof n.createdAt).toBe("string");
|
|
130
|
+
|
|
131
|
+
expect(n.publisher).toBeDefined();
|
|
132
|
+
expect(typeof n.publisher.name).toBe("string");
|
|
133
|
+
expect(
|
|
134
|
+
typeof n.publisher.logoUrl === "string" || n.publisher.logoUrl == null
|
|
135
|
+
).toBe(true);
|
|
136
|
+
|
|
137
|
+
expect(Array.isArray(n.relatedTickers)).toBe(true);
|
|
138
|
+
if (n.relatedTickers.length > 0) {
|
|
139
|
+
const t = n.relatedTickers[0];
|
|
140
|
+
expect(typeof t.id).toBe("string");
|
|
141
|
+
expect(typeof t.name).toBe("string");
|
|
142
|
+
expect(typeof t.symbol === "string" || t.symbol == null).toBe(true);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (n.tickers != null) {
|
|
146
|
+
expect(Array.isArray(n.tickers)).toBe(true);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (n.categories != null) {
|
|
150
|
+
expect(typeof n.categories.name).toBe("string");
|
|
151
|
+
expect(typeof n.categories.newsCount).toBe("number");
|
|
152
|
+
expect(
|
|
153
|
+
typeof n.categories.categoryType === "string" ||
|
|
154
|
+
n.categories.categoryType == null ||
|
|
155
|
+
n.categories.categoryType === undefined
|
|
156
|
+
).toBe(true);
|
|
157
|
+
expect(
|
|
158
|
+
typeof n.categories.meanType === "number" ||
|
|
159
|
+
n.categories.meanType == null ||
|
|
160
|
+
n.categories.meanType === undefined
|
|
161
|
+
).toBe(true);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (n.sectors != null) {
|
|
165
|
+
expect(typeof n.sectors.name).toBe("string");
|
|
166
|
+
expect(typeof n.sectors.newsCount).toBe("number");
|
|
167
|
+
expect(
|
|
168
|
+
typeof n.sectors.categoryType === "string" ||
|
|
169
|
+
n.sectors.categoryType == null ||
|
|
170
|
+
n.sectors.categoryType === undefined
|
|
171
|
+
).toBe(true);
|
|
172
|
+
expect(
|
|
173
|
+
typeof n.sectors.meanType === "number" ||
|
|
174
|
+
n.sectors.meanType == null ||
|
|
175
|
+
n.sectors.meanType === undefined
|
|
176
|
+
).toBe(true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (n.industries != null) {
|
|
180
|
+
expect(typeof n.industries.name).toBe("string");
|
|
181
|
+
expect(typeof n.industries.meanType).toBe("number");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (n.content != null) {
|
|
185
|
+
expect(typeof n.content.title).toBe("string");
|
|
186
|
+
expect(typeof n.content.description).toBe("string");
|
|
187
|
+
expect(Array.isArray(n.content.content)).toBe(true);
|
|
188
|
+
expect(Array.isArray(n.content.summary)).toBe(true);
|
|
189
|
+
expect(typeof n.content.investorInsight).toBe("string");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("streamNews yields item before timeout or throws gracefully if none arrive", async () => {
|
|
195
|
+
let newsItemsReceived = 0;
|
|
196
|
+
const { events, cancel } = client.streamNews(Locale.Tr);
|
|
197
|
+
|
|
198
|
+
const receivePromise = (async () => {
|
|
199
|
+
for await (const items of events) {
|
|
200
|
+
if (items && items.length > 0) {
|
|
201
|
+
newsItemsReceived += items.length;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
})();
|
|
206
|
+
|
|
207
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, 8000));
|
|
208
|
+
await Promise.race([receivePromise, timeoutPromise]);
|
|
209
|
+
cancel();
|
|
210
|
+
expect(newsItemsReceived).toBeGreaterThanOrEqual(0);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("Mock Tests", () => {
|
|
65
215
|
let client: NewsClient;
|
|
216
|
+
let cli: { request: jest.Mock };
|
|
217
|
+
|
|
218
|
+
beforeEach(() => {
|
|
219
|
+
cli = { request: jest.fn() };
|
|
220
|
+
|
|
221
|
+
const config = (global as any).testSuite.config as LaplaceConfiguration;
|
|
222
|
+
const logger: Logger = {
|
|
223
|
+
info: jest.fn(),
|
|
224
|
+
error: jest.fn(),
|
|
225
|
+
warn: jest.fn(),
|
|
226
|
+
debug: jest.fn()
|
|
227
|
+
} as unknown as Logger;
|
|
228
|
+
|
|
229
|
+
client = new NewsClient(config, logger, cli as any);
|
|
230
|
+
});
|
|
66
231
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
info: jest.fn(),
|
|
71
|
-
error: jest.fn(),
|
|
72
|
-
warn: jest.fn(),
|
|
73
|
-
debug: jest.fn(),
|
|
74
|
-
} as unknown as Logger;
|
|
232
|
+
describe("getHighlights", () => {
|
|
233
|
+
test("calls correct endpoint/params and matches raw response", async () => {
|
|
234
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsHighlightsResponse });
|
|
75
235
|
|
|
76
|
-
|
|
236
|
+
const resp = await client.getHighlights(Region.Tr, Locale.Tr);
|
|
237
|
+
|
|
238
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
239
|
+
const call = cli.request.mock.calls[0][0];
|
|
240
|
+
|
|
241
|
+
expect(call.method).toBe("GET");
|
|
242
|
+
expect(call.url).toBe("/api/v1/news/highlights");
|
|
243
|
+
expect(call.params).toEqual({ region: Region.Tr, locale: Locale.Tr });
|
|
244
|
+
|
|
245
|
+
expect(resp.consumer).toEqual(mockNewsHighlightsResponse.consumer);
|
|
246
|
+
expect(resp.energyAndUtilities).toEqual(mockNewsHighlightsResponse.energyAndUtilities);
|
|
247
|
+
expect(resp.finance).toEqual(mockNewsHighlightsResponse.finance);
|
|
248
|
+
expect(resp.healthcare).toEqual(mockNewsHighlightsResponse.healthcare);
|
|
249
|
+
expect(resp.industrialsAndMaterials).toEqual(mockNewsHighlightsResponse.industrialsAndMaterials);
|
|
250
|
+
expect(resp.tech).toEqual(mockNewsHighlightsResponse.tech);
|
|
251
|
+
expect(resp.other).toEqual(mockNewsHighlightsResponse.other);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("bubbles up request error", async () => {
|
|
255
|
+
cli.request.mockRejectedValueOnce(new Error("Failed to fetch highlights"));
|
|
256
|
+
|
|
257
|
+
await expect(client.getHighlights(Region.Tr, Locale.Tr)).rejects.toThrow(
|
|
258
|
+
"Failed to fetch highlights"
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
262
|
+
});
|
|
77
263
|
});
|
|
78
264
|
|
|
79
|
-
describe("
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
265
|
+
describe("getNews", () => {
|
|
266
|
+
test("calls correct endpoint/params and matches raw response", async () => {
|
|
267
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
|
|
268
|
+
|
|
269
|
+
const resp = await client.getNews(
|
|
270
|
+
Region.Tr,
|
|
271
|
+
Locale.Tr,
|
|
272
|
+
NewsType.BRIEFS,
|
|
273
|
+
1,
|
|
274
|
+
10,
|
|
275
|
+
NewsOrderBy.TIMESTAMP,
|
|
276
|
+
SortDirection.Desc,
|
|
277
|
+
undefined
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
281
|
+
const call = cli.request.mock.calls[0][0];
|
|
282
|
+
|
|
283
|
+
expect(call.method).toBe("GET");
|
|
284
|
+
expect(call.url).toBe("/api/v1/news");
|
|
285
|
+
expect(call.params).toEqual({
|
|
286
|
+
region: Region.Tr,
|
|
287
|
+
locale: Locale.Tr,
|
|
288
|
+
newsType: NewsType.BRIEFS,
|
|
289
|
+
page: 1,
|
|
290
|
+
size: 10,
|
|
291
|
+
orderBy: NewsOrderBy.TIMESTAMP,
|
|
292
|
+
orderByDirection: SortDirection.Desc
|
|
93
293
|
});
|
|
94
294
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
295
|
+
expect(resp.recordCount).toBe(352);
|
|
296
|
+
expect(resp.items).toHaveLength(1);
|
|
297
|
+
|
|
298
|
+
const n = resp.items[0];
|
|
299
|
+
|
|
300
|
+
expect(n.url).toBe(mockNewsResponse.items[0].url);
|
|
301
|
+
expect(n.imageUrl).toBe(mockNewsResponse.items[0].imageUrl);
|
|
302
|
+
expect(n.timestamp).toBe(mockNewsResponse.items[0].timestamp);
|
|
303
|
+
expect(n.publisherUrl).toBe(mockNewsResponse.items[0].publisherUrl);
|
|
304
|
+
expect(n.qualityScore).toBe(mockNewsResponse.items[0].qualityScore);
|
|
305
|
+
expect(n.createdAt).toBe(mockNewsResponse.items[0].createdAt);
|
|
306
|
+
|
|
307
|
+
expect(n.publisher.name).toBe(mockNewsResponse.items[0].publisher.name);
|
|
308
|
+
expect(n.publisher.logoUrl).toBeNull();
|
|
309
|
+
|
|
310
|
+
expect(n.relatedTickers).toHaveLength(1);
|
|
311
|
+
expect(n.relatedTickers[0].id).toBe(mockNewsResponse.items[0].relatedTickers[0].id);
|
|
312
|
+
expect(n.relatedTickers[0].name).toBe(mockNewsResponse.items[0].relatedTickers[0].name);
|
|
313
|
+
expect(n.relatedTickers[0].symbol).toBe(mockNewsResponse.items[0].relatedTickers[0].symbol);
|
|
314
|
+
|
|
315
|
+
expect(n.tickers).toHaveLength(1);
|
|
316
|
+
expect(n.tickers![0].symbol).toBe("EQT");
|
|
317
|
+
|
|
318
|
+
expect(n.categories?.name).toBe(mockNewsResponse.items[0].categories.name);
|
|
319
|
+
expect(n.categories?.newsCount).toBe(mockNewsResponse.items[0].categories.newsCount);
|
|
320
|
+
expect(n.categories?.categoryType).toBe(mockNewsResponse.items[0].categories.categoryType);
|
|
321
|
+
|
|
322
|
+
expect(n.sectors?.name).toBe(mockNewsResponse.items[0].sectors.name);
|
|
323
|
+
expect(n.sectors?.newsCount).toBe(mockNewsResponse.items[0].sectors.newsCount);
|
|
324
|
+
expect(n.sectors?.meanType).toBe(mockNewsResponse.items[0].sectors.meanType);
|
|
325
|
+
|
|
326
|
+
expect(n.industries?.name).toBe(mockNewsResponse.items[0].industries.name);
|
|
327
|
+
expect(n.industries?.meanType).toBe(mockNewsResponse.items[0].industries.meanType);
|
|
328
|
+
|
|
329
|
+
expect(n.content?.title).toBe(mockNewsResponse.items[0].content.title);
|
|
330
|
+
expect(n.content?.description).toBe(mockNewsResponse.items[0].content.description);
|
|
331
|
+
expect(n.content?.content).toEqual(mockNewsResponse.items[0].content.content);
|
|
332
|
+
expect(n.content?.summary).toEqual(mockNewsResponse.items[0].content.summary);
|
|
333
|
+
expect(n.content?.investorInsight).toBe(mockNewsResponse.items[0].content.investorInsight);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("does not send optional params when undefined", async () => {
|
|
337
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
|
|
338
|
+
|
|
339
|
+
await client.getNews(Region.Tr, Locale.Tr);
|
|
340
|
+
|
|
341
|
+
const call = cli.request.mock.calls[0][0];
|
|
342
|
+
expect(call.params).toEqual({
|
|
343
|
+
region: Region.Tr,
|
|
344
|
+
locale: Locale.Tr
|
|
120
345
|
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("bubbles up request error", async () => {
|
|
349
|
+
cli.request.mockRejectedValueOnce(new Error("Failed to fetch news"));
|
|
350
|
+
|
|
351
|
+
await expect(
|
|
352
|
+
client.getNews(
|
|
353
|
+
Region.Tr,
|
|
354
|
+
Locale.Tr,
|
|
355
|
+
NewsType.REUTERS,
|
|
356
|
+
0,
|
|
357
|
+
10,
|
|
358
|
+
NewsOrderBy.TIMESTAMP,
|
|
359
|
+
SortDirection.Desc
|
|
360
|
+
)
|
|
361
|
+
).rejects.toThrow("Failed to fetch news");
|
|
362
|
+
|
|
363
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
364
|
+
});
|
|
121
365
|
});
|
|
122
366
|
|
|
123
|
-
describe("
|
|
124
|
-
|
|
125
|
-
|
|
367
|
+
describe("streamNews", () => {
|
|
368
|
+
test("calls correct endpoint/params and correctly yields stream entities", async () => {
|
|
369
|
+
const eventsList: any[] = [];
|
|
370
|
+
|
|
371
|
+
// Mock get response to return a readable stream
|
|
372
|
+
const mockStreamData = [
|
|
373
|
+
"data: " + JSON.stringify([{ url: "http://example.com/stream-news-1", publiser: { name: "test-publisher" } }]) + "\n\n",
|
|
374
|
+
"data: " + JSON.stringify([{ url: "http://example.com/stream-news-2", publiser: { name: "test-publisher-2" } }]) + "\n\n",
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
const mockAsyncIterator = {
|
|
378
|
+
async *[Symbol.asyncIterator]() {
|
|
379
|
+
for (const chunk of mockStreamData) {
|
|
380
|
+
yield new TextEncoder().encode(chunk);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
|
|
386
|
+
data: mockAsyncIterator
|
|
126
387
|
});
|
|
127
388
|
|
|
128
|
-
|
|
129
|
-
test("should return news highlights with mock data", async () => {
|
|
130
|
-
jest.spyOn(client, 'getHighlights').mockResolvedValue(mockNewsHighlightsResponse);
|
|
389
|
+
const { events, cancel } = client.streamNews(Locale.Tr);
|
|
131
390
|
|
|
132
|
-
|
|
391
|
+
for await (const newsList of events) {
|
|
392
|
+
eventsList.push(newsList);
|
|
393
|
+
}
|
|
133
394
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
expect(resp.tech).toHaveLength(1);
|
|
395
|
+
expect(axiosGetSpy).toHaveBeenCalledTimes(1);
|
|
396
|
+
const callArgs = axiosGetSpy.mock.calls[0];
|
|
397
|
+
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=tr`);
|
|
398
|
+
expect(callArgs[1]?.responseType).toBe('stream');
|
|
139
399
|
|
|
140
|
-
|
|
141
|
-
|
|
400
|
+
expect(eventsList).toHaveLength(2);
|
|
401
|
+
expect(eventsList[0][0].url).toBe("http://example.com/stream-news-1");
|
|
402
|
+
expect(eventsList[1][0].url).toBe("http://example.com/stream-news-2");
|
|
142
403
|
|
|
143
|
-
|
|
144
|
-
|
|
404
|
+
cancel();
|
|
405
|
+
axiosGetSpy.mockRestore();
|
|
406
|
+
});
|
|
145
407
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
408
|
+
test("calls correct endpoint with optional parameters", async () => {
|
|
409
|
+
const mockAsyncIterator = {
|
|
410
|
+
async *[Symbol.asyncIterator]() {
|
|
411
|
+
yield new TextEncoder().encode("data: " + JSON.stringify([]) + "\n\n");
|
|
412
|
+
}
|
|
413
|
+
};
|
|
150
414
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
jest.spyOn(client, 'getNews').mockResolvedValue(mockPaginatedNewsResponse);
|
|
154
|
-
|
|
155
|
-
const resp = await client.getNews(
|
|
156
|
-
Region.Tr,
|
|
157
|
-
Locale.Tr,
|
|
158
|
-
NewsType.BRIEFS,
|
|
159
|
-
1,
|
|
160
|
-
10,
|
|
161
|
-
NewsOrderBy.TIMESTAMP,
|
|
162
|
-
SortDirection.Desc,
|
|
163
|
-
null
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
expect(resp.items).toHaveLength(2);
|
|
167
|
-
expect(resp.recordCount).toBe(2);
|
|
168
|
-
|
|
169
|
-
const firstNews = resp.items[0];
|
|
170
|
-
expect(firstNews.url).toBe("https://example.com/news1");
|
|
171
|
-
expect(firstNews.publisher.name).toBe("Example Publisher");
|
|
172
|
-
expect(firstNews.relatedTickers).toHaveLength(1);
|
|
173
|
-
expect(firstNews.qualityScore).toBe(85);
|
|
174
|
-
|
|
175
|
-
expect(client.getNews).toHaveBeenCalledWith(
|
|
176
|
-
Region.Tr,
|
|
177
|
-
Locale.Tr,
|
|
178
|
-
NewsType.BRIEFS,
|
|
179
|
-
1,
|
|
180
|
-
10,
|
|
181
|
-
NewsOrderBy.TIMESTAMP,
|
|
182
|
-
SortDirection.Desc,
|
|
183
|
-
null
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test("should handle API errors for news", async () => {
|
|
188
|
-
jest.spyOn(client, 'getNews').mockRejectedValue(new Error("Failed to fetch news"));
|
|
189
|
-
|
|
190
|
-
await expect(client.getNews(
|
|
191
|
-
Region.Tr,
|
|
192
|
-
Locale.Tr,
|
|
193
|
-
NewsType.BRIEFS,
|
|
194
|
-
1,
|
|
195
|
-
10,
|
|
196
|
-
NewsOrderBy.TIMESTAMP,
|
|
197
|
-
SortDirection.Desc,
|
|
198
|
-
null
|
|
199
|
-
)).rejects.toThrow("Failed to fetch news");
|
|
200
|
-
});
|
|
415
|
+
const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
|
|
416
|
+
data: mockAsyncIterator
|
|
201
417
|
});
|
|
418
|
+
|
|
419
|
+
const { events, cancel } = client.streamNews(Locale.En, ["tech"], ["AAPL"], ["category"], ["software"]);
|
|
420
|
+
|
|
421
|
+
for await (const _ of events) {
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
expect(axiosGetSpy).toHaveBeenCalledTimes(1);
|
|
426
|
+
const callArgs = axiosGetSpy.mock.calls[0];
|
|
427
|
+
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en§ors=tech&tickers=AAPL&categories=category&industries=software`);
|
|
428
|
+
|
|
429
|
+
cancel();
|
|
430
|
+
axiosGetSpy.mockRestore();
|
|
431
|
+
});
|
|
202
432
|
});
|
|
433
|
+
});
|
|
203
434
|
});
|
|
204
|
-
|