observability-toolkit 1.1.0 → 1.3.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 (89) hide show
  1. package/dist/backends/index.d.ts +13 -0
  2. package/dist/backends/index.d.ts.map +1 -1
  3. package/dist/backends/local-jsonl.d.ts +2 -1
  4. package/dist/backends/local-jsonl.d.ts.map +1 -1
  5. package/dist/backends/local-jsonl.js +62 -2
  6. package/dist/backends/local-jsonl.js.map +1 -1
  7. package/dist/backends/local-jsonl.test.d.ts +2 -0
  8. package/dist/backends/local-jsonl.test.d.ts.map +1 -0
  9. package/dist/backends/local-jsonl.test.js +558 -0
  10. package/dist/backends/local-jsonl.test.js.map +1 -0
  11. package/dist/backends/signoz-api.d.ts +9 -2
  12. package/dist/backends/signoz-api.d.ts.map +1 -1
  13. package/dist/backends/signoz-api.js +181 -106
  14. package/dist/backends/signoz-api.js.map +1 -1
  15. package/dist/backends/signoz-api.test.d.ts +2 -0
  16. package/dist/backends/signoz-api.test.d.ts.map +1 -0
  17. package/dist/backends/signoz-api.test.js +904 -0
  18. package/dist/backends/signoz-api.test.js.map +1 -0
  19. package/dist/lib/constants.d.ts +1 -0
  20. package/dist/lib/constants.d.ts.map +1 -1
  21. package/dist/lib/constants.js +3 -0
  22. package/dist/lib/constants.js.map +1 -1
  23. package/dist/lib/constants.test.d.ts +5 -0
  24. package/dist/lib/constants.test.d.ts.map +1 -0
  25. package/dist/lib/constants.test.js +199 -0
  26. package/dist/lib/constants.test.js.map +1 -0
  27. package/dist/lib/file-utils.test.d.ts +2 -0
  28. package/dist/lib/file-utils.test.d.ts.map +1 -0
  29. package/dist/lib/file-utils.test.js +422 -0
  30. package/dist/lib/file-utils.test.js.map +1 -0
  31. package/dist/server.js +5 -1
  32. package/dist/server.js.map +1 -1
  33. package/dist/tools/context-stats.d.ts +2 -2
  34. package/dist/tools/context-stats.d.ts.map +1 -1
  35. package/dist/tools/context-stats.js +2 -1
  36. package/dist/tools/context-stats.js.map +1 -1
  37. package/dist/tools/context-stats.test.d.ts +5 -0
  38. package/dist/tools/context-stats.test.d.ts.map +1 -0
  39. package/dist/tools/context-stats.test.js +339 -0
  40. package/dist/tools/context-stats.test.js.map +1 -0
  41. package/dist/tools/get-trace-url.test.d.ts +5 -0
  42. package/dist/tools/get-trace-url.test.d.ts.map +1 -0
  43. package/dist/tools/get-trace-url.test.js +423 -0
  44. package/dist/tools/get-trace-url.test.js.map +1 -0
  45. package/dist/tools/health-check.test.d.ts +5 -0
  46. package/dist/tools/health-check.test.d.ts.map +1 -0
  47. package/dist/tools/health-check.test.js +393 -0
  48. package/dist/tools/health-check.test.js.map +1 -0
  49. package/dist/tools/index.d.ts +1 -0
  50. package/dist/tools/index.d.ts.map +1 -1
  51. package/dist/tools/index.js +1 -0
  52. package/dist/tools/index.js.map +1 -1
  53. package/dist/tools/query-llm-events.d.ts +82 -0
  54. package/dist/tools/query-llm-events.d.ts.map +1 -0
  55. package/dist/tools/query-llm-events.js +60 -0
  56. package/dist/tools/query-llm-events.js.map +1 -0
  57. package/dist/tools/query-llm-events.test.d.ts +5 -0
  58. package/dist/tools/query-llm-events.test.d.ts.map +1 -0
  59. package/dist/tools/query-llm-events.test.js +111 -0
  60. package/dist/tools/query-llm-events.test.js.map +1 -0
  61. package/dist/tools/query-logs.d.ts +7 -2
  62. package/dist/tools/query-logs.d.ts.map +1 -1
  63. package/dist/tools/query-logs.js +7 -6
  64. package/dist/tools/query-logs.js.map +1 -1
  65. package/dist/tools/query-logs.test.d.ts +5 -0
  66. package/dist/tools/query-logs.test.d.ts.map +1 -0
  67. package/dist/tools/query-logs.test.js +668 -0
  68. package/dist/tools/query-logs.test.js.map +1 -0
  69. package/dist/tools/query-metrics.d.ts +2 -2
  70. package/dist/tools/query-metrics.d.ts.map +1 -1
  71. package/dist/tools/query-metrics.js +2 -1
  72. package/dist/tools/query-metrics.js.map +1 -1
  73. package/dist/tools/query-metrics.test.d.ts +5 -0
  74. package/dist/tools/query-metrics.test.d.ts.map +1 -0
  75. package/dist/tools/query-metrics.test.js +559 -0
  76. package/dist/tools/query-metrics.test.js.map +1 -0
  77. package/dist/tools/query-traces.d.ts +8 -2
  78. package/dist/tools/query-traces.d.ts.map +1 -1
  79. package/dist/tools/query-traces.js +4 -1
  80. package/dist/tools/query-traces.js.map +1 -1
  81. package/dist/tools/query-traces.test.d.ts +5 -0
  82. package/dist/tools/query-traces.test.d.ts.map +1 -0
  83. package/dist/tools/query-traces.test.js +547 -0
  84. package/dist/tools/query-traces.test.js.map +1 -0
  85. package/dist/tools/setup-claudeignore.test.d.ts +2 -0
  86. package/dist/tools/setup-claudeignore.test.d.ts.map +1 -0
  87. package/dist/tools/setup-claudeignore.test.js +236 -0
  88. package/dist/tools/setup-claudeignore.test.js.map +1 -0
  89. package/package.json +1 -1
@@ -0,0 +1,668 @@
1
+ /**
2
+ * Unit tests for query-logs tool
3
+ */
4
+ import { describe, it, beforeEach, afterEach } from 'node:test';
5
+ import assert from 'node:assert';
6
+ import { queryLogs, queryLogsSchema, queryLogsTool } from './query-logs.js';
7
+ // Mock data storage
8
+ let mockLocalLogs = [];
9
+ let mockSignozLogs = [];
10
+ // Create mock backends using the TelemetryBackend interface
11
+ function createMockLocalBackend() {
12
+ return {
13
+ name: 'mock-local',
14
+ queryLogs: async () => mockLocalLogs,
15
+ queryTraces: async () => [],
16
+ queryMetrics: async () => [],
17
+ healthCheck: async () => ({ status: 'ok' }),
18
+ };
19
+ }
20
+ function createMockSignozBackend() {
21
+ return {
22
+ name: 'mock-signoz',
23
+ queryLogs: async () => mockSignozLogs,
24
+ queryTraces: async () => [],
25
+ queryMetrics: async () => [],
26
+ healthCheck: async () => ({ status: 'ok' }),
27
+ };
28
+ }
29
+ // Options with mock backends
30
+ function getMockOptions() {
31
+ return {
32
+ localBackend: createMockLocalBackend(),
33
+ signozBackend: createMockSignozBackend(),
34
+ };
35
+ }
36
+ // Store original env values
37
+ const originalEnv = {
38
+ SIGNOZ_URL: process.env.SIGNOZ_URL,
39
+ SIGNOZ_API_KEY: process.env.SIGNOZ_API_KEY,
40
+ };
41
+ describe('query-logs tool', () => {
42
+ // Test the schema validation
43
+ describe('queryLogsSchema', () => {
44
+ it('should validate input with backend=local', () => {
45
+ const result = queryLogsSchema.parse({ backend: 'local' });
46
+ assert.deepStrictEqual(result.backend, 'local');
47
+ });
48
+ it('should validate input with backend=signoz', () => {
49
+ const result = queryLogsSchema.parse({ backend: 'signoz' });
50
+ assert.deepStrictEqual(result.backend, 'signoz');
51
+ });
52
+ it('should validate input with backend=auto', () => {
53
+ const result = queryLogsSchema.parse({ backend: 'auto' });
54
+ assert.deepStrictEqual(result.backend, 'auto');
55
+ });
56
+ it('should default backend to auto when not provided', () => {
57
+ const result = queryLogsSchema.parse({});
58
+ assert.deepStrictEqual(result.backend, 'auto');
59
+ });
60
+ it('should reject invalid backend values', () => {
61
+ assert.throws(() => queryLogsSchema.parse({ backend: 'invalid' }), { name: 'ZodError' });
62
+ });
63
+ it('should validate optional severity field', () => {
64
+ const result = queryLogsSchema.parse({ severity: 'ERROR' });
65
+ assert.deepStrictEqual(result.severity, 'ERROR');
66
+ });
67
+ it('should validate optional search field', () => {
68
+ const result = queryLogsSchema.parse({ search: 'error message' });
69
+ assert.deepStrictEqual(result.search, 'error message');
70
+ });
71
+ it('should validate optional traceId field', () => {
72
+ const result = queryLogsSchema.parse({ traceId: 'abc123' });
73
+ assert.deepStrictEqual(result.traceId, 'abc123');
74
+ });
75
+ it('should validate optional startDate field', () => {
76
+ const result = queryLogsSchema.parse({ startDate: '2026-01-01' });
77
+ assert.deepStrictEqual(result.startDate, '2026-01-01');
78
+ });
79
+ it('should validate optional endDate field', () => {
80
+ const result = queryLogsSchema.parse({ endDate: '2026-01-31' });
81
+ assert.deepStrictEqual(result.endDate, '2026-01-31');
82
+ });
83
+ it('should validate limit field with default of 50', () => {
84
+ const result = queryLogsSchema.parse({});
85
+ assert.deepStrictEqual(result.limit, 50);
86
+ });
87
+ it('should accept custom limit value', () => {
88
+ const result = queryLogsSchema.parse({ limit: 100 });
89
+ assert.deepStrictEqual(result.limit, 100);
90
+ });
91
+ it('should reject non-numeric limit', () => {
92
+ assert.throws(() => queryLogsSchema.parse({ limit: 'fifty' }), { name: 'ZodError' });
93
+ });
94
+ it('should validate multiple fields together', () => {
95
+ const result = queryLogsSchema.parse({
96
+ backend: 'local',
97
+ severity: 'WARN',
98
+ search: 'test',
99
+ traceId: 'trace-123',
100
+ startDate: '2026-01-01',
101
+ endDate: '2026-01-31',
102
+ limit: 25,
103
+ });
104
+ assert.deepStrictEqual(result.backend, 'local');
105
+ assert.deepStrictEqual(result.severity, 'WARN');
106
+ assert.deepStrictEqual(result.search, 'test');
107
+ assert.deepStrictEqual(result.traceId, 'trace-123');
108
+ assert.deepStrictEqual(result.startDate, '2026-01-01');
109
+ assert.deepStrictEqual(result.endDate, '2026-01-31');
110
+ assert.deepStrictEqual(result.limit, 25);
111
+ });
112
+ });
113
+ // Test query logs handler function
114
+ describe('queryLogs handler', () => {
115
+ // Reset state before each test
116
+ beforeEach(() => {
117
+ mockLocalLogs = [];
118
+ mockSignozLogs = [];
119
+ // Clear environment variables
120
+ process.env.SIGNOZ_URL = '';
121
+ process.env.SIGNOZ_API_KEY = '';
122
+ });
123
+ afterEach(() => {
124
+ // Restore original environment
125
+ process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
126
+ process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
127
+ });
128
+ it('should query local backend when backend=local', async () => {
129
+ mockLocalLogs = [
130
+ {
131
+ timestamp: '2026-01-28T12:00:00Z',
132
+ severity: 'ERROR',
133
+ body: 'Test error message',
134
+ },
135
+ ];
136
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
137
+ assert.strictEqual(result.backend, 'local');
138
+ assert.strictEqual(result.count, 1);
139
+ assert.ok(Array.isArray(result.logs));
140
+ });
141
+ it('should include severity filtering in options', async () => {
142
+ mockLocalLogs = [
143
+ {
144
+ timestamp: '2026-01-28T12:00:00Z',
145
+ severity: 'ERROR',
146
+ body: 'Error message',
147
+ },
148
+ {
149
+ timestamp: '2026-01-28T12:01:00Z',
150
+ severity: 'INFO',
151
+ body: 'Info message',
152
+ },
153
+ ];
154
+ const result = await queryLogs({
155
+ backend: 'local',
156
+ severity: 'ERROR',
157
+ }, getMockOptions());
158
+ assert.ok(result);
159
+ assert.strictEqual(typeof result.count, 'number');
160
+ });
161
+ it('should include search text filtering in options', async () => {
162
+ mockLocalLogs = [
163
+ {
164
+ timestamp: '2026-01-28T12:00:00Z',
165
+ severity: 'ERROR',
166
+ body: 'Database connection failed',
167
+ },
168
+ ];
169
+ const result = await queryLogs({
170
+ backend: 'local',
171
+ search: 'connection',
172
+ }, getMockOptions());
173
+ assert.ok(result);
174
+ assert.strictEqual(typeof result.count, 'number');
175
+ });
176
+ it('should include traceId filtering in options', async () => {
177
+ mockLocalLogs = [
178
+ {
179
+ timestamp: '2026-01-28T12:00:00Z',
180
+ severity: 'ERROR',
181
+ body: 'Error in span',
182
+ traceId: 'trace-123',
183
+ },
184
+ ];
185
+ const result = await queryLogs({
186
+ backend: 'local',
187
+ traceId: 'trace-123',
188
+ }, getMockOptions());
189
+ assert.ok(result);
190
+ assert.strictEqual(typeof result.count, 'number');
191
+ });
192
+ it('should include date range filtering in options', async () => {
193
+ mockLocalLogs = [
194
+ {
195
+ timestamp: '2026-01-15T12:00:00Z',
196
+ severity: 'INFO',
197
+ body: 'Message 1',
198
+ },
199
+ ];
200
+ const result = await queryLogs({
201
+ backend: 'local',
202
+ startDate: '2026-01-01',
203
+ endDate: '2026-01-31',
204
+ }, getMockOptions());
205
+ assert.ok(result);
206
+ assert.strictEqual(typeof result.count, 'number');
207
+ });
208
+ it('should respect limit parameter', async () => {
209
+ mockLocalLogs = [
210
+ {
211
+ timestamp: '2026-01-28T12:00:00Z',
212
+ severity: 'INFO',
213
+ body: 'Message 1',
214
+ },
215
+ {
216
+ timestamp: '2026-01-28T12:01:00Z',
217
+ severity: 'INFO',
218
+ body: 'Message 2',
219
+ },
220
+ ];
221
+ const result = await queryLogs({
222
+ backend: 'local',
223
+ limit: 1,
224
+ }, getMockOptions());
225
+ assert.ok(result);
226
+ assert.strictEqual(typeof result.count, 'number');
227
+ });
228
+ it('should return result with backend, count, and logs', async () => {
229
+ mockLocalLogs = [
230
+ {
231
+ timestamp: '2026-01-28T12:00:00Z',
232
+ severity: 'INFO',
233
+ body: 'Test log',
234
+ },
235
+ ];
236
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
237
+ assert.ok(result.backend);
238
+ assert.ok(typeof result.count === 'number');
239
+ assert.ok(Array.isArray(result.logs));
240
+ });
241
+ it('should group logs by severity', async () => {
242
+ mockLocalLogs = [
243
+ {
244
+ timestamp: '2026-01-28T12:00:00Z',
245
+ severity: 'ERROR',
246
+ body: 'Error 1',
247
+ },
248
+ {
249
+ timestamp: '2026-01-28T12:01:00Z',
250
+ severity: 'ERROR',
251
+ body: 'Error 2',
252
+ },
253
+ {
254
+ timestamp: '2026-01-28T12:02:00Z',
255
+ severity: 'INFO',
256
+ body: 'Info message',
257
+ },
258
+ ];
259
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
260
+ assert.ok(result.bySeverity);
261
+ assert.strictEqual(typeof result.bySeverity, 'object');
262
+ assert.ok(Object.keys(result.bySeverity).length > 0);
263
+ });
264
+ it('should correctly count logs per severity', async () => {
265
+ mockLocalLogs = [
266
+ {
267
+ timestamp: '2026-01-28T12:00:00Z',
268
+ severity: 'ERROR',
269
+ body: 'Error 1',
270
+ },
271
+ {
272
+ timestamp: '2026-01-28T12:01:00Z',
273
+ severity: 'ERROR',
274
+ body: 'Error 2',
275
+ },
276
+ {
277
+ timestamp: '2026-01-28T12:02:00Z',
278
+ severity: 'WARN',
279
+ body: 'Warning',
280
+ },
281
+ ];
282
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
283
+ assert.ok(result.bySeverity);
284
+ assert.strictEqual(result.bySeverity.ERROR, 2);
285
+ assert.strictEqual(result.bySeverity.WARN, 1);
286
+ });
287
+ it('should truncate long log body to 500 chars', async () => {
288
+ const longBody = 'x'.repeat(600);
289
+ mockLocalLogs = [
290
+ {
291
+ timestamp: '2026-01-28T12:00:00Z',
292
+ severity: 'ERROR',
293
+ body: longBody,
294
+ },
295
+ ];
296
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
297
+ assert.ok(result.logs.length > 0);
298
+ assert.ok(result.logs[0].body.length <= 503); // 500 + '...'
299
+ assert.ok(result.logs[0].body.endsWith('...'));
300
+ });
301
+ it('should not truncate short log body', async () => {
302
+ const shortBody = 'Short message';
303
+ mockLocalLogs = [
304
+ {
305
+ timestamp: '2026-01-28T12:00:00Z',
306
+ severity: 'INFO',
307
+ body: shortBody,
308
+ },
309
+ ];
310
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
311
+ assert.ok(result.logs.length > 0);
312
+ assert.strictEqual(result.logs[0].body, shortBody);
313
+ });
314
+ it('should preserve log metadata in response', async () => {
315
+ mockLocalLogs = [
316
+ {
317
+ timestamp: '2026-01-28T12:00:00Z',
318
+ severity: 'ERROR',
319
+ body: 'Test error',
320
+ traceId: 'trace-123',
321
+ spanId: 'span-456',
322
+ attributes: { key: 'value' },
323
+ },
324
+ ];
325
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
326
+ assert.ok(result.logs.length > 0);
327
+ const log = result.logs[0];
328
+ assert.strictEqual(log.timestamp, '2026-01-28T12:00:00Z');
329
+ assert.strictEqual(log.severity, 'ERROR');
330
+ assert.strictEqual(log.traceId, 'trace-123');
331
+ assert.deepStrictEqual(log.attributes, { key: 'value' });
332
+ });
333
+ it('should return empty result when no logs match', async () => {
334
+ mockLocalLogs = [];
335
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
336
+ assert.strictEqual(result.backend, 'local');
337
+ assert.strictEqual(result.count, 0);
338
+ assert.strictEqual(result.logs.length, 0);
339
+ });
340
+ });
341
+ // Test auto backend selection
342
+ describe('auto backend selection', () => {
343
+ beforeEach(() => {
344
+ mockLocalLogs = [];
345
+ mockSignozLogs = [];
346
+ process.env.SIGNOZ_URL = '';
347
+ process.env.SIGNOZ_API_KEY = '';
348
+ });
349
+ afterEach(() => {
350
+ process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
351
+ process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
352
+ });
353
+ it('should use local backend when signoz not configured', async () => {
354
+ mockLocalLogs = [
355
+ {
356
+ timestamp: '2026-01-28T12:00:00Z',
357
+ severity: 'INFO',
358
+ body: 'Test message',
359
+ },
360
+ ];
361
+ const result = await queryLogs({ backend: 'auto' }, getMockOptions());
362
+ assert.strictEqual(result.backend, 'local');
363
+ });
364
+ it('should use signoz backend when configured and available', async () => {
365
+ // Note: Constants are read at import time, so we can only test
366
+ // that the auto backend selection falls back to local when
367
+ // signoz is not configured via constants
368
+ mockLocalLogs = [
369
+ {
370
+ timestamp: '2026-01-28T12:00:00Z',
371
+ severity: 'ERROR',
372
+ body: 'Local error',
373
+ },
374
+ ];
375
+ const result = await queryLogs({ backend: 'auto' }, getMockOptions());
376
+ assert.ok(result.backend);
377
+ });
378
+ it('should fallback to signoz when local returns empty and signoz configured', async () => {
379
+ mockLocalLogs = [];
380
+ mockSignozLogs = [
381
+ {
382
+ timestamp: '2026-01-28T12:00:00Z',
383
+ severity: 'ERROR',
384
+ body: 'Remote error',
385
+ },
386
+ ];
387
+ const result = await queryLogs({ backend: 'auto' }, getMockOptions());
388
+ assert.ok(result.backend);
389
+ assert.ok(typeof result.count === 'number');
390
+ });
391
+ });
392
+ // Test tool export
393
+ describe('queryLogsTool export', () => {
394
+ it('should have correct tool name', () => {
395
+ assert.strictEqual(queryLogsTool.name, 'obs_query_logs');
396
+ });
397
+ it('should have description', () => {
398
+ assert.ok(queryLogsTool.description);
399
+ assert.strictEqual(typeof queryLogsTool.description, 'string');
400
+ assert.ok(queryLogsTool.description.length > 0);
401
+ });
402
+ it('should have inputSchema', () => {
403
+ assert.ok(queryLogsTool.inputSchema);
404
+ });
405
+ it('should have handler function', () => {
406
+ assert.strictEqual(typeof queryLogsTool.handler, 'function');
407
+ });
408
+ it('should have handler equal to queryLogs function', () => {
409
+ assert.strictEqual(queryLogsTool.handler, queryLogs);
410
+ });
411
+ it('should accept valid input in handler', async () => {
412
+ mockLocalLogs = [];
413
+ const result = await queryLogsTool.handler({ backend: 'local' }, getMockOptions());
414
+ assert.ok(result);
415
+ assert.ok(result.backend);
416
+ assert.ok(typeof result.count === 'number');
417
+ });
418
+ });
419
+ // Test error handling and edge cases
420
+ describe('error handling and edge cases', () => {
421
+ beforeEach(() => {
422
+ mockLocalLogs = [];
423
+ mockSignozLogs = [];
424
+ process.env.SIGNOZ_URL = '';
425
+ process.env.SIGNOZ_API_KEY = '';
426
+ });
427
+ afterEach(() => {
428
+ process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
429
+ process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
430
+ });
431
+ it('should handle missing optional parameters', async () => {
432
+ mockLocalLogs = [];
433
+ const result = await queryLogs({
434
+ backend: 'local',
435
+ // No optional parameters
436
+ }, getMockOptions());
437
+ assert.ok(result);
438
+ assert.strictEqual(result.backend, 'local');
439
+ });
440
+ it('should handle combined filters (severity + search + traceId)', async () => {
441
+ mockLocalLogs = [
442
+ {
443
+ timestamp: '2026-01-28T12:00:00Z',
444
+ severity: 'ERROR',
445
+ body: 'Database connection error',
446
+ traceId: 'trace-123',
447
+ },
448
+ ];
449
+ const result = await queryLogs({
450
+ backend: 'local',
451
+ severity: 'ERROR',
452
+ search: 'connection',
453
+ traceId: 'trace-123',
454
+ }, getMockOptions());
455
+ assert.ok(result);
456
+ assert.strictEqual(typeof result.count, 'number');
457
+ });
458
+ it('should handle date range with both start and end dates', async () => {
459
+ mockLocalLogs = [
460
+ {
461
+ timestamp: '2026-01-15T12:00:00Z',
462
+ severity: 'INFO',
463
+ body: 'Message',
464
+ },
465
+ ];
466
+ const result = await queryLogs({
467
+ backend: 'local',
468
+ startDate: '2026-01-01',
469
+ endDate: '2026-01-31',
470
+ }, getMockOptions());
471
+ assert.ok(result);
472
+ });
473
+ it('should handle date range with only start date', async () => {
474
+ mockLocalLogs = [];
475
+ const result = await queryLogs({
476
+ backend: 'local',
477
+ startDate: '2026-01-01',
478
+ }, getMockOptions());
479
+ assert.ok(result);
480
+ });
481
+ it('should handle date range with only end date', async () => {
482
+ mockLocalLogs = [];
483
+ const result = await queryLogs({
484
+ backend: 'local',
485
+ endDate: '2026-01-31',
486
+ }, getMockOptions());
487
+ assert.ok(result);
488
+ });
489
+ it('should handle large limit values', async () => {
490
+ mockLocalLogs = [];
491
+ const result = await queryLogs({
492
+ backend: 'local',
493
+ limit: 10000,
494
+ }, getMockOptions());
495
+ assert.ok(result);
496
+ assert.strictEqual(typeof result.count, 'number');
497
+ });
498
+ it('should handle very small limit values', async () => {
499
+ mockLocalLogs = [
500
+ {
501
+ timestamp: '2026-01-28T12:00:00Z',
502
+ severity: 'INFO',
503
+ body: 'Message',
504
+ },
505
+ ];
506
+ const result = await queryLogs({
507
+ backend: 'local',
508
+ limit: 1,
509
+ }, getMockOptions());
510
+ assert.ok(result);
511
+ });
512
+ it('should handle logs with missing optional fields', async () => {
513
+ mockLocalLogs = [
514
+ {
515
+ timestamp: '2026-01-28T12:00:00Z',
516
+ severity: 'INFO',
517
+ body: 'Message',
518
+ // No traceId, spanId, or attributes
519
+ },
520
+ ];
521
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
522
+ assert.ok(result);
523
+ assert.ok(result.logs.length > 0);
524
+ });
525
+ it('should handle logs with empty attributes', async () => {
526
+ mockLocalLogs = [
527
+ {
528
+ timestamp: '2026-01-28T12:00:00Z',
529
+ severity: 'INFO',
530
+ body: 'Message',
531
+ attributes: {},
532
+ },
533
+ ];
534
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
535
+ assert.ok(result);
536
+ });
537
+ it('should handle result with no backend available', async () => {
538
+ mockLocalLogs = [];
539
+ mockSignozLogs = [];
540
+ // Don't configure any backend
541
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
542
+ assert.ok(result);
543
+ assert.strictEqual(typeof result.count, 'number');
544
+ });
545
+ });
546
+ // Test severity filtering variations
547
+ describe('severity filtering', () => {
548
+ beforeEach(() => {
549
+ mockLocalLogs = [];
550
+ process.env.SIGNOZ_URL = '';
551
+ process.env.SIGNOZ_API_KEY = '';
552
+ });
553
+ afterEach(() => {
554
+ process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
555
+ process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
556
+ });
557
+ it('should filter by ERROR severity', async () => {
558
+ mockLocalLogs = [
559
+ {
560
+ timestamp: '2026-01-28T12:00:00Z',
561
+ severity: 'ERROR',
562
+ body: 'Error 1',
563
+ },
564
+ {
565
+ timestamp: '2026-01-28T12:01:00Z',
566
+ severity: 'INFO',
567
+ body: 'Info',
568
+ },
569
+ ];
570
+ const result = await queryLogs({
571
+ backend: 'local',
572
+ severity: 'ERROR',
573
+ }, getMockOptions());
574
+ assert.ok(result);
575
+ });
576
+ it('should filter by WARN severity', async () => {
577
+ const result = await queryLogs({
578
+ backend: 'local',
579
+ severity: 'WARN',
580
+ }, getMockOptions());
581
+ assert.ok(result);
582
+ });
583
+ it('should filter by INFO severity', async () => {
584
+ const result = await queryLogs({
585
+ backend: 'local',
586
+ severity: 'INFO',
587
+ }, getMockOptions());
588
+ assert.ok(result);
589
+ });
590
+ it('should filter by DEBUG severity', async () => {
591
+ const result = await queryLogs({
592
+ backend: 'local',
593
+ severity: 'DEBUG',
594
+ }, getMockOptions());
595
+ assert.ok(result);
596
+ });
597
+ });
598
+ // Test response structure
599
+ describe('response structure', () => {
600
+ beforeEach(() => {
601
+ mockLocalLogs = [];
602
+ process.env.SIGNOZ_URL = '';
603
+ process.env.SIGNOZ_API_KEY = '';
604
+ });
605
+ afterEach(() => {
606
+ process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
607
+ process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
608
+ });
609
+ it('should have backend property in response', async () => {
610
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
611
+ assert.ok(result.backend);
612
+ });
613
+ it('should have count property in response', async () => {
614
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
615
+ assert.ok(typeof result.count === 'number');
616
+ });
617
+ it('should have logs array in response', async () => {
618
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
619
+ assert.ok(Array.isArray(result.logs));
620
+ });
621
+ it('should have bySeverity object in response', async () => {
622
+ mockLocalLogs = [
623
+ {
624
+ timestamp: '2026-01-28T12:00:00Z',
625
+ severity: 'ERROR',
626
+ body: 'Error',
627
+ },
628
+ ];
629
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
630
+ assert.ok(result.bySeverity);
631
+ assert.strictEqual(typeof result.bySeverity, 'object');
632
+ });
633
+ it('should have correct structure for individual log entries', async () => {
634
+ mockLocalLogs = [
635
+ {
636
+ timestamp: '2026-01-28T12:00:00Z',
637
+ severity: 'ERROR',
638
+ body: 'Error message',
639
+ traceId: 'trace-123',
640
+ attributes: { key: 'value' },
641
+ },
642
+ ];
643
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
644
+ assert.ok(result.logs.length > 0);
645
+ const log = result.logs[0];
646
+ assert.ok(log.timestamp);
647
+ assert.ok(log.severity);
648
+ assert.ok(log.body);
649
+ });
650
+ it('should include optional log fields when present', async () => {
651
+ mockLocalLogs = [
652
+ {
653
+ timestamp: '2026-01-28T12:00:00Z',
654
+ severity: 'INFO',
655
+ body: 'Message',
656
+ traceId: 'trace-123',
657
+ spanId: 'span-456',
658
+ attributes: { service: 'api' },
659
+ },
660
+ ];
661
+ const result = await queryLogs({ backend: 'local' }, getMockOptions());
662
+ const log = result.logs[0];
663
+ assert.ok(log.traceId);
664
+ assert.ok(log.attributes);
665
+ });
666
+ });
667
+ });
668
+ //# sourceMappingURL=query-logs.test.js.map