@geekmidas/telescope 9.0.2 → 10.0.0-alpha.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.
Files changed (122) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
  3. package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
  4. package/dist/instrumentation/http.d.mts +1 -1
  5. package/dist/instrumentation/index.cjs +2 -2
  6. package/dist/instrumentation/index.d.cts +2 -2
  7. package/dist/instrumentation/index.d.mts +3 -3
  8. package/dist/instrumentation/index.mjs +2 -2
  9. package/dist/instrumentation/middleware.cjs +1 -1
  10. package/dist/instrumentation/middleware.d.cts +1 -1
  11. package/dist/instrumentation/middleware.d.mts +1 -1
  12. package/dist/instrumentation/middleware.mjs +1 -1
  13. package/dist/instrumentation/otel.cjs +1 -1
  14. package/dist/instrumentation/otel.d.cts +1 -1
  15. package/dist/instrumentation/otel.d.mts +1 -1
  16. package/dist/instrumentation/otel.mjs +1 -1
  17. package/dist/logger/console.d.cts.map +1 -1
  18. package/dist/logger/pino.d.mts +2 -2
  19. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  20. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  21. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  22. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  23. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  24. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  25. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  26. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  27. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  28. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  29. package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
  30. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  31. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  32. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
  34. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  35. package/dist/storage/kysely.cjs +48 -38
  36. package/dist/storage/kysely.cjs.map +1 -1
  37. package/dist/storage/kysely.d.cts +29 -9
  38. package/dist/storage/kysely.d.cts.map +1 -1
  39. package/dist/storage/kysely.d.mts +29 -9
  40. package/dist/storage/kysely.d.mts.map +1 -1
  41. package/dist/storage/kysely.mjs +48 -38
  42. package/dist/storage/kysely.mjs.map +1 -1
  43. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  44. package/package.json +6 -3
  45. package/CHANGELOG.md +0 -72
  46. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  47. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  48. package/dist/otel-40udNP5g.mjs.map +0 -1
  49. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  50. package/src/Telescope.ts +0 -561
  51. package/src/__tests__/Telescope.spec.ts +0 -438
  52. package/src/__tests__/pino-transport.spec.ts +0 -115
  53. package/src/__tests__/redact.spec.ts +0 -336
  54. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  55. package/src/adapters/__tests__/middy.spec.ts +0 -741
  56. package/src/adapters/index.ts +0 -22
  57. package/src/adapters/lambda.ts +0 -432
  58. package/src/adapters/middy.ts +0 -402
  59. package/src/adapters/types.ts +0 -181
  60. package/src/core.ts +0 -51
  61. package/src/index.ts +0 -34
  62. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  63. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  64. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  65. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  66. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  67. package/src/instrumentation/core.ts +0 -228
  68. package/src/instrumentation/hono.ts +0 -270
  69. package/src/instrumentation/http.ts +0 -306
  70. package/src/instrumentation/index.ts +0 -90
  71. package/src/instrumentation/middleware.ts +0 -297
  72. package/src/instrumentation/otel.ts +0 -271
  73. package/src/instrumentation/setup.ts +0 -246
  74. package/src/instrumentation/tracing.ts +0 -145
  75. package/src/logger/__tests__/console.spec.ts +0 -366
  76. package/src/logger/__tests__/pino.spec.ts +0 -217
  77. package/src/logger/console.ts +0 -230
  78. package/src/logger/pino.ts +0 -201
  79. package/src/metrics/MetricsAggregator.ts +0 -555
  80. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  81. package/src/metrics/index.ts +0 -2
  82. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  83. package/src/metrics/storage/MetricsStorage.ts +0 -74
  84. package/src/metrics/storage/index.ts +0 -3
  85. package/src/metrics/storage/types.ts +0 -243
  86. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  87. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  88. package/src/otlp/hono.ts +0 -120
  89. package/src/otlp/index.ts +0 -22
  90. package/src/otlp/receiver.ts +0 -162
  91. package/src/otlp/transformer.ts +0 -367
  92. package/src/otlp/types.ts +0 -364
  93. package/src/redact.ts +0 -116
  94. package/src/server/__tests__/hono.spec.ts +0 -814
  95. package/src/server/hono.ts +0 -442
  96. package/src/storage/__tests__/kysely.spec.ts +0 -715
  97. package/src/storage/__tests__/memory.spec.ts +0 -694
  98. package/src/storage/kysely.ts +0 -599
  99. package/src/storage/memory.ts +0 -204
  100. package/src/types.ts +0 -447
  101. package/src/ui-assets.ts +0 -40
  102. package/tsconfig.json +0 -9
  103. package/ui/CHANGELOG.md +0 -7
  104. package/ui/index.html +0 -12
  105. package/ui/node_modules/.bin/tsc +0 -21
  106. package/ui/node_modules/.bin/tsserver +0 -21
  107. package/ui/node_modules/.bin/vite +0 -21
  108. package/ui/package.json +0 -24
  109. package/ui/src/App.tsx +0 -595
  110. package/ui/src/api.ts +0 -88
  111. package/ui/src/components/ExceptionDetail.tsx +0 -100
  112. package/ui/src/components/FilterBar.tsx +0 -162
  113. package/ui/src/components/LogDetail.tsx +0 -91
  114. package/ui/src/components/RequestDetail.tsx +0 -143
  115. package/ui/src/main.tsx +0 -10
  116. package/ui/src/styles.css +0 -11
  117. package/ui/src/types.ts +0 -63
  118. package/ui/src/vite-env.d.ts +0 -1
  119. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  120. package/ui/tsconfig.json +0 -20
  121. package/ui/tsconfig.tsbuildinfo +0 -1
  122. package/ui/vite.config.ts +0 -13
@@ -1,694 +0,0 @@
1
- import { beforeEach, describe, expect, it } from 'vitest';
2
- import type { ExceptionEntry, LogEntry, RequestEntry } from '../../types';
3
- import { InMemoryStorage } from '../memory';
4
-
5
- describe('InMemoryStorage', () => {
6
- let storage: InMemoryStorage;
7
-
8
- beforeEach(() => {
9
- storage = new InMemoryStorage();
10
- });
11
-
12
- describe('requests', () => {
13
- const createRequest = (
14
- overrides: Partial<RequestEntry> = {},
15
- ): RequestEntry => ({
16
- id: `req-${Math.random().toString(36).slice(2)}`,
17
- method: 'GET',
18
- path: '/api/users',
19
- url: 'http://localhost:3000/api/users',
20
- headers: { 'content-type': 'application/json' },
21
- query: {},
22
- status: 200,
23
- responseHeaders: {},
24
- duration: 50,
25
- timestamp: new Date(),
26
- ...overrides,
27
- });
28
-
29
- it('should save and retrieve a request', async () => {
30
- const request = createRequest({ id: 'req-123' });
31
- await storage.saveRequest(request);
32
-
33
- const retrieved = await storage.getRequest('req-123');
34
- expect(retrieved).toEqual(request);
35
- });
36
-
37
- it('should return null for non-existent request', async () => {
38
- const result = await storage.getRequest('non-existent');
39
- expect(result).toBeNull();
40
- });
41
-
42
- it('should list requests in reverse chronological order', async () => {
43
- const req1 = createRequest({
44
- id: 'req-1',
45
- timestamp: new Date('2024-01-01T10:00:00Z'),
46
- });
47
- const req2 = createRequest({
48
- id: 'req-2',
49
- timestamp: new Date('2024-01-01T11:00:00Z'),
50
- });
51
- const req3 = createRequest({
52
- id: 'req-3',
53
- timestamp: new Date('2024-01-01T12:00:00Z'),
54
- });
55
-
56
- await storage.saveRequest(req1);
57
- await storage.saveRequest(req2);
58
- await storage.saveRequest(req3);
59
-
60
- const requests = await storage.getRequests();
61
- expect(requests.map((r) => r.id)).toEqual(['req-3', 'req-2', 'req-1']);
62
- });
63
-
64
- it('should respect limit and offset options', async () => {
65
- for (let i = 0; i < 10; i++) {
66
- await storage.saveRequest(
67
- createRequest({
68
- id: `req-${i}`,
69
- timestamp: new Date(Date.now() + i * 1000),
70
- }),
71
- );
72
- }
73
-
74
- const page1 = await storage.getRequests({ limit: 3, offset: 0 });
75
- expect(page1).toHaveLength(3);
76
- expect(page1[0].id).toBe('req-9');
77
-
78
- const page2 = await storage.getRequests({ limit: 3, offset: 3 });
79
- expect(page2).toHaveLength(3);
80
- expect(page2[0].id).toBe('req-6');
81
- });
82
-
83
- it('should filter by search term in path', async () => {
84
- await storage.saveRequest(
85
- createRequest({
86
- id: 'req-1',
87
- path: '/api/users',
88
- url: 'http://localhost/api/users',
89
- }),
90
- );
91
- await storage.saveRequest(
92
- createRequest({
93
- id: 'req-2',
94
- path: '/api/posts',
95
- url: 'http://localhost/api/posts',
96
- }),
97
- );
98
- await storage.saveRequest(
99
- createRequest({
100
- id: 'req-3',
101
- path: '/api/orders',
102
- url: 'http://localhost/api/orders',
103
- }),
104
- );
105
-
106
- const results = await storage.getRequests({ search: 'posts' });
107
- expect(results).toHaveLength(1);
108
- expect(results[0].id).toBe('req-2');
109
- });
110
-
111
- it('should filter by date range', async () => {
112
- await storage.saveRequest(
113
- createRequest({ id: 'req-1', timestamp: new Date('2024-01-01') }),
114
- );
115
- await storage.saveRequest(
116
- createRequest({ id: 'req-2', timestamp: new Date('2024-01-15') }),
117
- );
118
- await storage.saveRequest(
119
- createRequest({ id: 'req-3', timestamp: new Date('2024-02-01') }),
120
- );
121
-
122
- const results = await storage.getRequests({
123
- after: new Date('2024-01-10'),
124
- before: new Date('2024-01-20'),
125
- });
126
-
127
- expect(results).toHaveLength(1);
128
- expect(results[0].id).toBe('req-2');
129
- });
130
-
131
- it('should filter by tags', async () => {
132
- await storage.saveRequest(
133
- createRequest({ id: 'req-1', tags: ['auth', 'api'] }),
134
- );
135
- await storage.saveRequest(createRequest({ id: 'req-2', tags: ['api'] }));
136
- await storage.saveRequest(
137
- createRequest({ id: 'req-3', tags: ['admin'] }),
138
- );
139
-
140
- const results = await storage.getRequests({ tags: ['auth'] });
141
- expect(results).toHaveLength(1);
142
- expect(results[0].id).toBe('req-1');
143
- });
144
-
145
- it('should enforce maxEntries limit', async () => {
146
- const smallStorage = new InMemoryStorage({ maxEntries: 5 });
147
-
148
- for (let i = 0; i < 10; i++) {
149
- await smallStorage.saveRequest(
150
- createRequest({
151
- id: `req-${i}`,
152
- timestamp: new Date(Date.now() + i * 1000),
153
- }),
154
- );
155
- }
156
-
157
- const requests = await smallStorage.getRequests();
158
- expect(requests).toHaveLength(5);
159
- // Should keep the newest entries
160
- expect(requests[0].id).toBe('req-9');
161
- expect(requests[4].id).toBe('req-5');
162
- });
163
- });
164
-
165
- describe('exceptions', () => {
166
- const createException = (
167
- overrides: Partial<ExceptionEntry> = {},
168
- ): ExceptionEntry => ({
169
- id: `exc-${Math.random().toString(36).slice(2)}`,
170
- name: 'Error',
171
- message: 'Something went wrong',
172
- stack: [
173
- {
174
- file: '/app/src/index.ts',
175
- line: 10,
176
- column: 5,
177
- function: 'main',
178
- isApp: true,
179
- },
180
- ],
181
- timestamp: new Date(),
182
- handled: false,
183
- ...overrides,
184
- });
185
-
186
- it('should save and retrieve an exception', async () => {
187
- const exception = createException({ id: 'exc-123' });
188
- await storage.saveException(exception);
189
-
190
- const retrieved = await storage.getException('exc-123');
191
- expect(retrieved).toEqual(exception);
192
- });
193
-
194
- it('should return null for non-existent exception', async () => {
195
- const result = await storage.getException('non-existent');
196
- expect(result).toBeNull();
197
- });
198
-
199
- it('should list exceptions in reverse chronological order', async () => {
200
- await storage.saveException(
201
- createException({
202
- id: 'exc-1',
203
- timestamp: new Date('2024-01-01T10:00:00Z'),
204
- }),
205
- );
206
- await storage.saveException(
207
- createException({
208
- id: 'exc-2',
209
- timestamp: new Date('2024-01-01T11:00:00Z'),
210
- }),
211
- );
212
-
213
- const exceptions = await storage.getExceptions();
214
- expect(exceptions.map((e) => e.id)).toEqual(['exc-2', 'exc-1']);
215
- });
216
-
217
- it('should filter exceptions by search term in message', async () => {
218
- await storage.saveException(
219
- createException({ id: 'exc-1', message: 'Database connection failed' }),
220
- );
221
- await storage.saveException(
222
- createException({ id: 'exc-2', message: 'Invalid user input' }),
223
- );
224
-
225
- const results = await storage.getExceptions({ search: 'database' });
226
- expect(results).toHaveLength(1);
227
- expect(results[0].id).toBe('exc-1');
228
- });
229
- });
230
-
231
- describe('logs', () => {
232
- const createLog = (overrides: Partial<LogEntry> = {}): LogEntry => ({
233
- id: `log-${Math.random().toString(36).slice(2)}`,
234
- level: 'info',
235
- message: 'Test log message',
236
- timestamp: new Date(),
237
- ...overrides,
238
- });
239
-
240
- it('should save and retrieve logs', async () => {
241
- await storage.saveLog(createLog({ id: 'log-1', message: 'First log' }));
242
- await storage.saveLog(createLog({ id: 'log-2', message: 'Second log' }));
243
-
244
- const logs = await storage.getLogs();
245
- expect(logs).toHaveLength(2);
246
- });
247
-
248
- it('should filter logs by level via search', async () => {
249
- await storage.saveLog(createLog({ id: 'log-1', level: 'debug' }));
250
- await storage.saveLog(createLog({ id: 'log-2', level: 'error' }));
251
- await storage.saveLog(createLog({ id: 'log-3', level: 'error' }));
252
-
253
- const results = await storage.getLogs({ search: 'error' });
254
- expect(results).toHaveLength(2);
255
- });
256
-
257
- it('should include context in logs', async () => {
258
- await storage.saveLog(
259
- createLog({
260
- id: 'log-1',
261
- context: { userId: '123', action: 'login' },
262
- }),
263
- );
264
-
265
- const logs = await storage.getLogs();
266
- expect(logs[0].context).toEqual({ userId: '123', action: 'login' });
267
- });
268
- });
269
-
270
- describe('prune', () => {
271
- it('should remove entries older than specified date', async () => {
272
- const oldDate = new Date('2024-01-01');
273
- const newDate = new Date('2024-06-01');
274
-
275
- await storage.saveRequest({
276
- id: 'req-old',
277
- method: 'GET',
278
- path: '/old',
279
- url: 'http://localhost/old',
280
- headers: {},
281
- query: {},
282
- status: 200,
283
- responseHeaders: {},
284
- duration: 10,
285
- timestamp: oldDate,
286
- });
287
-
288
- await storage.saveRequest({
289
- id: 'req-new',
290
- method: 'GET',
291
- path: '/new',
292
- url: 'http://localhost/new',
293
- headers: {},
294
- query: {},
295
- status: 200,
296
- responseHeaders: {},
297
- duration: 10,
298
- timestamp: newDate,
299
- });
300
-
301
- await storage.saveException({
302
- id: 'exc-old',
303
- name: 'Error',
304
- message: 'Old error',
305
- stack: [],
306
- timestamp: oldDate,
307
- handled: false,
308
- });
309
-
310
- await storage.saveLog({
311
- id: 'log-old',
312
- level: 'info',
313
- message: 'Old log',
314
- timestamp: oldDate,
315
- });
316
-
317
- const pruneDate = new Date('2024-03-01');
318
- const deleted = await storage.prune(pruneDate);
319
-
320
- expect(deleted).toBe(3);
321
-
322
- const requests = await storage.getRequests();
323
- expect(requests).toHaveLength(1);
324
- expect(requests[0].id).toBe('req-new');
325
-
326
- const exceptions = await storage.getExceptions();
327
- expect(exceptions).toHaveLength(0);
328
-
329
- const logs = await storage.getLogs();
330
- expect(logs).toHaveLength(0);
331
- });
332
- });
333
-
334
- describe('getStats', () => {
335
- it('should return correct statistics', async () => {
336
- await storage.saveRequest({
337
- id: 'req-1',
338
- method: 'GET',
339
- path: '/api',
340
- url: 'http://localhost/api',
341
- headers: {},
342
- query: {},
343
- status: 200,
344
- responseHeaders: {},
345
- duration: 10,
346
- timestamp: new Date(),
347
- });
348
-
349
- await storage.saveException({
350
- id: 'exc-1',
351
- name: 'Error',
352
- message: 'Test',
353
- stack: [],
354
- timestamp: new Date(),
355
- handled: false,
356
- });
357
-
358
- await storage.saveLog({
359
- id: 'log-1',
360
- level: 'info',
361
- message: 'Test',
362
- timestamp: new Date(),
363
- });
364
- await storage.saveLog({
365
- id: 'log-2',
366
- level: 'error',
367
- message: 'Test',
368
- timestamp: new Date(),
369
- });
370
-
371
- const stats = await storage.getStats();
372
-
373
- expect(stats.requests).toBe(1);
374
- expect(stats.exceptions).toBe(1);
375
- expect(stats.logs).toBe(2);
376
- });
377
- });
378
-
379
- describe('clear', () => {
380
- it('should remove all entries', async () => {
381
- await storage.saveRequest({
382
- id: 'req-1',
383
- method: 'GET',
384
- path: '/api',
385
- url: 'http://localhost/api',
386
- headers: {},
387
- query: {},
388
- status: 200,
389
- responseHeaders: {},
390
- duration: 10,
391
- timestamp: new Date(),
392
- });
393
-
394
- await storage.saveException({
395
- id: 'exc-1',
396
- name: 'Error',
397
- message: 'Test',
398
- stack: [],
399
- timestamp: new Date(),
400
- handled: false,
401
- });
402
-
403
- storage.clear();
404
-
405
- const stats = await storage.getStats();
406
- expect(stats.requests).toBe(0);
407
- expect(stats.exceptions).toBe(0);
408
- expect(stats.logs).toBe(0);
409
- });
410
- });
411
-
412
- describe('bulk operations', () => {
413
- it('should save multiple requests at once', async () => {
414
- await storage.saveRequests([
415
- {
416
- id: 'req-1',
417
- method: 'GET',
418
- path: '/api/a',
419
- url: 'http://localhost/api/a',
420
- headers: {},
421
- query: {},
422
- status: 200,
423
- responseHeaders: {},
424
- duration: 10,
425
- timestamp: new Date(),
426
- },
427
- {
428
- id: 'req-2',
429
- method: 'POST',
430
- path: '/api/b',
431
- url: 'http://localhost/api/b',
432
- headers: {},
433
- query: {},
434
- status: 201,
435
- responseHeaders: {},
436
- duration: 20,
437
- timestamp: new Date(),
438
- },
439
- ]);
440
-
441
- const requests = await storage.getRequests();
442
- expect(requests).toHaveLength(2);
443
- });
444
-
445
- it('should save multiple exceptions at once', async () => {
446
- await storage.saveExceptions([
447
- {
448
- id: 'exc-1',
449
- name: 'Error',
450
- message: 'First',
451
- stack: [],
452
- timestamp: new Date(),
453
- handled: false,
454
- },
455
- {
456
- id: 'exc-2',
457
- name: 'Error',
458
- message: 'Second',
459
- stack: [],
460
- timestamp: new Date(),
461
- handled: true,
462
- },
463
- ]);
464
-
465
- const exceptions = await storage.getExceptions();
466
- expect(exceptions).toHaveLength(2);
467
- });
468
-
469
- it('should save multiple logs at once', async () => {
470
- await storage.saveLogs([
471
- { id: 'log-1', level: 'info', message: 'First', timestamp: new Date() },
472
- {
473
- id: 'log-2',
474
- level: 'error',
475
- message: 'Second',
476
- timestamp: new Date(),
477
- },
478
- ]);
479
-
480
- const logs = await storage.getLogs();
481
- expect(logs).toHaveLength(2);
482
- });
483
- });
484
-
485
- describe('request filters', () => {
486
- it('should filter by HTTP method', async () => {
487
- await storage.saveRequest({
488
- id: 'req-1',
489
- method: 'GET',
490
- path: '/api',
491
- url: 'http://localhost/api',
492
- headers: {},
493
- query: {},
494
- status: 200,
495
- responseHeaders: {},
496
- duration: 10,
497
- timestamp: new Date(),
498
- });
499
- await storage.saveRequest({
500
- id: 'req-2',
501
- method: 'POST',
502
- path: '/api',
503
- url: 'http://localhost/api',
504
- headers: {},
505
- query: {},
506
- status: 201,
507
- responseHeaders: {},
508
- duration: 10,
509
- timestamp: new Date(),
510
- });
511
-
512
- const getRequests = await storage.getRequests({ method: 'GET' });
513
- expect(getRequests).toHaveLength(1);
514
- expect(getRequests[0].id).toBe('req-1');
515
- });
516
-
517
- it('should filter by exact status code', async () => {
518
- await storage.saveRequest({
519
- id: 'req-1',
520
- method: 'GET',
521
- path: '/api',
522
- url: 'http://localhost/api',
523
- headers: {},
524
- query: {},
525
- status: 200,
526
- responseHeaders: {},
527
- duration: 10,
528
- timestamp: new Date(),
529
- });
530
- await storage.saveRequest({
531
- id: 'req-2',
532
- method: 'GET',
533
- path: '/api',
534
- url: 'http://localhost/api',
535
- headers: {},
536
- query: {},
537
- status: 404,
538
- responseHeaders: {},
539
- duration: 10,
540
- timestamp: new Date(),
541
- });
542
-
543
- const notFoundRequests = await storage.getRequests({ status: '404' });
544
- expect(notFoundRequests).toHaveLength(1);
545
- expect(notFoundRequests[0].id).toBe('req-2');
546
- });
547
-
548
- it('should filter by status category (2xx)', async () => {
549
- await storage.saveRequest({
550
- id: 'req-1',
551
- method: 'GET',
552
- path: '/api',
553
- url: 'http://localhost/api',
554
- headers: {},
555
- query: {},
556
- status: 200,
557
- responseHeaders: {},
558
- duration: 10,
559
- timestamp: new Date(),
560
- });
561
- await storage.saveRequest({
562
- id: 'req-2',
563
- method: 'GET',
564
- path: '/api',
565
- url: 'http://localhost/api',
566
- headers: {},
567
- query: {},
568
- status: 201,
569
- responseHeaders: {},
570
- duration: 10,
571
- timestamp: new Date(),
572
- });
573
- await storage.saveRequest({
574
- id: 'req-3',
575
- method: 'GET',
576
- path: '/api',
577
- url: 'http://localhost/api',
578
- headers: {},
579
- query: {},
580
- status: 500,
581
- responseHeaders: {},
582
- duration: 10,
583
- timestamp: new Date(),
584
- });
585
-
586
- const successRequests = await storage.getRequests({ status: '2xx' });
587
- expect(successRequests).toHaveLength(2);
588
- });
589
-
590
- it('should filter by status category (5xx)', async () => {
591
- await storage.saveRequest({
592
- id: 'req-1',
593
- method: 'GET',
594
- path: '/api',
595
- url: 'http://localhost/api',
596
- headers: {},
597
- query: {},
598
- status: 200,
599
- responseHeaders: {},
600
- duration: 10,
601
- timestamp: new Date(),
602
- });
603
- await storage.saveRequest({
604
- id: 'req-2',
605
- method: 'GET',
606
- path: '/api',
607
- url: 'http://localhost/api',
608
- headers: {},
609
- query: {},
610
- status: 500,
611
- responseHeaders: {},
612
- duration: 10,
613
- timestamp: new Date(),
614
- });
615
- await storage.saveRequest({
616
- id: 'req-3',
617
- method: 'GET',
618
- path: '/api',
619
- url: 'http://localhost/api',
620
- headers: {},
621
- query: {},
622
- status: 503,
623
- responseHeaders: {},
624
- duration: 10,
625
- timestamp: new Date(),
626
- });
627
-
628
- const errorRequests = await storage.getRequests({ status: '5xx' });
629
- expect(errorRequests).toHaveLength(2);
630
- });
631
- });
632
-
633
- describe('log filters', () => {
634
- it('should filter logs by level', async () => {
635
- await storage.saveLog({
636
- id: 'log-1',
637
- level: 'info',
638
- message: 'Info message',
639
- timestamp: new Date(),
640
- });
641
- await storage.saveLog({
642
- id: 'log-2',
643
- level: 'error',
644
- message: 'Error message',
645
- timestamp: new Date(),
646
- });
647
- await storage.saveLog({
648
- id: 'log-3',
649
- level: 'error',
650
- message: 'Another error',
651
- timestamp: new Date(),
652
- });
653
-
654
- const errorLogs = await storage.getLogs({ level: 'error' });
655
- expect(errorLogs).toHaveLength(2);
656
- });
657
- });
658
-
659
- describe('enforceLimit', () => {
660
- it('should enforce limit on exceptions', async () => {
661
- const smallStorage = new InMemoryStorage({ maxEntries: 3 });
662
-
663
- for (let i = 0; i < 5; i++) {
664
- await smallStorage.saveException({
665
- id: `exc-${i}`,
666
- name: 'Error',
667
- message: `Error ${i}`,
668
- stack: [],
669
- timestamp: new Date(Date.now() + i * 1000),
670
- handled: false,
671
- });
672
- }
673
-
674
- const exceptions = await smallStorage.getExceptions();
675
- expect(exceptions).toHaveLength(3);
676
- });
677
-
678
- it('should enforce limit on logs', async () => {
679
- const smallStorage = new InMemoryStorage({ maxEntries: 3 });
680
-
681
- for (let i = 0; i < 5; i++) {
682
- await smallStorage.saveLog({
683
- id: `log-${i}`,
684
- level: 'info',
685
- message: `Log ${i}`,
686
- timestamp: new Date(Date.now() + i * 1000),
687
- });
688
- }
689
-
690
- const logs = await smallStorage.getLogs();
691
- expect(logs).toHaveLength(3);
692
- });
693
- });
694
- });