laplace-api 4.8.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.
@@ -1,204 +1,347 @@
1
1
  import { Logger } from "winston";
2
2
  import { LaplaceConfiguration } from "../utilities/configuration";
3
3
  import {
4
- NewsClient,
5
- NewsHighlights,
6
- News,
7
- NewsType,
8
- NewsOrderBy,
4
+ NewsClient,
5
+ NewsType,
6
+ NewsOrderBy,
9
7
  } from "../client/news";
10
8
  import "./client_test_suite";
11
9
  import { Region, Locale } from "../client/collections";
12
10
  import { SortDirection } from "../client/broker";
13
- import { PaginatedResponse } from "../client/capital_increase";
14
-
15
- const mockNewsHighlightsResponse: NewsHighlights = {
16
- consumer: ["news1", "news2"],
17
- energyAndUtilities: ["news3"],
18
- finance: ["news4", "news5"],
19
- healthcare: ["news6"],
20
- industrialsAndMaterials: ["news7"],
21
- tech: ["news8"],
22
- other: ["news9"]
23
- };
24
-
25
- const mockNewsResponse: News[] = [
26
- {
27
- url: "https://example.com/news1",
28
- imageUrl: "https://example.com/image1.jpg",
29
- timestamp: "2024-03-14T10:00:00Z",
30
- publisherUrl: "https://example.com",
31
- publisher: {
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
- }
41
- ],
42
- qualityScore: 85,
43
- createdAt: "2024-03-14T09:00:00Z"
44
- },
45
- {
46
- url: "https://example.com/news2",
47
- imageUrl: "https://example.com/image2.jpg",
48
- timestamp: "2024-03-14T11:00:00Z",
49
- publisherUrl: "https://example.com",
50
- publisher: {
51
- name: "Example Publisher 2"
11
+
12
+ const mockNewsHighlightsResponse = {
13
+ tech: [
14
+ "Alphabet ve Amazon'un desteğiyle Anthropic, 2026 başlarında Hindistan'ın Bengaluru kentinde bir ofis açacak."
15
+ ],
16
+ other: [
17
+ "ABD Yüksek Mahkemesi, Epic Games'in davası kapsamında Google'ın Play uygulamalarındaki değişikliği engellemeyecek."
18
+ ],
19
+ finance: [
20
+ "Fifth Third Bank, Comerica'yı 10,9 milyar dolara satın alacak ve böylece ABD'nin 9. en büyük bankası olacak."
21
+ ],
22
+ consumer: [
23
+ "Tesla, rekabet ortamında pazar payını geri almak için daha ucuz Model Y ve Model 3'ü piyasaya sürdü; duyuru hisseleri etkiledi."
24
+ ],
25
+ healthcare: [
26
+ "İ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."
27
+ ],
28
+ energyAndUtilities: [
29
+ "ABD Enerji Bakanlığı, Stellantis ve GM'ye verilen 1,1 milyar dolarlık hibeleri iptal edebilir."
30
+ ],
31
+ industrialsAndMaterials: [
32
+ "Boeing, bir grevi sona erdirmek için IAM Sendikası ile geçici bir anlaşmaya vardı; detaylar açıklanmadı."
33
+ ]
34
+ };
35
+
36
+ const mockNewsResponse = {
37
+ items: [
38
+ {
39
+ url: "https://www.reuters.com/business/energy/commonwealth-lng-wants-more-time-build-planned-export-facility-louisiana-2025-10-07/",
40
+ content: {
41
+ title: "Commonwealth LNG wants more time to build planned export facility in Louisiana",
42
+ content: [
43
+ "Commonwealth LNG has requested a four-year extension from federal regulators to construct & begin exporting liquefied natural gas..."
44
+ ],
45
+ summary: [
46
+ "Commonwealth LNG has requested a four-year extension from federal regulators..."
47
+ ],
48
+ description:
49
+ "Commonwealth LNG has asked federal regulators for a four-year extension...",
50
+ investorInsight:
51
+ "What it means for investors: The extension request could postpone..."
52
52
  },
53
- relatedTickers: [],
54
- qualityScore: 90,
55
- createdAt: "2024-03-14T10:00:00Z"
56
- }
57
- ];
58
-
59
- const mockPaginatedNewsResponse: PaginatedResponse<News> = {
60
- recordCount: 2,
61
- items: mockNewsResponse
62
- };
63
-
64
- describe("News Client", () => {
53
+ sectors: { name: "Energy", meanType: 9, newsCount: 1 },
54
+ tickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }],
55
+ imageUrl: "",
56
+ createdAt: "2025-10-07T17:10:01.560644Z",
57
+ publisher: { name: "Reuters", logoUrl: null },
58
+ timestamp: "2025-10-07T16:50:16Z",
59
+ categories: { name: "Sector News", newsCount: 1, categoryType: "StockSpesific" },
60
+ industries: { name: "Oil/Gas (Production and Exploration)", meanType: 78 },
61
+ publisherUrl: "Reuters",
62
+ qualityScore: 0,
63
+ relatedTickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }]
64
+ }
65
+ ],
66
+ recordCount: 352
67
+ };
68
+
69
+ describe("NewsClient", () => {
70
+ let client: NewsClient;
71
+
72
+ beforeAll(() => {
73
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
74
+ const logger: Logger = {
75
+ info: jest.fn(),
76
+ error: jest.fn(),
77
+ warn: jest.fn(),
78
+ debug: jest.fn(),
79
+ } as unknown as Logger;
80
+
81
+ client = new NewsClient(config, logger);
82
+ });
83
+
84
+ describe("Integration Tests", () => {
85
+ jest.setTimeout(60_000);
86
+
87
+ test("getHighlights returns valid data", async () => {
88
+ const resp = await client.getHighlights(Region.Us, Locale.Tr);
89
+
90
+ expect(resp).toBeDefined();
91
+
92
+ expect(Array.isArray(resp.consumer)).toBe(true);
93
+ expect(Array.isArray(resp.energyAndUtilities)).toBe(true);
94
+ expect(Array.isArray(resp.finance)).toBe(true);
95
+ expect(Array.isArray(resp.healthcare)).toBe(true);
96
+ expect(Array.isArray(resp.industrialsAndMaterials)).toBe(true);
97
+ expect(Array.isArray(resp.tech)).toBe(true);
98
+ expect(Array.isArray(resp.other)).toBe(true);
99
+
100
+ const first = resp.tech?.[0];
101
+ if (first != null) expect(typeof first).toBe("string");
102
+ });
103
+
104
+ test("getNews returns valid paginated data", async () => {
105
+ const resp = await client.getNews(
106
+ Region.Us,
107
+ Locale.Tr,
108
+ NewsType.BRIEFS,
109
+ 0,
110
+ 10,
111
+ NewsOrderBy.TIMESTAMP,
112
+ SortDirection.Desc
113
+ );
114
+
115
+ expect(resp).toBeDefined();
116
+ expect(typeof resp.recordCount).toBe("number");
117
+ expect(resp.recordCount).toBeGreaterThanOrEqual(0);
118
+ expect(Array.isArray(resp.items)).toBe(true);
119
+
120
+ if (resp.items.length > 0) {
121
+ const n = resp.items[0];
122
+
123
+ expect(typeof n.url).toBe("string");
124
+ expect(typeof n.imageUrl).toBe("string");
125
+ expect(typeof n.timestamp).toBe("string");
126
+ expect(typeof n.publisherUrl).toBe("string");
127
+ expect(typeof n.qualityScore).toBe("number");
128
+ expect(typeof n.createdAt).toBe("string");
129
+
130
+ expect(n.publisher).toBeDefined();
131
+ expect(typeof n.publisher.name).toBe("string");
132
+ expect(
133
+ typeof n.publisher.logoUrl === "string" || n.publisher.logoUrl == null
134
+ ).toBe(true);
135
+
136
+ expect(Array.isArray(n.relatedTickers)).toBe(true);
137
+ if (n.relatedTickers.length > 0) {
138
+ const t = n.relatedTickers[0];
139
+ expect(typeof t.id).toBe("string");
140
+ expect(typeof t.name).toBe("string");
141
+ expect(typeof t.symbol === "string" || t.symbol == null).toBe(true);
142
+ }
143
+
144
+ if (n.tickers != null) {
145
+ expect(Array.isArray(n.tickers)).toBe(true);
146
+ }
147
+
148
+ if (n.categories != null) {
149
+ expect(typeof n.categories.name).toBe("string");
150
+ expect(typeof n.categories.newsCount).toBe("number");
151
+ expect(
152
+ typeof n.categories.categoryType === "string" ||
153
+ n.categories.categoryType == null ||
154
+ n.categories.categoryType === undefined
155
+ ).toBe(true);
156
+ expect(
157
+ typeof n.categories.meanType === "number" ||
158
+ n.categories.meanType == null ||
159
+ n.categories.meanType === undefined
160
+ ).toBe(true);
161
+ }
162
+
163
+ if (n.sectors != null) {
164
+ expect(typeof n.sectors.name).toBe("string");
165
+ expect(typeof n.sectors.newsCount).toBe("number");
166
+ expect(
167
+ typeof n.sectors.categoryType === "string" ||
168
+ n.sectors.categoryType == null ||
169
+ n.sectors.categoryType === undefined
170
+ ).toBe(true);
171
+ expect(
172
+ typeof n.sectors.meanType === "number" ||
173
+ n.sectors.meanType == null ||
174
+ n.sectors.meanType === undefined
175
+ ).toBe(true);
176
+ }
177
+
178
+ if (n.industries != null) {
179
+ expect(typeof n.industries.name).toBe("string");
180
+ expect(typeof n.industries.meanType).toBe("number");
181
+ }
182
+
183
+ if (n.content != null) {
184
+ expect(typeof n.content.title).toBe("string");
185
+ expect(typeof n.content.description).toBe("string");
186
+ expect(Array.isArray(n.content.content)).toBe(true);
187
+ expect(Array.isArray(n.content.summary)).toBe(true);
188
+ expect(typeof n.content.investorInsight).toBe("string");
189
+ }
190
+ }
191
+ });
192
+ });
193
+
194
+ describe("Mock Tests", () => {
65
195
  let client: NewsClient;
196
+ let cli: { request: jest.Mock };
66
197
 
67
- beforeAll(() => {
68
- const config = (global as any).testSuite.config as LaplaceConfiguration;
69
- const logger: Logger = {
70
- info: jest.fn(),
71
- error: jest.fn(),
72
- warn: jest.fn(),
73
- debug: jest.fn(),
74
- } as unknown as Logger;
198
+ beforeEach(() => {
199
+ cli = { request: jest.fn() };
75
200
 
76
- client = new NewsClient(config, logger);
201
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
202
+ const logger: Logger = {
203
+ info: jest.fn(),
204
+ error: jest.fn(),
205
+ warn: jest.fn(),
206
+ debug: jest.fn()
207
+ } as unknown as Logger;
208
+
209
+ client = new NewsClient(config, logger, cli as any);
77
210
  });
78
211
 
79
- describe("Integration Tests", () => {
80
- describe("getHighlights", () => {
81
- test("should return news highlights for region and locale", async () => {
82
- const resp = await client.getHighlights(Region.Tr, Locale.Tr);
83
-
84
- expect(resp).toBeDefined();
85
- expect(Array.isArray(resp.consumer)).toBe(true);
86
- expect(Array.isArray(resp.energyAndUtilities)).toBe(true);
87
- expect(Array.isArray(resp.finance)).toBe(true);
88
- expect(Array.isArray(resp.healthcare)).toBe(true);
89
- expect(Array.isArray(resp.industrialsAndMaterials)).toBe(true);
90
- expect(Array.isArray(resp.tech)).toBe(true);
91
- expect(Array.isArray(resp.other)).toBe(true);
92
- });
93
- });
212
+ describe("getHighlights", () => {
213
+ test("calls correct endpoint/params and matches raw response", async () => {
214
+ cli.request.mockResolvedValueOnce({ data: mockNewsHighlightsResponse });
94
215
 
95
- describe("getNews", () => {
96
- test("should return paginated news list", async () => {
97
- const resp = await client.getNews(
98
- Region.Tr,
99
- Locale.Tr,
100
- NewsType.BRIEFS,
101
- 1,
102
- 10,
103
- NewsOrderBy.TIMESTAMP,
104
- SortDirection.Desc,
105
- null
106
- );
107
-
108
- expect(resp).toBeDefined();
109
- expect(typeof resp.recordCount).toBe("number");
110
- expect(Array.isArray(resp.items)).toBe(true);
111
-
112
- if (resp.items.length > 0) {
113
- const firstNews = resp.items[0];
114
- expect(typeof firstNews.url).toBe("string");
115
- expect(typeof firstNews.timestamp).toBe("string");
116
- expect(typeof firstNews.publisher).toBe("object");
117
- expect(Array.isArray(firstNews.relatedTickers)).toBe(true);
118
- }
119
- });
120
- });
216
+ const resp = await client.getHighlights(Region.Tr, Locale.Tr);
217
+
218
+ expect(cli.request).toHaveBeenCalledTimes(1);
219
+ const call = cli.request.mock.calls[0][0];
220
+
221
+ expect(call.method).toBe("GET");
222
+ expect(call.url).toBe("/api/v1/news/highlights");
223
+ expect(call.params).toEqual({ region: Region.Tr, locale: Locale.Tr });
224
+
225
+ expect(resp.consumer).toEqual(mockNewsHighlightsResponse.consumer);
226
+ expect(resp.energyAndUtilities).toEqual(mockNewsHighlightsResponse.energyAndUtilities);
227
+ expect(resp.finance).toEqual(mockNewsHighlightsResponse.finance);
228
+ expect(resp.healthcare).toEqual(mockNewsHighlightsResponse.healthcare);
229
+ expect(resp.industrialsAndMaterials).toEqual(mockNewsHighlightsResponse.industrialsAndMaterials);
230
+ expect(resp.tech).toEqual(mockNewsHighlightsResponse.tech);
231
+ expect(resp.other).toEqual(mockNewsHighlightsResponse.other);
232
+ });
233
+
234
+ test("bubbles up request error", async () => {
235
+ cli.request.mockRejectedValueOnce(new Error("Failed to fetch highlights"));
236
+
237
+ await expect(client.getHighlights(Region.Tr, Locale.Tr)).rejects.toThrow(
238
+ "Failed to fetch highlights"
239
+ );
240
+
241
+ expect(cli.request).toHaveBeenCalledTimes(1);
242
+ });
121
243
  });
122
244
 
123
- describe("Mock Tests", () => {
124
- beforeEach(() => {
125
- jest.clearAllMocks();
245
+ describe("getNews", () => {
246
+ test("calls correct endpoint/params and matches raw response", async () => {
247
+ cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
248
+
249
+ const resp = await client.getNews(
250
+ Region.Tr,
251
+ Locale.Tr,
252
+ NewsType.BRIEFS,
253
+ 1,
254
+ 10,
255
+ NewsOrderBy.TIMESTAMP,
256
+ SortDirection.Desc,
257
+ undefined
258
+ );
259
+
260
+ expect(cli.request).toHaveBeenCalledTimes(1);
261
+ const call = cli.request.mock.calls[0][0];
262
+
263
+ expect(call.method).toBe("GET");
264
+ expect(call.url).toBe("/api/v1/news");
265
+ expect(call.params).toEqual({
266
+ region: Region.Tr,
267
+ locale: Locale.Tr,
268
+ newsType: NewsType.BRIEFS,
269
+ page: 1,
270
+ size: 10,
271
+ orderBy: NewsOrderBy.TIMESTAMP,
272
+ orderByDirection: SortDirection.Desc
126
273
  });
127
274
 
128
- describe("getHighlights", () => {
129
- test("should return news highlights with mock data", async () => {
130
- jest.spyOn(client, 'getHighlights').mockResolvedValue(mockNewsHighlightsResponse);
275
+ expect(resp.recordCount).toBe(352);
276
+ expect(resp.items).toHaveLength(1);
131
277
 
132
- const resp = await client.getHighlights(Region.Tr, Locale.Tr);
278
+ const n = resp.items[0];
133
279
 
134
- expect(resp).toBeDefined();
135
- expect(resp.consumer).toHaveLength(2);
136
- expect(resp.energyAndUtilities).toHaveLength(1);
137
- expect(resp.finance).toHaveLength(2);
138
- expect(resp.tech).toHaveLength(1);
280
+ expect(n.url).toBe(mockNewsResponse.items[0].url);
281
+ expect(n.imageUrl).toBe(mockNewsResponse.items[0].imageUrl);
282
+ expect(n.timestamp).toBe(mockNewsResponse.items[0].timestamp);
283
+ expect(n.publisherUrl).toBe(mockNewsResponse.items[0].publisherUrl);
284
+ expect(n.qualityScore).toBe(mockNewsResponse.items[0].qualityScore);
285
+ expect(n.createdAt).toBe(mockNewsResponse.items[0].createdAt);
139
286
 
140
- expect(client.getHighlights).toHaveBeenCalledWith(Region.Tr, Locale.Tr);
141
- });
287
+ expect(n.publisher.name).toBe(mockNewsResponse.items[0].publisher.name);
288
+ expect(n.publisher.logoUrl).toBeNull();
142
289
 
143
- test("should handle API errors for highlights", async () => {
144
- jest.spyOn(client, 'getHighlights').mockRejectedValue(new Error("Failed to fetch highlights"));
290
+ expect(n.relatedTickers).toHaveLength(1);
291
+ expect(n.relatedTickers[0].id).toBe(mockNewsResponse.items[0].relatedTickers[0].id);
292
+ expect(n.relatedTickers[0].name).toBe(mockNewsResponse.items[0].relatedTickers[0].name);
293
+ expect(n.relatedTickers[0].symbol).toBe(mockNewsResponse.items[0].relatedTickers[0].symbol);
145
294
 
146
- await expect(client.getHighlights(Region.Tr, Locale.Tr))
147
- .rejects.toThrow("Failed to fetch highlights");
148
- });
149
- });
295
+ expect(n.tickers).toHaveLength(1);
296
+ expect(n.tickers![0].symbol).toBe("EQT");
297
+
298
+ expect(n.categories?.name).toBe(mockNewsResponse.items[0].categories.name);
299
+ expect(n.categories?.newsCount).toBe(mockNewsResponse.items[0].categories.newsCount);
300
+ expect(n.categories?.categoryType).toBe(mockNewsResponse.items[0].categories.categoryType);
301
+
302
+ expect(n.sectors?.name).toBe(mockNewsResponse.items[0].sectors.name);
303
+ expect(n.sectors?.newsCount).toBe(mockNewsResponse.items[0].sectors.newsCount);
304
+ expect(n.sectors?.meanType).toBe(mockNewsResponse.items[0].sectors.meanType);
305
+
306
+ expect(n.industries?.name).toBe(mockNewsResponse.items[0].industries.name);
307
+ expect(n.industries?.meanType).toBe(mockNewsResponse.items[0].industries.meanType);
308
+
309
+ expect(n.content?.title).toBe(mockNewsResponse.items[0].content.title);
310
+ expect(n.content?.description).toBe(mockNewsResponse.items[0].content.description);
311
+ expect(n.content?.content).toEqual(mockNewsResponse.items[0].content.content);
312
+ expect(n.content?.summary).toEqual(mockNewsResponse.items[0].content.summary);
313
+ expect(n.content?.investorInsight).toBe(mockNewsResponse.items[0].content.investorInsight);
314
+ });
315
+
316
+ test("does not send optional params when undefined", async () => {
317
+ cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
150
318
 
151
- describe("getNews", () => {
152
- test("should return paginated news with mock data", async () => {
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
- });
319
+ await client.getNews(Region.Tr, Locale.Tr);
320
+
321
+ const call = cli.request.mock.calls[0][0];
322
+ expect(call.params).toEqual({
323
+ region: Region.Tr,
324
+ locale: Locale.Tr
201
325
  });
326
+ });
327
+
328
+ test("bubbles up request error", async () => {
329
+ cli.request.mockRejectedValueOnce(new Error("Failed to fetch news"));
330
+
331
+ await expect(
332
+ client.getNews(
333
+ Region.Tr,
334
+ Locale.Tr,
335
+ NewsType.REUTERS,
336
+ 0,
337
+ 10,
338
+ NewsOrderBy.TIMESTAMP,
339
+ SortDirection.Desc
340
+ )
341
+ ).rejects.toThrow("Failed to fetch news");
342
+
343
+ expect(cli.request).toHaveBeenCalledTimes(1);
344
+ });
202
345
  });
346
+ });
203
347
  });
204
-