@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.
- package/README.md +14 -4
- package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
- package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
- package/dist/instrumentation/http.d.mts +1 -1
- package/dist/instrumentation/index.cjs +2 -2
- package/dist/instrumentation/index.d.cts +2 -2
- package/dist/instrumentation/index.d.mts +3 -3
- package/dist/instrumentation/index.mjs +2 -2
- package/dist/instrumentation/middleware.cjs +1 -1
- package/dist/instrumentation/middleware.d.cts +1 -1
- package/dist/instrumentation/middleware.d.mts +1 -1
- package/dist/instrumentation/middleware.mjs +1 -1
- package/dist/instrumentation/otel.cjs +1 -1
- package/dist/instrumentation/otel.d.cts +1 -1
- package/dist/instrumentation/otel.d.mts +1 -1
- package/dist/instrumentation/otel.mjs +1 -1
- package/dist/logger/console.d.cts.map +1 -1
- package/dist/logger/pino.d.mts +2 -2
- package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
- package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
- package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
- package/dist/middleware-D1XEHvyy.mjs.map +1 -0
- package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
- package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
- package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
- package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
- package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
- package/dist/otel-B9bNoeZ2.mjs.map +1 -0
- package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
- package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
- package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
- package/dist/otel-BzjOtVxZ.cjs.map +1 -0
- package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
- package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
- package/dist/storage/kysely.cjs +48 -38
- package/dist/storage/kysely.cjs.map +1 -1
- package/dist/storage/kysely.d.cts +29 -9
- package/dist/storage/kysely.d.cts.map +1 -1
- package/dist/storage/kysely.d.mts +29 -9
- package/dist/storage/kysely.d.mts.map +1 -1
- package/dist/storage/kysely.mjs +48 -38
- package/dist/storage/kysely.mjs.map +1 -1
- package/dist/tracing-PnECEbEu.d.mts.map +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -72
- package/dist/middleware-4-GrQlOT.mjs.map +0 -1
- package/dist/middleware-DqPQpcup.cjs.map +0 -1
- package/dist/otel-40udNP5g.mjs.map +0 -1
- package/dist/otel-COrpz_GZ.cjs.map +0 -1
- package/src/Telescope.ts +0 -561
- package/src/__tests__/Telescope.spec.ts +0 -438
- package/src/__tests__/pino-transport.spec.ts +0 -115
- package/src/__tests__/redact.spec.ts +0 -336
- package/src/adapters/__tests__/lambda.spec.ts +0 -318
- package/src/adapters/__tests__/middy.spec.ts +0 -741
- package/src/adapters/index.ts +0 -22
- package/src/adapters/lambda.ts +0 -432
- package/src/adapters/middy.ts +0 -402
- package/src/adapters/types.ts +0 -181
- package/src/core.ts +0 -51
- package/src/index.ts +0 -34
- package/src/instrumentation/__tests__/core.spec.ts +0 -248
- package/src/instrumentation/__tests__/hono.spec.ts +0 -348
- package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
- package/src/instrumentation/__tests__/otel.spec.ts +0 -291
- package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
- package/src/instrumentation/core.ts +0 -228
- package/src/instrumentation/hono.ts +0 -270
- package/src/instrumentation/http.ts +0 -306
- package/src/instrumentation/index.ts +0 -90
- package/src/instrumentation/middleware.ts +0 -297
- package/src/instrumentation/otel.ts +0 -271
- package/src/instrumentation/setup.ts +0 -246
- package/src/instrumentation/tracing.ts +0 -145
- package/src/logger/__tests__/console.spec.ts +0 -366
- package/src/logger/__tests__/pino.spec.ts +0 -217
- package/src/logger/console.ts +0 -230
- package/src/logger/pino.ts +0 -201
- package/src/metrics/MetricsAggregator.ts +0 -555
- package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
- package/src/metrics/index.ts +0 -2
- package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
- package/src/metrics/storage/MetricsStorage.ts +0 -74
- package/src/metrics/storage/index.ts +0 -3
- package/src/metrics/storage/types.ts +0 -243
- package/src/otlp/__tests__/receiver.spec.ts +0 -367
- package/src/otlp/__tests__/transformer.spec.ts +0 -455
- package/src/otlp/hono.ts +0 -120
- package/src/otlp/index.ts +0 -22
- package/src/otlp/receiver.ts +0 -162
- package/src/otlp/transformer.ts +0 -367
- package/src/otlp/types.ts +0 -364
- package/src/redact.ts +0 -116
- package/src/server/__tests__/hono.spec.ts +0 -814
- package/src/server/hono.ts +0 -442
- package/src/storage/__tests__/kysely.spec.ts +0 -715
- package/src/storage/__tests__/memory.spec.ts +0 -694
- package/src/storage/kysely.ts +0 -599
- package/src/storage/memory.ts +0 -204
- package/src/types.ts +0 -447
- package/src/ui-assets.ts +0 -40
- package/tsconfig.json +0 -9
- package/ui/CHANGELOG.md +0 -7
- package/ui/index.html +0 -12
- package/ui/node_modules/.bin/tsc +0 -21
- package/ui/node_modules/.bin/tsserver +0 -21
- package/ui/node_modules/.bin/vite +0 -21
- package/ui/package.json +0 -24
- package/ui/src/App.tsx +0 -595
- package/ui/src/api.ts +0 -88
- package/ui/src/components/ExceptionDetail.tsx +0 -100
- package/ui/src/components/FilterBar.tsx +0 -162
- package/ui/src/components/LogDetail.tsx +0 -91
- package/ui/src/components/RequestDetail.tsx +0 -143
- package/ui/src/main.tsx +0 -10
- package/ui/src/styles.css +0 -11
- package/ui/src/types.ts +0 -63
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/src/vite-plugin-gkm-config.ts +0 -54
- package/ui/tsconfig.json +0 -20
- package/ui/tsconfig.tsbuildinfo +0 -1
- 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
|
-
});
|