@geekmidas/telescope 9.0.2 → 10.0.0-alpha.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.
Files changed (128) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
  3. package/dist/{http-Dvca6fC3.d.cts.map → http-DULM1u2L.d.cts.map} +1 -1
  4. package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
  5. package/dist/{http-D5kn3_Mt.d.mts.map → http-GB-Nlvwc.d.mts.map} +1 -1
  6. package/dist/instrumentation/http.d.cts +1 -1
  7. package/dist/instrumentation/http.d.mts +1 -1
  8. package/dist/instrumentation/index.cjs +2 -2
  9. package/dist/instrumentation/index.d.cts +3 -3
  10. package/dist/instrumentation/index.d.mts +3 -3
  11. package/dist/instrumentation/index.mjs +2 -2
  12. package/dist/instrumentation/middleware.cjs +1 -1
  13. package/dist/instrumentation/middleware.d.cts +1 -1
  14. package/dist/instrumentation/middleware.d.mts +1 -1
  15. package/dist/instrumentation/middleware.mjs +1 -1
  16. package/dist/instrumentation/otel.cjs +1 -1
  17. package/dist/instrumentation/otel.d.cts +1 -1
  18. package/dist/instrumentation/otel.d.mts +1 -1
  19. package/dist/instrumentation/otel.mjs +1 -1
  20. package/dist/logger/console.d.cts.map +1 -1
  21. package/dist/logger/console.d.mts.map +1 -1
  22. package/dist/logger/pino.d.cts +2 -2
  23. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  24. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  25. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  26. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  27. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  28. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  29. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  30. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  31. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  32. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
  34. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  35. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  36. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  37. package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
  38. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  39. package/dist/storage/kysely.cjs +48 -38
  40. package/dist/storage/kysely.cjs.map +1 -1
  41. package/dist/storage/kysely.d.cts +29 -9
  42. package/dist/storage/kysely.d.cts.map +1 -1
  43. package/dist/storage/kysely.d.mts +29 -9
  44. package/dist/storage/kysely.d.mts.map +1 -1
  45. package/dist/storage/kysely.mjs +48 -38
  46. package/dist/storage/kysely.mjs.map +1 -1
  47. package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
  48. package/dist/types-B1EIa_cQ.d.mts.map +1 -1
  49. package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
  50. package/package.json +6 -3
  51. package/CHANGELOG.md +0 -72
  52. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  53. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  54. package/dist/otel-40udNP5g.mjs.map +0 -1
  55. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  56. package/src/Telescope.ts +0 -561
  57. package/src/__tests__/Telescope.spec.ts +0 -438
  58. package/src/__tests__/pino-transport.spec.ts +0 -115
  59. package/src/__tests__/redact.spec.ts +0 -336
  60. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  61. package/src/adapters/__tests__/middy.spec.ts +0 -741
  62. package/src/adapters/index.ts +0 -22
  63. package/src/adapters/lambda.ts +0 -432
  64. package/src/adapters/middy.ts +0 -402
  65. package/src/adapters/types.ts +0 -181
  66. package/src/core.ts +0 -51
  67. package/src/index.ts +0 -34
  68. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  69. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  70. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  71. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  72. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  73. package/src/instrumentation/core.ts +0 -228
  74. package/src/instrumentation/hono.ts +0 -270
  75. package/src/instrumentation/http.ts +0 -306
  76. package/src/instrumentation/index.ts +0 -90
  77. package/src/instrumentation/middleware.ts +0 -297
  78. package/src/instrumentation/otel.ts +0 -271
  79. package/src/instrumentation/setup.ts +0 -246
  80. package/src/instrumentation/tracing.ts +0 -145
  81. package/src/logger/__tests__/console.spec.ts +0 -366
  82. package/src/logger/__tests__/pino.spec.ts +0 -217
  83. package/src/logger/console.ts +0 -230
  84. package/src/logger/pino.ts +0 -201
  85. package/src/metrics/MetricsAggregator.ts +0 -555
  86. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  87. package/src/metrics/index.ts +0 -2
  88. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  89. package/src/metrics/storage/MetricsStorage.ts +0 -74
  90. package/src/metrics/storage/index.ts +0 -3
  91. package/src/metrics/storage/types.ts +0 -243
  92. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  93. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  94. package/src/otlp/hono.ts +0 -120
  95. package/src/otlp/index.ts +0 -22
  96. package/src/otlp/receiver.ts +0 -162
  97. package/src/otlp/transformer.ts +0 -367
  98. package/src/otlp/types.ts +0 -364
  99. package/src/redact.ts +0 -116
  100. package/src/server/__tests__/hono.spec.ts +0 -814
  101. package/src/server/hono.ts +0 -442
  102. package/src/storage/__tests__/kysely.spec.ts +0 -715
  103. package/src/storage/__tests__/memory.spec.ts +0 -694
  104. package/src/storage/kysely.ts +0 -599
  105. package/src/storage/memory.ts +0 -204
  106. package/src/types.ts +0 -447
  107. package/src/ui-assets.ts +0 -40
  108. package/tsconfig.json +0 -9
  109. package/ui/CHANGELOG.md +0 -7
  110. package/ui/index.html +0 -12
  111. package/ui/node_modules/.bin/tsc +0 -21
  112. package/ui/node_modules/.bin/tsserver +0 -21
  113. package/ui/node_modules/.bin/vite +0 -21
  114. package/ui/package.json +0 -24
  115. package/ui/src/App.tsx +0 -595
  116. package/ui/src/api.ts +0 -88
  117. package/ui/src/components/ExceptionDetail.tsx +0 -100
  118. package/ui/src/components/FilterBar.tsx +0 -162
  119. package/ui/src/components/LogDetail.tsx +0 -91
  120. package/ui/src/components/RequestDetail.tsx +0 -143
  121. package/ui/src/main.tsx +0 -10
  122. package/ui/src/styles.css +0 -11
  123. package/ui/src/types.ts +0 -63
  124. package/ui/src/vite-env.d.ts +0 -1
  125. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  126. package/ui/tsconfig.json +0 -20
  127. package/ui/tsconfig.tsbuildinfo +0 -1
  128. package/ui/vite.config.ts +0 -13
@@ -1,528 +0,0 @@
1
- import { trace } from '@opentelemetry/api';
2
- import {
3
- BasicTracerProvider,
4
- InMemorySpanExporter,
5
- SimpleSpanProcessor,
6
- } from '@opentelemetry/sdk-trace-base';
7
- import type { Context as LambdaContext } from 'aws-lambda';
8
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
9
- import {
10
- getContextFromEvent,
11
- getSpanFromEvent,
12
- telemetryMiddleware,
13
- withEventContext,
14
- } from '../middleware';
15
-
16
- describe('telemetryMiddleware', () => {
17
- let mockContext: LambdaContext;
18
- let middleware: ReturnType<typeof telemetryMiddleware>;
19
- let exporter: InMemorySpanExporter;
20
- let provider: BasicTracerProvider;
21
-
22
- beforeEach(() => {
23
- // Set up real OpenTelemetry with in-memory exporter
24
- exporter = new InMemorySpanExporter();
25
- provider = new BasicTracerProvider();
26
- provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
27
- provider.register();
28
-
29
- mockContext = {
30
- callbackWaitsForEmptyEventLoop: true,
31
- functionName: 'test-function',
32
- functionVersion: '1',
33
- invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789:function:test',
34
- memoryLimitInMB: '256',
35
- awsRequestId: 'test-request-id-123',
36
- logGroupName: '/aws/lambda/test',
37
- logStreamName: '2024/01/01/[$LATEST]abc123',
38
- getRemainingTimeInMillis: () => 10000,
39
- done: vi.fn(),
40
- fail: vi.fn(),
41
- succeed: vi.fn(),
42
- };
43
-
44
- middleware = telemetryMiddleware();
45
- });
46
-
47
- afterEach(async () => {
48
- exporter.reset();
49
- await provider.shutdown();
50
- // Reset global tracer provider
51
- trace.disable();
52
- });
53
-
54
- describe('before hook', () => {
55
- it('should create span for API Gateway v2 event', async () => {
56
- const event = {
57
- rawPath: '/api/users',
58
- headers: {
59
- host: 'api.example.com',
60
- 'content-type': 'application/json',
61
- },
62
- requestContext: {
63
- requestId: 'req-123',
64
- http: {
65
- method: 'POST',
66
- sourceIp: '192.168.1.1',
67
- userAgent: 'test-agent',
68
- path: '/api/users',
69
- },
70
- },
71
- body: JSON.stringify({ name: 'John' }),
72
- routeKey: 'POST /api/users',
73
- };
74
-
75
- const request = { event, context: mockContext, response: null };
76
- await middleware.before?.(request as any);
77
-
78
- // Span should be stored on event
79
- const span = getSpanFromEvent(event);
80
- expect(span).toBeDefined();
81
- expect(getContextFromEvent(event)).toBeDefined();
82
-
83
- // End span to export it
84
- const response = { statusCode: 201, body: '{"id":"123"}' };
85
- await middleware.after?.({
86
- event,
87
- context: mockContext,
88
- response,
89
- } as any);
90
-
91
- // Check exported spans
92
- const spans = exporter.getFinishedSpans();
93
- expect(spans).toHaveLength(1);
94
- expect(spans[0].name).toBe('POST /api/users');
95
- expect(spans[0].attributes['http.request.method']).toBe('POST');
96
- expect(spans[0].attributes['url.path']).toBe('/api/users');
97
- expect(spans[0].attributes['client.address']).toBe('192.168.1.1');
98
- });
99
-
100
- it('should create span for API Gateway v1 event', async () => {
101
- const event = {
102
- httpMethod: 'GET',
103
- path: '/api/items',
104
- headers: {
105
- Host: 'api.example.com',
106
- 'User-Agent': 'test-agent',
107
- },
108
- requestContext: {
109
- requestId: 'req-456',
110
- identity: {
111
- sourceIp: '10.0.0.1',
112
- },
113
- },
114
- resource: '/api/items/{id}',
115
- queryStringParameters: { page: '1' },
116
- };
117
-
118
- const request = { event, context: mockContext, response: null };
119
- await middleware.before?.(request as any);
120
-
121
- expect(getSpanFromEvent(event)).toBeDefined();
122
-
123
- await middleware.after?.({
124
- event,
125
- context: mockContext,
126
- response: { statusCode: 200 },
127
- } as any);
128
-
129
- const spans = exporter.getFinishedSpans();
130
- expect(spans).toHaveLength(1);
131
- expect(spans[0].name).toBe('GET /api/items/{id}');
132
- expect(spans[0].attributes['http.request.method']).toBe('GET');
133
- expect(spans[0].attributes['http.route']).toBe('/api/items/{id}');
134
- });
135
-
136
- it('should handle generic Lambda invocation', async () => {
137
- const event = {
138
- customField: 'value',
139
- headers: {},
140
- };
141
-
142
- const request = { event, context: mockContext, response: null };
143
- await middleware.before?.(request as any);
144
-
145
- expect(getSpanFromEvent(event)).toBeDefined();
146
-
147
- await middleware.after?.({
148
- event,
149
- context: mockContext,
150
- response: { statusCode: 200 },
151
- } as any);
152
-
153
- const spans = exporter.getFinishedSpans();
154
- expect(spans).toHaveLength(1);
155
- expect(spans[0].attributes['http.request.method']).toBe('INVOKE');
156
- });
157
-
158
- it('should skip tracing when shouldSkip returns true', async () => {
159
- const skipMiddleware = telemetryMiddleware({
160
- shouldSkip: (event) => event.skipTrace === true,
161
- });
162
-
163
- const event = {
164
- skipTrace: true,
165
- headers: {},
166
- };
167
-
168
- const request = { event, context: mockContext, response: null };
169
- await skipMiddleware.before?.(request as any);
170
-
171
- expect(getSpanFromEvent(event)).toBeUndefined();
172
- expect(exporter.getFinishedSpans()).toHaveLength(0);
173
- });
174
-
175
- it('should record request body when recordBody is true', async () => {
176
- const bodyMiddleware = telemetryMiddleware({ recordBody: true });
177
- const bodyContent = JSON.stringify({
178
- name: 'John',
179
- email: 'john@example.com',
180
- });
181
-
182
- const event = {
183
- httpMethod: 'POST',
184
- path: '/api/users',
185
- headers: {},
186
- body: bodyContent,
187
- };
188
-
189
- const request = { event, context: mockContext, response: null };
190
- await bodyMiddleware.before?.(request as any);
191
- await bodyMiddleware.after?.({
192
- event,
193
- context: mockContext,
194
- response: { statusCode: 201 },
195
- } as any);
196
-
197
- const spans = exporter.getFinishedSpans();
198
- expect(spans[0].attributes['http.request.body']).toBe(bodyContent);
199
- });
200
-
201
- it('should truncate large request bodies', async () => {
202
- const bodyMiddleware = telemetryMiddleware({ recordBody: true });
203
- const largeBody = 'x'.repeat(5000);
204
-
205
- const event = {
206
- httpMethod: 'POST',
207
- path: '/api/upload',
208
- headers: {},
209
- body: largeBody,
210
- };
211
-
212
- const request = { event, context: mockContext, response: null };
213
- await bodyMiddleware.before?.(request as any);
214
- await bodyMiddleware.after?.({
215
- event,
216
- context: mockContext,
217
- response: { statusCode: 200 },
218
- } as any);
219
-
220
- const spans = exporter.getFinishedSpans();
221
- const recordedBody = spans[0].attributes['http.request.body'] as string;
222
- expect(recordedBody.length).toBeLessThan(largeBody.length);
223
- expect(recordedBody).toContain('...');
224
- });
225
-
226
- it('should add Lambda-specific attributes', async () => {
227
- const event = {
228
- httpMethod: 'GET',
229
- path: '/api/test',
230
- headers: {},
231
- };
232
-
233
- const request = { event, context: mockContext, response: null };
234
- await middleware.before?.(request as any);
235
- await middleware.after?.({
236
- event,
237
- context: mockContext,
238
- response: { statusCode: 200 },
239
- } as any);
240
-
241
- const spans = exporter.getFinishedSpans();
242
- expect(spans[0].attributes['faas.invocation_id']).toBe(
243
- 'test-request-id-123',
244
- );
245
- expect(spans[0].attributes['faas.name']).toBe('test-function');
246
- });
247
- });
248
-
249
- describe('after hook', () => {
250
- it('should record success status code', async () => {
251
- const event = {
252
- httpMethod: 'GET',
253
- path: '/api/users',
254
- headers: {},
255
- };
256
-
257
- await middleware.before?.({
258
- event,
259
- context: mockContext,
260
- response: null,
261
- } as any);
262
- await middleware.after?.({
263
- event,
264
- context: mockContext,
265
- response: { statusCode: 200, body: '{"users":[]}' },
266
- } as any);
267
-
268
- const spans = exporter.getFinishedSpans();
269
- expect(spans[0].attributes['http.response.status_code']).toBe(200);
270
- expect(spans[0].status.code).toBe(1); // SpanStatusCode.OK
271
- });
272
-
273
- it('should record 4xx status as error', async () => {
274
- const event = {
275
- httpMethod: 'GET',
276
- path: '/api/users/999',
277
- headers: {},
278
- };
279
-
280
- await middleware.before?.({
281
- event,
282
- context: mockContext,
283
- response: null,
284
- } as any);
285
- await middleware.after?.({
286
- event,
287
- context: mockContext,
288
- response: { statusCode: 404, body: '{"error":"Not found"}' },
289
- } as any);
290
-
291
- const spans = exporter.getFinishedSpans();
292
- expect(spans[0].attributes['http.response.status_code']).toBe(404);
293
- expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
294
- });
295
-
296
- it('should record response body when recordResponseBody is true', async () => {
297
- const responseMiddleware = telemetryMiddleware({
298
- recordResponseBody: true,
299
- });
300
- const responseBody = '{"users":[{"id":"1","name":"John"}]}';
301
-
302
- const event = {
303
- httpMethod: 'GET',
304
- path: '/api/users',
305
- headers: {},
306
- };
307
-
308
- await responseMiddleware.before?.({
309
- event,
310
- context: mockContext,
311
- response: null,
312
- } as any);
313
- await responseMiddleware.after?.({
314
- event,
315
- context: mockContext,
316
- response: { statusCode: 200, body: responseBody },
317
- } as any);
318
-
319
- const spans = exporter.getFinishedSpans();
320
- expect(spans[0].attributes['http.response.body']).toBe(responseBody);
321
- });
322
- });
323
-
324
- describe('onError hook', () => {
325
- it('should record error with exception', async () => {
326
- const event = {
327
- httpMethod: 'GET',
328
- path: '/api/error',
329
- headers: {},
330
- };
331
-
332
- await middleware.before?.({
333
- event,
334
- context: mockContext,
335
- response: null,
336
- } as any);
337
-
338
- const error = new Error('Database connection failed');
339
- await middleware.onError?.({
340
- event,
341
- context: mockContext,
342
- response: { statusCode: 500 },
343
- error,
344
- } as any);
345
-
346
- const spans = exporter.getFinishedSpans();
347
- expect(spans[0].attributes['http.response.status_code']).toBe(500);
348
- expect(spans[0].status.code).toBe(2); // SpanStatusCode.ERROR
349
- expect(spans[0].events).toHaveLength(1);
350
- expect(spans[0].events[0].name).toBe('exception');
351
- });
352
-
353
- it('should use error statusCode if available', async () => {
354
- const event = {
355
- httpMethod: 'GET',
356
- path: '/api/protected',
357
- headers: {},
358
- };
359
-
360
- await middleware.before?.({
361
- event,
362
- context: mockContext,
363
- response: null,
364
- } as any);
365
-
366
- const error = Object.assign(new Error('Forbidden'), { statusCode: 403 });
367
- await middleware.onError?.({
368
- event,
369
- context: mockContext,
370
- response: null,
371
- error,
372
- } as any);
373
-
374
- const spans = exporter.getFinishedSpans();
375
- expect(spans[0].attributes['http.response.status_code']).toBe(403);
376
- });
377
- });
378
-
379
- describe('withEventContext', () => {
380
- it('should run function within span context', async () => {
381
- const event = {
382
- httpMethod: 'GET',
383
- path: '/api/users',
384
- headers: {},
385
- };
386
-
387
- await middleware.before?.({
388
- event,
389
- context: mockContext,
390
- response: null,
391
- } as any);
392
-
393
- const result = await withEventContext(event, async () => {
394
- return 'executed-in-context';
395
- });
396
-
397
- expect(result).toBe('executed-in-context');
398
- });
399
-
400
- it('should run function normally when no context exists', async () => {
401
- const event = {};
402
-
403
- const result = await withEventContext(event, async () => {
404
- return 'executed-without-context';
405
- });
406
-
407
- expect(result).toBe('executed-without-context');
408
- });
409
- });
410
-
411
- describe('custom extractors', () => {
412
- it('should extract user ID using getUserId', async () => {
413
- const userMiddleware = telemetryMiddleware({
414
- getUserId: (event) => event.requestContext?.authorizer?.userId,
415
- });
416
-
417
- const event = {
418
- httpMethod: 'GET',
419
- path: '/api/profile',
420
- headers: {},
421
- requestContext: {
422
- authorizer: {
423
- userId: 'user-abc123',
424
- },
425
- },
426
- };
427
-
428
- await userMiddleware.before?.({
429
- event,
430
- context: mockContext,
431
- response: null,
432
- } as any);
433
- await userMiddleware.after?.({
434
- event,
435
- context: mockContext,
436
- response: { statusCode: 200 },
437
- } as any);
438
-
439
- const spans = exporter.getFinishedSpans();
440
- expect(spans[0].attributes['enduser.id']).toBe('user-abc123');
441
- });
442
-
443
- it('should extract endpoint name using getEndpointName', async () => {
444
- const endpointMiddleware = telemetryMiddleware({
445
- getEndpointName: (event) => event.endpointName,
446
- });
447
-
448
- const event = {
449
- httpMethod: 'GET',
450
- path: '/api/users',
451
- headers: {},
452
- endpointName: 'ListUsers',
453
- };
454
-
455
- await endpointMiddleware.before?.({
456
- event,
457
- context: mockContext,
458
- response: null,
459
- } as any);
460
- await endpointMiddleware.after?.({
461
- event,
462
- context: mockContext,
463
- response: { statusCode: 200 },
464
- } as any);
465
-
466
- const spans = exporter.getFinishedSpans();
467
- expect(spans[0].attributes['endpoint.name']).toBe('ListUsers');
468
- });
469
-
470
- it('should extract operation ID using getOperationId', async () => {
471
- const opMiddleware = telemetryMiddleware({
472
- getOperationId: (event) => event.operationId,
473
- });
474
-
475
- const event = {
476
- httpMethod: 'POST',
477
- path: '/api/orders',
478
- headers: {},
479
- operationId: 'createOrder',
480
- };
481
-
482
- await opMiddleware.before?.({
483
- event,
484
- context: mockContext,
485
- response: null,
486
- } as any);
487
- await opMiddleware.after?.({
488
- event,
489
- context: mockContext,
490
- response: { statusCode: 201 },
491
- } as any);
492
-
493
- const spans = exporter.getFinishedSpans();
494
- expect(spans[0].attributes['endpoint.operation_id']).toBe('createOrder');
495
- });
496
- });
497
-
498
- describe('trace context propagation', () => {
499
- it('should extract trace context from traceparent header', async () => {
500
- const traceId = '0af7651916cd43dd8448eb211c80319c';
501
- const parentSpanId = 'b7ad6b7169203331';
502
-
503
- const event = {
504
- httpMethod: 'GET',
505
- path: '/api/downstream',
506
- headers: {
507
- traceparent: `00-${traceId}-${parentSpanId}-01`,
508
- },
509
- };
510
-
511
- await middleware.before?.({
512
- event,
513
- context: mockContext,
514
- response: null,
515
- } as any);
516
- await middleware.after?.({
517
- event,
518
- context: mockContext,
519
- response: { statusCode: 200 },
520
- } as any);
521
-
522
- const spans = exporter.getFinishedSpans();
523
- expect(spans).toHaveLength(1);
524
- // The span should be created (even if not linked to parent in this basic setup)
525
- expect(spans[0].name).toContain('GET');
526
- });
527
- });
528
- });