laplace-api 5.1.0 → 5.2.1
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/news.ts +137 -91
- package/src/test/news.test.ts +258 -63
package/package.json
CHANGED
package/src/client/news.ts
CHANGED
|
@@ -1,124 +1,170 @@
|
|
|
1
|
-
import {Client} from "./client";
|
|
2
|
-
import {Locale, Region} from "./collections";
|
|
3
|
-
import {PaginatedResponse} from "./capital_increase";
|
|
4
|
-
import {SortDirection} from "./broker";
|
|
1
|
+
import { Client } from "./client";
|
|
2
|
+
import { Locale, Region } from "./collections";
|
|
3
|
+
import { PaginatedResponse } from "./capital_increase";
|
|
4
|
+
import { SortDirection } from "./broker";
|
|
5
5
|
|
|
6
6
|
export interface NewsHighlights {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
consumer: string[];
|
|
8
|
+
energyAndUtilities: string[];
|
|
9
|
+
finance: string[];
|
|
10
|
+
healthcare: string[];
|
|
11
|
+
industrialsAndMaterials: string[];
|
|
12
|
+
tech: string[];
|
|
13
|
+
other: string[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export enum NewsType {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
BRIEFS = "briefs",
|
|
18
|
+
BLOOMBERG = "bloomberg",
|
|
19
|
+
FDA = "fda",
|
|
20
|
+
REUTERS = "reuters",
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export enum NewsOrderBy {
|
|
24
|
-
|
|
24
|
+
TIMESTAMP = "timestamp",
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface News {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
url: string;
|
|
29
|
+
imageUrl: string;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
publisherUrl: string;
|
|
32
|
+
publisher: NewsPublisher;
|
|
33
|
+
relatedTickers: NewsTicker[];
|
|
34
|
+
qualityScore: number;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
tickers?: NewsTicker[];
|
|
37
|
+
categories?: NewsCategories;
|
|
38
|
+
sectors?: NewsSector;
|
|
39
|
+
content?: NewsContent;
|
|
40
|
+
industries?: NewsIndustry;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export type NewsV2 = Omit<News, "relatedTickers">;
|
|
44
|
+
|
|
43
45
|
export interface NewsPublisher {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
name: string;
|
|
47
|
+
logoUrl: string | null;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export interface NewsTicker {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
symbol?: string;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export interface NewsCategories {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
name: string;
|
|
58
|
+
newsCount: number;
|
|
59
|
+
categoryType?: string | null;
|
|
60
|
+
meanType?: number | null;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
export interface NewsSector {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
name: string;
|
|
65
|
+
newsCount: number;
|
|
66
|
+
categoryType?: string | null;
|
|
67
|
+
meanType?: number | null;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export interface NewsContent {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
title: string;
|
|
72
|
+
description: string;
|
|
73
|
+
content: string[];
|
|
74
|
+
summary: string[];
|
|
75
|
+
investorInsight: string;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export interface NewsIndustry {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
name: string;
|
|
80
|
+
meanType: number;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export class NewsClient extends Client {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
84
|
+
async getHighlights(
|
|
85
|
+
region: Region,
|
|
86
|
+
locale: Locale
|
|
87
|
+
): Promise<NewsHighlights> {
|
|
88
|
+
return this.sendRequest<NewsHighlights>({
|
|
89
|
+
method: "GET",
|
|
90
|
+
url: "/api/v1/news/highlights",
|
|
91
|
+
params: {
|
|
92
|
+
region,
|
|
93
|
+
locale,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async getNews(
|
|
100
|
+
region: Region,
|
|
101
|
+
locale: Locale,
|
|
102
|
+
newsType?: NewsType,
|
|
103
|
+
page?: number,
|
|
104
|
+
size?: number,
|
|
105
|
+
orderBy?: NewsOrderBy,
|
|
106
|
+
orderByDirection?: SortDirection,
|
|
107
|
+
extraFilters?: string
|
|
108
|
+
): Promise<PaginatedResponse<News>> {
|
|
109
|
+
const params = {
|
|
110
|
+
region,
|
|
111
|
+
locale,
|
|
112
|
+
...(newsType != null && { newsType }),
|
|
113
|
+
...(page != null && { page }),
|
|
114
|
+
...(size != null && { size }),
|
|
115
|
+
...(orderBy != null && { orderBy }),
|
|
116
|
+
...(orderByDirection != null && { orderByDirection }),
|
|
117
|
+
...(extraFilters != null && { extraFilters }),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return this.sendRequest<PaginatedResponse<News>>({
|
|
121
|
+
method: "GET",
|
|
122
|
+
url: "/api/v1/news",
|
|
123
|
+
params,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async getNewsV2(
|
|
128
|
+
region: Region,
|
|
129
|
+
locale: Locale,
|
|
130
|
+
newsType?: NewsType,
|
|
131
|
+
page?: number,
|
|
132
|
+
size?: number,
|
|
133
|
+
orderBy?: NewsOrderBy,
|
|
134
|
+
orderByDirection?: SortDirection,
|
|
135
|
+
extraFilters?: string
|
|
136
|
+
): Promise<PaginatedResponse<NewsV2>> {
|
|
137
|
+
const params = {
|
|
138
|
+
region,
|
|
139
|
+
locale,
|
|
140
|
+
...(newsType != null && { newsType }),
|
|
141
|
+
...(page != null && { page }),
|
|
142
|
+
...(size != null && { size }),
|
|
143
|
+
...(orderBy != null && { orderBy }),
|
|
144
|
+
...(orderByDirection != null && { orderByDirection }),
|
|
145
|
+
...(extraFilters != null && { extraFilters }),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return this.sendRequest<PaginatedResponse<NewsV2>>({
|
|
149
|
+
method: "GET",
|
|
150
|
+
url: "/api/v2/news",
|
|
151
|
+
params,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
streamNews(
|
|
156
|
+
region: Region,
|
|
157
|
+
locale: Locale,
|
|
158
|
+
sectors?: string[],
|
|
159
|
+
tickers?: string[],
|
|
160
|
+
categories?: string[],
|
|
161
|
+
industries?: string[]
|
|
162
|
+
): { events: AsyncIterable<NewsV2[]>, cancel: () => void } {
|
|
163
|
+
let url = `${this["baseUrl"]}/api/v1/news/stream?locale=${locale}®ion=${region}`;
|
|
164
|
+
if (sectors?.length) url += `§ors=${encodeURIComponent(sectors.join(","))}`;
|
|
165
|
+
if (tickers?.length) url += `&tickers=${encodeURIComponent(tickers.join(","))}`;
|
|
166
|
+
if (categories?.length) url += `&categories=${encodeURIComponent(categories.join(","))}`;
|
|
167
|
+
if (industries?.length) url += `&industries=${encodeURIComponent(industries.join(","))}`;
|
|
168
|
+
return this.sendSSERequest<NewsV2[]>(url);
|
|
169
|
+
}
|
|
124
170
|
}
|
package/src/test/news.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Logger } from "winston";
|
|
2
|
+
import axios from "axios";
|
|
2
3
|
import { LaplaceConfiguration } from "../utilities/configuration";
|
|
3
4
|
import {
|
|
4
5
|
NewsClient,
|
|
@@ -10,61 +11,66 @@ import { Region, Locale } from "../client/collections";
|
|
|
10
11
|
import { SortDirection } from "../client/broker";
|
|
11
12
|
|
|
12
13
|
const mockNewsHighlightsResponse = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
]
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const mockNewsResponse = {
|
|
38
|
+
items: [
|
|
39
|
+
{
|
|
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..."
|
|
45
|
+
],
|
|
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" }]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
recordCount: 352
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const mockNewsV2Response = {
|
|
71
|
+
items: mockNewsResponse.items.map(({ relatedTickers, ...rest }) => rest),
|
|
72
|
+
recordCount: mockNewsResponse.recordCount
|
|
73
|
+
};
|
|
68
74
|
|
|
69
75
|
describe("NewsClient", () => {
|
|
70
76
|
let client: NewsClient;
|
|
@@ -150,13 +156,13 @@ describe("NewsClient", () => {
|
|
|
150
156
|
expect(typeof n.categories.newsCount).toBe("number");
|
|
151
157
|
expect(
|
|
152
158
|
typeof n.categories.categoryType === "string" ||
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
n.categories.categoryType == null ||
|
|
160
|
+
n.categories.categoryType === undefined
|
|
155
161
|
).toBe(true);
|
|
156
162
|
expect(
|
|
157
163
|
typeof n.categories.meanType === "number" ||
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
n.categories.meanType == null ||
|
|
165
|
+
n.categories.meanType === undefined
|
|
160
166
|
).toBe(true);
|
|
161
167
|
}
|
|
162
168
|
|
|
@@ -165,13 +171,13 @@ describe("NewsClient", () => {
|
|
|
165
171
|
expect(typeof n.sectors.newsCount).toBe("number");
|
|
166
172
|
expect(
|
|
167
173
|
typeof n.sectors.categoryType === "string" ||
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
n.sectors.categoryType == null ||
|
|
175
|
+
n.sectors.categoryType === undefined
|
|
170
176
|
).toBe(true);
|
|
171
177
|
expect(
|
|
172
178
|
typeof n.sectors.meanType === "number" ||
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
n.sectors.meanType == null ||
|
|
180
|
+
n.sectors.meanType === undefined
|
|
175
181
|
).toBe(true);
|
|
176
182
|
}
|
|
177
183
|
|
|
@@ -189,6 +195,58 @@ describe("NewsClient", () => {
|
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
});
|
|
198
|
+
|
|
199
|
+
test("getNewsV2 returns valid paginated data", async () => {
|
|
200
|
+
const resp = await client.getNewsV2(
|
|
201
|
+
Region.Us,
|
|
202
|
+
Locale.Tr,
|
|
203
|
+
NewsType.BRIEFS,
|
|
204
|
+
0,
|
|
205
|
+
10,
|
|
206
|
+
NewsOrderBy.TIMESTAMP,
|
|
207
|
+
SortDirection.Desc
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
expect(resp).toBeDefined();
|
|
211
|
+
expect(typeof resp.recordCount).toBe("number");
|
|
212
|
+
expect(resp.recordCount).toBeGreaterThanOrEqual(0);
|
|
213
|
+
expect(Array.isArray(resp.items)).toBe(true);
|
|
214
|
+
|
|
215
|
+
if (resp.items.length > 0) {
|
|
216
|
+
const n = resp.items[0];
|
|
217
|
+
|
|
218
|
+
expect(typeof n.url).toBe("string");
|
|
219
|
+
expect(typeof n.imageUrl).toBe("string");
|
|
220
|
+
expect(typeof n.timestamp).toBe("string");
|
|
221
|
+
expect(typeof n.publisherUrl).toBe("string");
|
|
222
|
+
expect(typeof n.qualityScore).toBe("number");
|
|
223
|
+
expect(typeof n.createdAt).toBe("string");
|
|
224
|
+
|
|
225
|
+
expect((n as any).relatedTickers).toBeUndefined();
|
|
226
|
+
|
|
227
|
+
expect(n.publisher).toBeDefined();
|
|
228
|
+
expect(typeof n.publisher.name).toBe("string");
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("streamNews yields item before timeout or throws gracefully if none arrive", async () => {
|
|
233
|
+
let newsItemsReceived = 0;
|
|
234
|
+
const { events, cancel } = client.streamNews(Region.Us, Locale.Tr);
|
|
235
|
+
|
|
236
|
+
const receivePromise = (async () => {
|
|
237
|
+
for await (const items of events) {
|
|
238
|
+
if (items && items.length > 0) {
|
|
239
|
+
newsItemsReceived += items.length;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
})();
|
|
244
|
+
|
|
245
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, 8000));
|
|
246
|
+
await Promise.race([receivePromise, timeoutPromise]);
|
|
247
|
+
cancel();
|
|
248
|
+
expect(newsItemsReceived).toBeGreaterThanOrEqual(0);
|
|
249
|
+
});
|
|
192
250
|
});
|
|
193
251
|
|
|
194
252
|
describe("Mock Tests", () => {
|
|
@@ -343,5 +401,142 @@ describe("NewsClient", () => {
|
|
|
343
401
|
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
344
402
|
});
|
|
345
403
|
});
|
|
404
|
+
|
|
405
|
+
describe("getNewsV2", () => {
|
|
406
|
+
test("calls correct endpoint/params and matches raw response", async () => {
|
|
407
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });
|
|
408
|
+
|
|
409
|
+
const resp = await client.getNewsV2(
|
|
410
|
+
Region.Tr,
|
|
411
|
+
Locale.Tr,
|
|
412
|
+
NewsType.BRIEFS,
|
|
413
|
+
1,
|
|
414
|
+
10,
|
|
415
|
+
NewsOrderBy.TIMESTAMP,
|
|
416
|
+
SortDirection.Desc,
|
|
417
|
+
undefined
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
421
|
+
const call = cli.request.mock.calls[0][0];
|
|
422
|
+
|
|
423
|
+
expect(call.method).toBe("GET");
|
|
424
|
+
expect(call.url).toBe("/api/v2/news");
|
|
425
|
+
expect(call.params).toEqual({
|
|
426
|
+
region: Region.Tr,
|
|
427
|
+
locale: Locale.Tr,
|
|
428
|
+
newsType: NewsType.BRIEFS,
|
|
429
|
+
page: 1,
|
|
430
|
+
size: 10,
|
|
431
|
+
orderBy: NewsOrderBy.TIMESTAMP,
|
|
432
|
+
orderByDirection: SortDirection.Desc
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(resp.recordCount).toBe(352);
|
|
436
|
+
expect(resp.items).toHaveLength(1);
|
|
437
|
+
|
|
438
|
+
const n = resp.items[0];
|
|
439
|
+
|
|
440
|
+
expect((n as any).relatedTickers).toBeUndefined();
|
|
441
|
+
expect(n.url).toBe(mockNewsV2Response.items[0].url);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("does not send optional params when undefined", async () => {
|
|
445
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });
|
|
446
|
+
|
|
447
|
+
await client.getNewsV2(Region.Tr, Locale.Tr);
|
|
448
|
+
|
|
449
|
+
const call = cli.request.mock.calls[0][0];
|
|
450
|
+
expect(call.params).toEqual({
|
|
451
|
+
region: Region.Tr,
|
|
452
|
+
locale: Locale.Tr
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("bubbles up request error", async () => {
|
|
457
|
+
cli.request.mockRejectedValueOnce(new Error("Failed to fetch news v2"));
|
|
458
|
+
|
|
459
|
+
await expect(
|
|
460
|
+
client.getNewsV2(
|
|
461
|
+
Region.Tr,
|
|
462
|
+
Locale.Tr,
|
|
463
|
+
NewsType.REUTERS,
|
|
464
|
+
0,
|
|
465
|
+
10,
|
|
466
|
+
NewsOrderBy.TIMESTAMP,
|
|
467
|
+
SortDirection.Desc
|
|
468
|
+
)
|
|
469
|
+
).rejects.toThrow("Failed to fetch news v2");
|
|
470
|
+
|
|
471
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe("streamNews", () => {
|
|
476
|
+
test("calls correct endpoint/params and correctly yields stream entities", async () => {
|
|
477
|
+
const eventsList: any[] = [];
|
|
478
|
+
|
|
479
|
+
// Mock get response to return a readable stream
|
|
480
|
+
const mockStreamData = [
|
|
481
|
+
"data: " + JSON.stringify([{ url: "http://example.com/stream-news-1", publiser: { name: "test-publisher" } }]) + "\n\n",
|
|
482
|
+
"data: " + JSON.stringify([{ url: "http://example.com/stream-news-2", publiser: { name: "test-publisher-2" } }]) + "\n\n",
|
|
483
|
+
];
|
|
484
|
+
|
|
485
|
+
const mockAsyncIterator = {
|
|
486
|
+
async *[Symbol.asyncIterator]() {
|
|
487
|
+
for (const chunk of mockStreamData) {
|
|
488
|
+
yield new TextEncoder().encode(chunk);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
|
|
494
|
+
data: mockAsyncIterator
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const { events, cancel } = client.streamNews(Region.Us, Locale.Tr);
|
|
498
|
+
|
|
499
|
+
for await (const newsList of events) {
|
|
500
|
+
eventsList.push(newsList);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
expect(axiosGetSpy).toHaveBeenCalledTimes(1);
|
|
504
|
+
const callArgs = axiosGetSpy.mock.calls[0];
|
|
505
|
+
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=tr®ion=us`);
|
|
506
|
+
expect(callArgs[1]?.responseType).toBe('stream');
|
|
507
|
+
|
|
508
|
+
expect(eventsList).toHaveLength(2);
|
|
509
|
+
expect(eventsList[0][0].url).toBe("http://example.com/stream-news-1");
|
|
510
|
+
expect(eventsList[1][0].url).toBe("http://example.com/stream-news-2");
|
|
511
|
+
|
|
512
|
+
cancel();
|
|
513
|
+
axiosGetSpy.mockRestore();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test("calls correct endpoint with optional parameters", async () => {
|
|
517
|
+
const mockAsyncIterator = {
|
|
518
|
+
async *[Symbol.asyncIterator]() {
|
|
519
|
+
yield new TextEncoder().encode("data: " + JSON.stringify([]) + "\n\n");
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
|
|
524
|
+
data: mockAsyncIterator
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"]);
|
|
528
|
+
|
|
529
|
+
for await (const _ of events) {
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
expect(axiosGetSpy).toHaveBeenCalledTimes(1);
|
|
534
|
+
const callArgs = axiosGetSpy.mock.calls[0];
|
|
535
|
+
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en®ion=us§ors=tech&tickers=AAPL&categories=category&industries=software`);
|
|
536
|
+
|
|
537
|
+
cancel();
|
|
538
|
+
axiosGetSpy.mockRestore();
|
|
539
|
+
});
|
|
540
|
+
});
|
|
346
541
|
});
|
|
347
542
|
});
|