@miradorlabs/parallax-web 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,439 @@
1
+ // ParallaxClient Unit Tests
2
+ import { ParallaxClient } from '../src/parallax';
3
+ import { GrpcWebRpc } from '../src/grpc';
4
+ import * as apiGateway from "mirador-gateway-parallax-web/proto/gateway/parallax/v1/parallax_gateway";
5
+ import { ResponseStatus_StatusCode } from "mirador-gateway-parallax-web/proto/common/v1/status";
6
+
7
+ // Mock the GrpcWebRpc class
8
+ jest.mock('../src/grpc');
9
+
10
+ // Mock console.error to avoid cluttering test output
11
+ const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
12
+
13
+ describe('ParallaxClient', () => {
14
+ let parallaxClient: ParallaxClient;
15
+ let mockApiGatewayClient: jest.Mocked<apiGateway.ParallaxGatewayServiceClientImpl>;
16
+
17
+ beforeEach(() => {
18
+ // Clear all mocks before each test
19
+ jest.clearAllMocks();
20
+
21
+ // Create a new ParallaxClient instance
22
+ parallaxClient = new ParallaxClient("test-api-key");
23
+
24
+ // Create mock for ApiGatewayServiceClientImpl
25
+ mockApiGatewayClient = {
26
+ CreateTrace: jest.fn(),
27
+ StartSpan: jest.fn(),
28
+ FinishSpan: jest.fn(),
29
+ AddSpanAttributes: jest.fn(),
30
+ AddSpanEvent: jest.fn(),
31
+ AddSpanError: jest.fn(),
32
+ AddSpanHint: jest.fn(),
33
+ } as unknown as jest.Mocked<apiGateway.ParallaxGatewayServiceClientImpl>;
34
+
35
+ // Mock the ParallaxGatewayServiceClientImpl constructor
36
+ jest
37
+ .spyOn(apiGateway, "ParallaxGatewayServiceClientImpl")
38
+ .mockImplementation(() => mockApiGatewayClient);
39
+ });
40
+
41
+ afterEach(() => {
42
+ mockConsoleError.mockClear();
43
+ });
44
+
45
+ afterAll(() => {
46
+ mockConsoleError.mockRestore();
47
+ });
48
+
49
+ describe('constructor', () => {
50
+ it('should create a ParallaxClient instance with API key', () => {
51
+ const client = new ParallaxClient('my-api-key');
52
+ expect(client).toBeInstanceOf(ParallaxClient);
53
+ expect(client.apiKey).toBe('my-api-key');
54
+ });
55
+
56
+ it('should create a ParallaxClient instance without API key', () => {
57
+ const client = new ParallaxClient();
58
+ expect(client).toBeInstanceOf(ParallaxClient);
59
+ expect(client.apiKey).toBeUndefined();
60
+ });
61
+
62
+ it('should initialize GrpcWebRpc with the correct URL and API key', () => {
63
+ const apiKey = 'test-key';
64
+ const customUrl = 'https://custom-gateway.example.com:50053';
65
+ new ParallaxClient(apiKey, customUrl);
66
+ expect(GrpcWebRpc).toHaveBeenCalledWith(customUrl, apiKey);
67
+ });
68
+ });
69
+
70
+ describe('createTrace', () => {
71
+ it('should create a trace successfully', async () => {
72
+ const mockRequest: apiGateway.CreateTraceRequest = {
73
+ name: 'Test Trace',
74
+ attributes: {
75
+ 'project.id': 'test-project',
76
+ 'environment': 'test'
77
+ },
78
+ tags: ['tag1', 'tag2'],
79
+ };
80
+
81
+ const mockResponse: apiGateway.CreateTraceResponse = {
82
+ status: {
83
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
84
+ errorMessage: undefined
85
+ },
86
+ traceId: 'trace-123',
87
+ };
88
+
89
+ mockApiGatewayClient.CreateTrace.mockResolvedValue(mockResponse);
90
+
91
+ const result = await parallaxClient.createTrace(mockRequest);
92
+
93
+ expect(result).toEqual(mockResponse);
94
+ expect(mockApiGatewayClient.CreateTrace).toHaveBeenCalledWith(mockRequest);
95
+ expect(mockApiGatewayClient.CreateTrace).toHaveBeenCalledTimes(1);
96
+ });
97
+
98
+ it('should handle errors when creating a trace', async () => {
99
+ const mockRequest: apiGateway.CreateTraceRequest = {
100
+ name: 'Test Trace',
101
+ attributes: {},
102
+ tags: ['tag1', 'tag2'],
103
+ };
104
+
105
+ const mockError = new Error('gRPC-Web connection failed');
106
+ mockApiGatewayClient.CreateTrace.mockRejectedValue(mockError);
107
+
108
+ await expect(parallaxClient.createTrace(mockRequest)).rejects.toThrow('gRPC-Web connection failed');
109
+ expect(mockConsoleError).toHaveBeenCalledWith(
110
+ '[ParallaxClient][createTrace] Error:',
111
+ expect.any(Error)
112
+ );
113
+ });
114
+ });
115
+
116
+ describe('startSpan', () => {
117
+ it('should start a span successfully', async () => {
118
+ const mockRequest: apiGateway.StartSpanRequest = {
119
+ name: 'Test Span',
120
+ traceId: 'trace-123',
121
+ parentSpanId: undefined,
122
+ attributes: {
123
+ 'span.type': 'http'
124
+ },
125
+ startTime: undefined,
126
+ };
127
+
128
+ const mockResponse: apiGateway.StartSpanResponse = {
129
+ status: {
130
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
131
+ errorMessage: undefined
132
+ },
133
+ spanId: 'span-456',
134
+ };
135
+
136
+ mockApiGatewayClient.StartSpan.mockResolvedValue(mockResponse);
137
+
138
+ const result = await parallaxClient.startSpan(mockRequest);
139
+
140
+ expect(result).toEqual(mockResponse);
141
+ expect(mockApiGatewayClient.StartSpan).toHaveBeenCalledWith(mockRequest);
142
+ expect(mockApiGatewayClient.StartSpan).toHaveBeenCalledTimes(1);
143
+ });
144
+
145
+ it('should handle errors when starting a span', async () => {
146
+ const mockRequest: apiGateway.StartSpanRequest = {
147
+ name: 'Test Span',
148
+ traceId: 'trace-123',
149
+ attributes: {},
150
+ };
151
+
152
+ const mockError = new Error('Span creation failed');
153
+ mockApiGatewayClient.StartSpan.mockRejectedValue(mockError);
154
+
155
+ await expect(parallaxClient.startSpan(mockRequest)).rejects.toThrow('Span creation failed');
156
+ expect(mockConsoleError).toHaveBeenCalledWith(
157
+ '[ParallaxClient][startSpan] Error:',
158
+ expect.any(Error)
159
+ );
160
+ });
161
+ });
162
+
163
+ describe('finishSpan', () => {
164
+ it('should finish a span successfully', async () => {
165
+ const mockRequest: apiGateway.FinishSpanRequest = {
166
+ traceId: 'trace-123',
167
+ spanId: 'span-456',
168
+ endTime: undefined,
169
+ status: undefined,
170
+ };
171
+
172
+ const mockResponse: apiGateway.FinishSpanResponse = {
173
+ status: {
174
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
175
+ errorMessage: undefined
176
+ },
177
+ };
178
+
179
+ mockApiGatewayClient.FinishSpan.mockResolvedValue(mockResponse);
180
+
181
+ const result = await parallaxClient.finishSpan(mockRequest);
182
+
183
+ expect(result).toEqual(mockResponse);
184
+ expect(mockApiGatewayClient.FinishSpan).toHaveBeenCalledWith(mockRequest);
185
+ expect(mockApiGatewayClient.FinishSpan).toHaveBeenCalledTimes(1);
186
+ });
187
+
188
+ it('should handle errors when finishing a span', async () => {
189
+ const mockRequest: apiGateway.FinishSpanRequest = {
190
+ traceId: 'trace-123',
191
+ spanId: 'span-456',
192
+ };
193
+
194
+ const mockError = new Error('Finish span failed');
195
+ mockApiGatewayClient.FinishSpan.mockRejectedValue(mockError);
196
+
197
+ await expect(parallaxClient.finishSpan(mockRequest)).rejects.toThrow('Finish span failed');
198
+ expect(mockConsoleError).toHaveBeenCalledWith(
199
+ '[ParallaxClient][finishSpan] Error:',
200
+ expect.any(Error)
201
+ );
202
+ });
203
+ });
204
+
205
+ describe('addSpanAttributes', () => {
206
+ it('should add span attributes successfully', async () => {
207
+ const mockRequest: apiGateway.AddSpanAttributesRequest = {
208
+ traceId: 'trace-123',
209
+ spanId: 'span-456',
210
+ attributes: {
211
+ key1: 'value1',
212
+ key2: 'value2',
213
+ },
214
+ };
215
+
216
+ const mockResponse: apiGateway.AddSpanAttributesResponse = {
217
+ status: {
218
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
219
+ errorMessage: undefined
220
+ },
221
+ };
222
+
223
+ mockApiGatewayClient.AddSpanAttributes.mockResolvedValue(mockResponse);
224
+
225
+ const result = await parallaxClient.addSpanAttributes(mockRequest);
226
+
227
+ expect(result).toEqual(mockResponse);
228
+ expect(mockApiGatewayClient.AddSpanAttributes).toHaveBeenCalledWith(mockRequest);
229
+ expect(mockApiGatewayClient.AddSpanAttributes).toHaveBeenCalledTimes(1);
230
+ });
231
+
232
+ it('should handle errors when adding span attributes', async () => {
233
+ const mockRequest: apiGateway.AddSpanAttributesRequest = {
234
+ traceId: 'trace-123',
235
+ spanId: 'span-456',
236
+ attributes: {},
237
+ };
238
+
239
+ const mockError = new Error('Add attributes failed');
240
+ mockApiGatewayClient.AddSpanAttributes.mockRejectedValue(mockError);
241
+
242
+ await expect(parallaxClient.addSpanAttributes(mockRequest)).rejects.toThrow('Add attributes failed');
243
+ expect(mockConsoleError).toHaveBeenCalledWith(
244
+ '[ParallaxClient][addSpanAttributes] Error:',
245
+ expect.any(Error)
246
+ );
247
+ });
248
+ });
249
+
250
+ describe('addSpanEvent', () => {
251
+ it('should add span event successfully', async () => {
252
+ const mockRequest: apiGateway.AddSpanEventRequest = {
253
+ traceId: 'trace-123',
254
+ spanId: 'span-456',
255
+ eventName: 'Test Event',
256
+ attributes: {
257
+ eventType: 'custom',
258
+ },
259
+ timestamp: undefined,
260
+ };
261
+
262
+ const mockResponse: apiGateway.AddSpanEventResponse = {
263
+ status: {
264
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
265
+ errorMessage: undefined
266
+ },
267
+ };
268
+
269
+ mockApiGatewayClient.AddSpanEvent.mockResolvedValue(mockResponse);
270
+
271
+ const result = await parallaxClient.addSpanEvent(mockRequest);
272
+
273
+ expect(result).toEqual(mockResponse);
274
+ expect(mockApiGatewayClient.AddSpanEvent).toHaveBeenCalledWith(mockRequest);
275
+ expect(mockApiGatewayClient.AddSpanEvent).toHaveBeenCalledTimes(1);
276
+ });
277
+
278
+ it('should handle errors when adding span event', async () => {
279
+ const mockRequest: apiGateway.AddSpanEventRequest = {
280
+ traceId: 'trace-123',
281
+ spanId: 'span-456',
282
+ eventName: 'Test Event',
283
+ attributes: {},
284
+ };
285
+
286
+ const mockError = new Error('Add event failed');
287
+ mockApiGatewayClient.AddSpanEvent.mockRejectedValue(mockError);
288
+
289
+ await expect(parallaxClient.addSpanEvent(mockRequest)).rejects.toThrow('Add event failed');
290
+ expect(mockConsoleError).toHaveBeenCalledWith(
291
+ '[ParallaxClient][addSpanEvent] Error:',
292
+ expect.any(Error)
293
+ );
294
+ });
295
+ });
296
+
297
+ describe('addSpanError', () => {
298
+ it('should add span error successfully', async () => {
299
+ const mockRequest: apiGateway.AddSpanErrorRequest = {
300
+ traceId: 'trace-123',
301
+ spanId: 'span-456',
302
+ errorType: 'RuntimeError',
303
+ message: 'Something went wrong',
304
+ stackTrace: undefined,
305
+ attributes: {},
306
+ timestamp: undefined,
307
+ };
308
+
309
+ const mockResponse: apiGateway.AddSpanErrorResponse = {
310
+ status: {
311
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
312
+ errorMessage: undefined
313
+ },
314
+ };
315
+
316
+ mockApiGatewayClient.AddSpanError.mockResolvedValue(mockResponse);
317
+
318
+ const result = await parallaxClient.addSpanError(mockRequest);
319
+
320
+ expect(result).toEqual(mockResponse);
321
+ expect(mockApiGatewayClient.AddSpanError).toHaveBeenCalledWith(mockRequest);
322
+ expect(mockApiGatewayClient.AddSpanError).toHaveBeenCalledTimes(1);
323
+ });
324
+
325
+ it('should handle errors when adding span error', async () => {
326
+ const mockRequest: apiGateway.AddSpanErrorRequest = {
327
+ traceId: 'trace-123',
328
+ spanId: 'span-456',
329
+ errorType: 'Error',
330
+ message: 'Error message',
331
+ attributes: {},
332
+ };
333
+
334
+ const mockError = new Error('Add error failed');
335
+ mockApiGatewayClient.AddSpanError.mockRejectedValue(mockError);
336
+
337
+ await expect(parallaxClient.addSpanError(mockRequest)).rejects.toThrow('Add error failed');
338
+ expect(mockConsoleError).toHaveBeenCalledWith(
339
+ '[ParallaxClient][addSpanError] Error:',
340
+ expect.any(Error)
341
+ );
342
+ });
343
+ });
344
+
345
+ describe('addSpanHint', () => {
346
+ it('should add span hint successfully', async () => {
347
+ const mockRequest: apiGateway.AddSpanHintRequest = {
348
+ traceId: 'trace-123',
349
+ parentSpanId: 'span-456',
350
+ timestamp: undefined,
351
+ chainTransaction: {
352
+ txHash: '0x123abc',
353
+ chainId: 1,
354
+ },
355
+ };
356
+
357
+ const mockResponse: apiGateway.AddSpanHintResponse = {
358
+ status: {
359
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
360
+ errorMessage: undefined
361
+ },
362
+ };
363
+
364
+ mockApiGatewayClient.AddSpanHint.mockResolvedValue(mockResponse);
365
+
366
+ const result = await parallaxClient.addSpanHint(mockRequest);
367
+
368
+ expect(result).toEqual(mockResponse);
369
+ expect(mockApiGatewayClient.AddSpanHint).toHaveBeenCalledWith(mockRequest);
370
+ expect(mockApiGatewayClient.AddSpanHint).toHaveBeenCalledTimes(1);
371
+ });
372
+
373
+ it('should handle errors when adding span hint', async () => {
374
+ const mockRequest: apiGateway.AddSpanHintRequest = {
375
+ traceId: 'trace-123',
376
+ parentSpanId: 'span-456',
377
+ chainTransaction: undefined,
378
+ };
379
+
380
+ const mockError = new Error('Add hint failed');
381
+ mockApiGatewayClient.AddSpanHint.mockRejectedValue(mockError);
382
+
383
+ await expect(parallaxClient.addSpanHint(mockRequest)).rejects.toThrow('Add hint failed');
384
+ expect(mockConsoleError).toHaveBeenCalledWith(
385
+ '[ParallaxClient][addSpanHint] Error:',
386
+ expect.any(Error)
387
+ );
388
+ });
389
+ });
390
+
391
+ describe('integration scenarios', () => {
392
+ it('should handle multiple method calls in sequence', async () => {
393
+ const traceRequest: apiGateway.CreateTraceRequest = {
394
+ name: 'Integration Test',
395
+ attributes: {
396
+ 'project.id': 'test-project'
397
+ },
398
+ tags: ['tag1', 'tag2'],
399
+ };
400
+
401
+ const spanRequest: apiGateway.StartSpanRequest = {
402
+ name: 'Integration Span',
403
+ traceId: 'trace-123',
404
+ attributes: {},
405
+ };
406
+
407
+ mockApiGatewayClient.CreateTrace.mockResolvedValue({
408
+ traceId: 'trace-123',
409
+ status: {
410
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
411
+ errorMessage: undefined
412
+ }
413
+ });
414
+ mockApiGatewayClient.StartSpan.mockResolvedValue({
415
+ spanId: 'span-456',
416
+ status: {
417
+ code: ResponseStatus_StatusCode.STATUS_CODE_SUCCESS,
418
+ errorMessage: undefined
419
+ }
420
+ });
421
+
422
+ await parallaxClient.createTrace(traceRequest);
423
+ await parallaxClient.startSpan(spanRequest);
424
+
425
+ expect(mockApiGatewayClient.CreateTrace).toHaveBeenCalledTimes(1);
426
+ expect(mockApiGatewayClient.StartSpan).toHaveBeenCalledTimes(1);
427
+ });
428
+
429
+ it('should create client instances with different API keys', () => {
430
+ const client1 = new ParallaxClient('key1');
431
+ const client2 = new ParallaxClient('key2');
432
+ const client3 = new ParallaxClient();
433
+
434
+ expect(client1.apiKey).toBe('key1');
435
+ expect(client2.apiKey).toBe('key2');
436
+ expect(client3.apiKey).toBeUndefined();
437
+ });
438
+ });
439
+ });
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "declaration": true,
6
+ "sourceMap": true,
7
+ "outDir": "./dist",
8
+ "allowImportingTsExtensions": false
9
+ },
10
+ "include": [
11
+ "index.ts",
12
+ "src/**/*"
13
+ ],
14
+ "exclude": [
15
+ "node_modules",
16
+ "dist",
17
+ "tests"
18
+ ]
19
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "noEmit": true,
5
+ "useDefineForClassFields": true,
6
+ "module": "ESNext",
7
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "isolatedModules": true,
13
+ "declaration": true,
14
+ "outDir": "./dist",
15
+ "allowImportingTsExtensions": true,
16
+ "moduleResolution": "bundler",
17
+ "resolveJsonModule": true
18
+ },
19
+ "exclude": ["node_modules", "dist", "*.config.{js,mjs,ts,mts}"]
20
+ }