observability-toolkit 1.1.0 → 1.4.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.
- package/README.md +52 -3
- package/dist/backends/index.d.ts +28 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/local-jsonl.d.ts +29 -1
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +259 -27
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/local-jsonl.test.d.ts +2 -0
- package/dist/backends/local-jsonl.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl.test.js +1638 -0
- package/dist/backends/local-jsonl.test.js.map +1 -0
- package/dist/backends/signoz-api.d.ts +9 -2
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.integration.test.d.ts +8 -0
- package/dist/backends/signoz-api.integration.test.d.ts.map +1 -0
- package/dist/backends/signoz-api.integration.test.js +137 -0
- package/dist/backends/signoz-api.integration.test.js.map +1 -0
- package/dist/backends/signoz-api.js +206 -115
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.d.ts +2 -0
- package/dist/backends/signoz-api.test.d.ts.map +1 -0
- package/dist/backends/signoz-api.test.js +1080 -0
- package/dist/backends/signoz-api.test.js.map +1 -0
- package/dist/lib/constants.d.ts +28 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +73 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.d.ts +5 -0
- package/dist/lib/constants.test.d.ts.map +1 -0
- package/dist/lib/constants.test.js +381 -0
- package/dist/lib/constants.test.js.map +1 -0
- package/dist/lib/file-utils.d.ts +53 -1
- package/dist/lib/file-utils.d.ts.map +1 -1
- package/dist/lib/file-utils.js +142 -3
- package/dist/lib/file-utils.js.map +1 -1
- package/dist/lib/file-utils.test.d.ts +2 -0
- package/dist/lib/file-utils.test.d.ts.map +1 -0
- package/dist/lib/file-utils.test.js +649 -0
- package/dist/lib/file-utils.test.js.map +1 -0
- package/dist/server.js +50 -63
- package/dist/server.js.map +1 -1
- package/dist/server.test.d.ts +5 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +547 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/context-stats.d.ts +2 -2
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +2 -1
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.d.ts +5 -0
- package/dist/tools/context-stats.test.d.ts.map +1 -0
- package/dist/tools/context-stats.test.js +465 -0
- package/dist/tools/context-stats.test.js.map +1 -0
- package/dist/tools/get-trace-url.d.ts.map +1 -1
- package/dist/tools/get-trace-url.js +5 -1
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/get-trace-url.test.d.ts +5 -0
- package/dist/tools/get-trace-url.test.d.ts.map +1 -0
- package/dist/tools/get-trace-url.test.js +429 -0
- package/dist/tools/get-trace-url.test.js.map +1 -0
- package/dist/tools/health-check.d.ts +9 -2
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +66 -27
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/health-check.test.d.ts +5 -0
- package/dist/tools/health-check.test.d.ts.map +1 -0
- package/dist/tools/health-check.test.js +386 -0
- package/dist/tools/health-check.test.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/query-llm-events.d.ts +82 -0
- package/dist/tools/query-llm-events.d.ts.map +1 -0
- package/dist/tools/query-llm-events.js +60 -0
- package/dist/tools/query-llm-events.js.map +1 -0
- package/dist/tools/query-llm-events.test.d.ts +5 -0
- package/dist/tools/query-llm-events.test.d.ts.map +1 -0
- package/dist/tools/query-llm-events.test.js +111 -0
- package/dist/tools/query-llm-events.test.js.map +1 -0
- package/dist/tools/query-logs.d.ts +15 -8
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +11 -10
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.d.ts +5 -0
- package/dist/tools/query-logs.test.d.ts.map +1 -0
- package/dist/tools/query-logs.test.js +688 -0
- package/dist/tools/query-logs.test.js.map +1 -0
- package/dist/tools/query-metrics.d.ts +13 -15
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +12 -13
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.d.ts +5 -0
- package/dist/tools/query-metrics.test.d.ts.map +1 -0
- package/dist/tools/query-metrics.test.js +597 -0
- package/dist/tools/query-metrics.test.js.map +1 -0
- package/dist/tools/query-traces.d.ts +19 -14
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +14 -14
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.d.ts +5 -0
- package/dist/tools/query-traces.test.d.ts.map +1 -0
- package/dist/tools/query-traces.test.js +643 -0
- package/dist/tools/query-traces.test.js.map +1 -0
- package/dist/tools/setup-claudeignore.d.ts +36 -10
- package/dist/tools/setup-claudeignore.d.ts.map +1 -1
- package/dist/tools/setup-claudeignore.js +193 -33
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.d.ts +2 -0
- package/dist/tools/setup-claudeignore.test.d.ts.map +1 -0
- package/dist/tools/setup-claudeignore.test.js +481 -0
- package/dist/tools/setup-claudeignore.test.js.map +1 -0
- package/dist/tools/signoz.integration.test.d.ts +8 -0
- package/dist/tools/signoz.integration.test.d.ts.map +1 -0
- package/dist/tools/signoz.integration.test.js +141 -0
- package/dist/tools/signoz.integration.test.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for query-traces.ts
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
5
|
+
import * as assert from 'node:assert';
|
|
6
|
+
import { queryTraces, queryTracesSchema, queryTracesTool } from './query-traces.js';
|
|
7
|
+
// Mock data storage
|
|
8
|
+
let mockLocalTraces = [];
|
|
9
|
+
let mockSignozTraces = [];
|
|
10
|
+
// Create mock backends using the TelemetryBackend interface
|
|
11
|
+
function createMockLocalBackend() {
|
|
12
|
+
return {
|
|
13
|
+
name: 'mock-local',
|
|
14
|
+
queryTraces: async () => mockLocalTraces,
|
|
15
|
+
queryLogs: async () => [],
|
|
16
|
+
queryMetrics: async () => [],
|
|
17
|
+
healthCheck: async () => ({ status: 'ok' }),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createMockSignozBackend() {
|
|
21
|
+
return {
|
|
22
|
+
name: 'mock-signoz',
|
|
23
|
+
queryTraces: async () => mockSignozTraces,
|
|
24
|
+
queryLogs: 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('queryTraces', () => {
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
// Reset mock data before each test
|
|
44
|
+
mockLocalTraces = [];
|
|
45
|
+
mockSignozTraces = [];
|
|
46
|
+
});
|
|
47
|
+
describe('schema validation', () => {
|
|
48
|
+
it('should validate valid input', () => {
|
|
49
|
+
const input = {
|
|
50
|
+
backend: 'local',
|
|
51
|
+
traceId: 'trace-123',
|
|
52
|
+
limit: 50,
|
|
53
|
+
};
|
|
54
|
+
const result = queryTracesSchema.safeParse(input);
|
|
55
|
+
assert.strictEqual(result.success, true);
|
|
56
|
+
});
|
|
57
|
+
it('should use auto backend by default', () => {
|
|
58
|
+
const input = { limit: 10 };
|
|
59
|
+
const result = queryTracesSchema.safeParse(input);
|
|
60
|
+
assert.strictEqual(result.success, true);
|
|
61
|
+
if (result.success) {
|
|
62
|
+
assert.strictEqual(result.data.backend, 'auto');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
it('should use 50 as default limit', () => {
|
|
66
|
+
const input = { backend: 'local' };
|
|
67
|
+
const result = queryTracesSchema.safeParse(input);
|
|
68
|
+
assert.strictEqual(result.success, true);
|
|
69
|
+
if (result.success) {
|
|
70
|
+
assert.strictEqual(result.data.limit, 50);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it('should reject invalid backend', () => {
|
|
74
|
+
const input = { backend: 'invalid' };
|
|
75
|
+
const result = queryTracesSchema.safeParse(input);
|
|
76
|
+
assert.strictEqual(result.success, false);
|
|
77
|
+
});
|
|
78
|
+
it('should accept all optional filter parameters', () => {
|
|
79
|
+
const input = {
|
|
80
|
+
backend: 'signoz',
|
|
81
|
+
traceId: 'trace-123',
|
|
82
|
+
serviceName: 'my-service',
|
|
83
|
+
spanName: 'handler',
|
|
84
|
+
minDurationMs: 10,
|
|
85
|
+
maxDurationMs: 5000,
|
|
86
|
+
startDate: '2026-01-01',
|
|
87
|
+
endDate: '2026-01-31',
|
|
88
|
+
limit: 100,
|
|
89
|
+
};
|
|
90
|
+
const result = queryTracesSchema.safeParse(input);
|
|
91
|
+
assert.strictEqual(result.success, true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('local backend', () => {
|
|
95
|
+
it('should query local backend when backend=local', async () => {
|
|
96
|
+
// We need to test the actual behavior without modifying the source
|
|
97
|
+
// This test verifies the schema is correct for local backend selection
|
|
98
|
+
const input = {
|
|
99
|
+
backend: 'local',
|
|
100
|
+
limit: 50,
|
|
101
|
+
};
|
|
102
|
+
const result = queryTracesSchema.safeParse(input);
|
|
103
|
+
assert.strictEqual(result.success, true);
|
|
104
|
+
if (result.success) {
|
|
105
|
+
assert.strictEqual(result.data.backend, 'local');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
it('should filter by traceId from local backend', async () => {
|
|
109
|
+
const input = {
|
|
110
|
+
backend: 'local',
|
|
111
|
+
traceId: 'trace-001',
|
|
112
|
+
limit: 50,
|
|
113
|
+
};
|
|
114
|
+
const result = queryTracesSchema.safeParse(input);
|
|
115
|
+
assert.strictEqual(result.success, true);
|
|
116
|
+
if (result.success) {
|
|
117
|
+
assert.strictEqual(result.data.traceId, 'trace-001');
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
it('should filter by serviceName from local backend', async () => {
|
|
121
|
+
const input = {
|
|
122
|
+
backend: 'local',
|
|
123
|
+
serviceName: 'my-service',
|
|
124
|
+
limit: 50,
|
|
125
|
+
};
|
|
126
|
+
const result = queryTracesSchema.safeParse(input);
|
|
127
|
+
assert.strictEqual(result.success, true);
|
|
128
|
+
if (result.success) {
|
|
129
|
+
assert.strictEqual(result.data.serviceName, 'my-service');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it('should filter by spanName from local backend', async () => {
|
|
133
|
+
const input = {
|
|
134
|
+
backend: 'local',
|
|
135
|
+
spanName: 'http-request',
|
|
136
|
+
limit: 50,
|
|
137
|
+
};
|
|
138
|
+
const result = queryTracesSchema.safeParse(input);
|
|
139
|
+
assert.strictEqual(result.success, true);
|
|
140
|
+
if (result.success) {
|
|
141
|
+
assert.strictEqual(result.data.spanName, 'http-request');
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
it('should filter by minDurationMs from local backend', async () => {
|
|
145
|
+
const input = {
|
|
146
|
+
backend: 'local',
|
|
147
|
+
minDurationMs: 100,
|
|
148
|
+
limit: 50,
|
|
149
|
+
};
|
|
150
|
+
const result = queryTracesSchema.safeParse(input);
|
|
151
|
+
assert.strictEqual(result.success, true);
|
|
152
|
+
if (result.success) {
|
|
153
|
+
assert.strictEqual(result.data.minDurationMs, 100);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
it('should filter by maxDurationMs from local backend', async () => {
|
|
157
|
+
const input = {
|
|
158
|
+
backend: 'local',
|
|
159
|
+
maxDurationMs: 5000,
|
|
160
|
+
limit: 50,
|
|
161
|
+
};
|
|
162
|
+
const result = queryTracesSchema.safeParse(input);
|
|
163
|
+
assert.strictEqual(result.success, true);
|
|
164
|
+
if (result.success) {
|
|
165
|
+
assert.strictEqual(result.data.maxDurationMs, 5000);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
it('should filter by date range from local backend', async () => {
|
|
169
|
+
const input = {
|
|
170
|
+
backend: 'local',
|
|
171
|
+
startDate: '2026-01-20',
|
|
172
|
+
endDate: '2026-01-28',
|
|
173
|
+
limit: 50,
|
|
174
|
+
};
|
|
175
|
+
const result = queryTracesSchema.safeParse(input);
|
|
176
|
+
assert.strictEqual(result.success, true);
|
|
177
|
+
if (result.success) {
|
|
178
|
+
assert.strictEqual(result.data.startDate, '2026-01-20');
|
|
179
|
+
assert.strictEqual(result.data.endDate, '2026-01-28');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('SigNoz backend', () => {
|
|
184
|
+
it('should accept signoz backend', async () => {
|
|
185
|
+
const input = {
|
|
186
|
+
backend: 'signoz',
|
|
187
|
+
limit: 50,
|
|
188
|
+
};
|
|
189
|
+
const result = queryTracesSchema.safeParse(input);
|
|
190
|
+
assert.strictEqual(result.success, true);
|
|
191
|
+
if (result.success) {
|
|
192
|
+
assert.strictEqual(result.data.backend, 'signoz');
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
it('should filter by traceId from SigNoz backend', async () => {
|
|
196
|
+
const input = {
|
|
197
|
+
backend: 'signoz',
|
|
198
|
+
traceId: 'trace-999',
|
|
199
|
+
limit: 50,
|
|
200
|
+
};
|
|
201
|
+
const result = queryTracesSchema.safeParse(input);
|
|
202
|
+
assert.strictEqual(result.success, true);
|
|
203
|
+
if (result.success) {
|
|
204
|
+
assert.strictEqual(result.data.traceId, 'trace-999');
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
it('should filter by serviceName from SigNoz backend', async () => {
|
|
208
|
+
const input = {
|
|
209
|
+
backend: 'signoz',
|
|
210
|
+
serviceName: 'api-service',
|
|
211
|
+
limit: 50,
|
|
212
|
+
};
|
|
213
|
+
const result = queryTracesSchema.safeParse(input);
|
|
214
|
+
assert.strictEqual(result.success, true);
|
|
215
|
+
if (result.success) {
|
|
216
|
+
assert.strictEqual(result.data.serviceName, 'api-service');
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
it('should filter by spanName from SigNoz backend', async () => {
|
|
220
|
+
const input = {
|
|
221
|
+
backend: 'signoz',
|
|
222
|
+
spanName: 'database-query',
|
|
223
|
+
limit: 50,
|
|
224
|
+
};
|
|
225
|
+
const result = queryTracesSchema.safeParse(input);
|
|
226
|
+
assert.strictEqual(result.success, true);
|
|
227
|
+
if (result.success) {
|
|
228
|
+
assert.strictEqual(result.data.spanName, 'database-query');
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
it('should filter by minDurationMs from SigNoz backend', async () => {
|
|
232
|
+
const input = {
|
|
233
|
+
backend: 'signoz',
|
|
234
|
+
minDurationMs: 500,
|
|
235
|
+
limit: 50,
|
|
236
|
+
};
|
|
237
|
+
const result = queryTracesSchema.safeParse(input);
|
|
238
|
+
assert.strictEqual(result.success, true);
|
|
239
|
+
if (result.success) {
|
|
240
|
+
assert.strictEqual(result.data.minDurationMs, 500);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
it('should filter by date range from SigNoz backend', async () => {
|
|
244
|
+
const input = {
|
|
245
|
+
backend: 'signoz',
|
|
246
|
+
startDate: '2026-01-15',
|
|
247
|
+
endDate: '2026-01-28',
|
|
248
|
+
limit: 50,
|
|
249
|
+
};
|
|
250
|
+
const result = queryTracesSchema.safeParse(input);
|
|
251
|
+
assert.strictEqual(result.success, true);
|
|
252
|
+
if (result.success) {
|
|
253
|
+
assert.strictEqual(result.data.startDate, '2026-01-15');
|
|
254
|
+
assert.strictEqual(result.data.endDate, '2026-01-28');
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
describe('auto backend selection', () => {
|
|
259
|
+
beforeEach(() => {
|
|
260
|
+
mockLocalTraces = [];
|
|
261
|
+
mockSignozTraces = [];
|
|
262
|
+
process.env.SIGNOZ_URL = '';
|
|
263
|
+
process.env.SIGNOZ_API_KEY = '';
|
|
264
|
+
});
|
|
265
|
+
afterEach(() => {
|
|
266
|
+
process.env.SIGNOZ_URL = originalEnv.SIGNOZ_URL;
|
|
267
|
+
process.env.SIGNOZ_API_KEY = originalEnv.SIGNOZ_API_KEY;
|
|
268
|
+
});
|
|
269
|
+
it('should accept auto backend schema', async () => {
|
|
270
|
+
const input = {
|
|
271
|
+
backend: 'auto',
|
|
272
|
+
limit: 50,
|
|
273
|
+
};
|
|
274
|
+
const result = queryTracesSchema.safeParse(input);
|
|
275
|
+
assert.strictEqual(result.success, true);
|
|
276
|
+
if (result.success) {
|
|
277
|
+
assert.strictEqual(result.data.backend, 'auto');
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
it('should be the default backend', async () => {
|
|
281
|
+
const input = { limit: 50 };
|
|
282
|
+
const result = queryTracesSchema.safeParse(input);
|
|
283
|
+
assert.strictEqual(result.success, true);
|
|
284
|
+
if (result.success) {
|
|
285
|
+
assert.strictEqual(result.data.backend, 'auto');
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
it('should use local backend when signoz not configured', async () => {
|
|
289
|
+
mockLocalTraces = [
|
|
290
|
+
{
|
|
291
|
+
traceId: 'trace-local-001',
|
|
292
|
+
spanId: 'span-001',
|
|
293
|
+
name: 'local-operation',
|
|
294
|
+
kind: 'INTERNAL',
|
|
295
|
+
durationMs: 10,
|
|
296
|
+
status: { code: 0 },
|
|
297
|
+
attributes: { 'service.name': 'test-service' },
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
const result = await queryTraces({ backend: 'auto' }, getMockOptions());
|
|
301
|
+
assert.strictEqual(result.backend, 'local');
|
|
302
|
+
assert.strictEqual(result.count, 1);
|
|
303
|
+
});
|
|
304
|
+
it('should return local traces when available', async () => {
|
|
305
|
+
mockLocalTraces = [
|
|
306
|
+
{
|
|
307
|
+
traceId: 'trace-local-002',
|
|
308
|
+
spanId: 'span-002',
|
|
309
|
+
name: 'test-op',
|
|
310
|
+
kind: 'SERVER',
|
|
311
|
+
durationMs: 20,
|
|
312
|
+
status: { code: 0 },
|
|
313
|
+
attributes: {},
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
const result = await queryTraces({ backend: 'auto' }, getMockOptions());
|
|
317
|
+
assert.strictEqual(result.backend, 'local');
|
|
318
|
+
assert.strictEqual(result.traces.length, 1);
|
|
319
|
+
assert.strictEqual(result.traces[0].traceId, 'trace-local-002');
|
|
320
|
+
});
|
|
321
|
+
it('should return empty result with local backend when no traces found', async () => {
|
|
322
|
+
mockLocalTraces = [];
|
|
323
|
+
const result = await queryTraces({ backend: 'auto' }, getMockOptions());
|
|
324
|
+
assert.ok(result.backend);
|
|
325
|
+
assert.strictEqual(result.count, 0);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
describe('limit parameter', () => {
|
|
329
|
+
it('should accept custom limit', async () => {
|
|
330
|
+
const input = {
|
|
331
|
+
backend: 'local',
|
|
332
|
+
limit: 100,
|
|
333
|
+
};
|
|
334
|
+
const result = queryTracesSchema.safeParse(input);
|
|
335
|
+
assert.strictEqual(result.success, true);
|
|
336
|
+
if (result.success) {
|
|
337
|
+
assert.strictEqual(result.data.limit, 100);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
it('should accept limit of 1', async () => {
|
|
341
|
+
const input = {
|
|
342
|
+
backend: 'local',
|
|
343
|
+
limit: 1,
|
|
344
|
+
};
|
|
345
|
+
const result = queryTracesSchema.safeParse(input);
|
|
346
|
+
assert.strictEqual(result.success, true);
|
|
347
|
+
if (result.success) {
|
|
348
|
+
assert.strictEqual(result.data.limit, 1);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
it('should accept max limit value of 1000', async () => {
|
|
352
|
+
const input = {
|
|
353
|
+
backend: 'local',
|
|
354
|
+
limit: 1000,
|
|
355
|
+
};
|
|
356
|
+
const result = queryTracesSchema.safeParse(input);
|
|
357
|
+
assert.strictEqual(result.success, true);
|
|
358
|
+
if (result.success) {
|
|
359
|
+
assert.strictEqual(result.data.limit, 1000);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
it('should reject limit values over 1000', async () => {
|
|
363
|
+
const input = {
|
|
364
|
+
backend: 'local',
|
|
365
|
+
limit: 1001,
|
|
366
|
+
};
|
|
367
|
+
const result = queryTracesSchema.safeParse(input);
|
|
368
|
+
assert.strictEqual(result.success, false);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
describe('attribute filter', () => {
|
|
372
|
+
it('should accept attributeFilter with string values', async () => {
|
|
373
|
+
const input = {
|
|
374
|
+
backend: 'local',
|
|
375
|
+
attributeFilter: { 'hook.name': 'session-start' },
|
|
376
|
+
limit: 50,
|
|
377
|
+
};
|
|
378
|
+
const result = queryTracesSchema.safeParse(input);
|
|
379
|
+
assert.strictEqual(result.success, true);
|
|
380
|
+
if (result.success) {
|
|
381
|
+
assert.deepStrictEqual(result.data.attributeFilter, { 'hook.name': 'session-start' });
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
it('should accept attributeFilter with number values', async () => {
|
|
385
|
+
const input = {
|
|
386
|
+
backend: 'local',
|
|
387
|
+
attributeFilter: { 'http.status_code': 200 },
|
|
388
|
+
limit: 50,
|
|
389
|
+
};
|
|
390
|
+
const result = queryTracesSchema.safeParse(input);
|
|
391
|
+
assert.strictEqual(result.success, true);
|
|
392
|
+
if (result.success) {
|
|
393
|
+
assert.deepStrictEqual(result.data.attributeFilter, { 'http.status_code': 200 });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
it('should accept attributeFilter with boolean values', async () => {
|
|
397
|
+
const input = {
|
|
398
|
+
backend: 'local',
|
|
399
|
+
attributeFilter: { 'mcp.success': true },
|
|
400
|
+
limit: 50,
|
|
401
|
+
};
|
|
402
|
+
const result = queryTracesSchema.safeParse(input);
|
|
403
|
+
assert.strictEqual(result.success, true);
|
|
404
|
+
if (result.success) {
|
|
405
|
+
assert.deepStrictEqual(result.data.attributeFilter, { 'mcp.success': true });
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
it('should accept attributeFilter with multiple attributes', async () => {
|
|
409
|
+
const input = {
|
|
410
|
+
backend: 'local',
|
|
411
|
+
attributeFilter: {
|
|
412
|
+
'hook.name': 'mcp-pre-tool',
|
|
413
|
+
'mcp.server': 'signoz',
|
|
414
|
+
'mcp.success': true,
|
|
415
|
+
},
|
|
416
|
+
limit: 50,
|
|
417
|
+
};
|
|
418
|
+
const result = queryTracesSchema.safeParse(input);
|
|
419
|
+
assert.strictEqual(result.success, true);
|
|
420
|
+
if (result.success) {
|
|
421
|
+
assert.strictEqual(result.data.attributeFilter?.['hook.name'], 'mcp-pre-tool');
|
|
422
|
+
assert.strictEqual(result.data.attributeFilter?.['mcp.server'], 'signoz');
|
|
423
|
+
assert.strictEqual(result.data.attributeFilter?.['mcp.success'], true);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
it('should accept attributeFilter combined with other filters', async () => {
|
|
427
|
+
const input = {
|
|
428
|
+
backend: 'local',
|
|
429
|
+
spanName: 'hook',
|
|
430
|
+
attributeFilter: { 'agent.type': 'Explore' },
|
|
431
|
+
minDurationMs: 100,
|
|
432
|
+
limit: 50,
|
|
433
|
+
};
|
|
434
|
+
const result = queryTracesSchema.safeParse(input);
|
|
435
|
+
assert.strictEqual(result.success, true);
|
|
436
|
+
if (result.success) {
|
|
437
|
+
assert.strictEqual(result.data.spanName, 'hook');
|
|
438
|
+
assert.deepStrictEqual(result.data.attributeFilter, { 'agent.type': 'Explore' });
|
|
439
|
+
assert.strictEqual(result.data.minDurationMs, 100);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
describe('combined filters', () => {
|
|
444
|
+
it('should accept multiple filters together', async () => {
|
|
445
|
+
const input = {
|
|
446
|
+
backend: 'local',
|
|
447
|
+
serviceName: 'auth-service',
|
|
448
|
+
spanName: 'login',
|
|
449
|
+
minDurationMs: 50,
|
|
450
|
+
maxDurationMs: 5000,
|
|
451
|
+
limit: 100,
|
|
452
|
+
};
|
|
453
|
+
const result = queryTracesSchema.safeParse(input);
|
|
454
|
+
assert.strictEqual(result.success, true);
|
|
455
|
+
if (result.success) {
|
|
456
|
+
assert.strictEqual(result.data.serviceName, 'auth-service');
|
|
457
|
+
assert.strictEqual(result.data.spanName, 'login');
|
|
458
|
+
assert.strictEqual(result.data.minDurationMs, 50);
|
|
459
|
+
assert.strictEqual(result.data.maxDurationMs, 5000);
|
|
460
|
+
assert.strictEqual(result.data.limit, 100);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
it('should accept filters with date range', async () => {
|
|
464
|
+
const input = {
|
|
465
|
+
backend: 'signoz',
|
|
466
|
+
serviceName: 'api',
|
|
467
|
+
startDate: '2026-01-20',
|
|
468
|
+
endDate: '2026-01-28',
|
|
469
|
+
minDurationMs: 10,
|
|
470
|
+
limit: 50,
|
|
471
|
+
};
|
|
472
|
+
const result = queryTracesSchema.safeParse(input);
|
|
473
|
+
assert.strictEqual(result.success, true);
|
|
474
|
+
if (result.success) {
|
|
475
|
+
assert.strictEqual(result.data.serviceName, 'api');
|
|
476
|
+
assert.strictEqual(result.data.startDate, '2026-01-20');
|
|
477
|
+
assert.strictEqual(result.data.endDate, '2026-01-28');
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
it('should accept traceId with other filters', async () => {
|
|
481
|
+
const input = {
|
|
482
|
+
backend: 'local',
|
|
483
|
+
traceId: 'trace-123',
|
|
484
|
+
spanName: 'operation',
|
|
485
|
+
limit: 50,
|
|
486
|
+
};
|
|
487
|
+
const result = queryTracesSchema.safeParse(input);
|
|
488
|
+
assert.strictEqual(result.success, true);
|
|
489
|
+
if (result.success) {
|
|
490
|
+
assert.strictEqual(result.data.traceId, 'trace-123');
|
|
491
|
+
assert.strictEqual(result.data.spanName, 'operation');
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
describe('tool definition', () => {
|
|
496
|
+
it('should have correct tool name', () => {
|
|
497
|
+
assert.strictEqual(queryTracesTool.name, 'obs_query_traces');
|
|
498
|
+
});
|
|
499
|
+
it('should have description', () => {
|
|
500
|
+
assert.ok(queryTracesTool.description);
|
|
501
|
+
assert.strictEqual(typeof queryTracesTool.description, 'string');
|
|
502
|
+
assert.ok(queryTracesTool.description.length > 0);
|
|
503
|
+
});
|
|
504
|
+
it('should have inputSchema', () => {
|
|
505
|
+
assert.ok(queryTracesTool.inputSchema);
|
|
506
|
+
assert.strictEqual(queryTracesTool.inputSchema, queryTracesSchema);
|
|
507
|
+
});
|
|
508
|
+
it('should have handler function', () => {
|
|
509
|
+
assert.ok(queryTracesTool.handler);
|
|
510
|
+
assert.strictEqual(typeof queryTracesTool.handler, 'function');
|
|
511
|
+
assert.strictEqual(queryTracesTool.handler, queryTraces);
|
|
512
|
+
});
|
|
513
|
+
it('should have description mentioning key features', () => {
|
|
514
|
+
const desc = queryTracesTool.description.toLowerCase();
|
|
515
|
+
assert.ok(desc.includes('trace'));
|
|
516
|
+
assert.ok(desc.includes('filter') || desc.includes('query'));
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
describe('error handling', () => {
|
|
520
|
+
it('should accept negative limit (schema does not restrict)', () => {
|
|
521
|
+
const input = {
|
|
522
|
+
backend: 'local',
|
|
523
|
+
limit: -1,
|
|
524
|
+
};
|
|
525
|
+
const result = queryTracesSchema.safeParse(input);
|
|
526
|
+
// Note: Zod number() by default allows negative numbers
|
|
527
|
+
// This documents current behavior - schema accepts it
|
|
528
|
+
assert.strictEqual(result.success, true);
|
|
529
|
+
});
|
|
530
|
+
it('should accept invalid date format (schema accepts any string)', () => {
|
|
531
|
+
const input = {
|
|
532
|
+
backend: 'local',
|
|
533
|
+
startDate: 'invalid-date',
|
|
534
|
+
limit: 50,
|
|
535
|
+
};
|
|
536
|
+
const result = queryTracesSchema.safeParse(input);
|
|
537
|
+
// Note: Schema accepts any string, doesn't validate date format
|
|
538
|
+
// This documents current validation scope
|
|
539
|
+
assert.strictEqual(result.success, true);
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
describe('handler function', () => {
|
|
543
|
+
it('should be exported', () => {
|
|
544
|
+
assert.ok(queryTraces);
|
|
545
|
+
assert.strictEqual(typeof queryTraces, 'function');
|
|
546
|
+
});
|
|
547
|
+
it('should accept QueryTracesInput', async () => {
|
|
548
|
+
const input = {
|
|
549
|
+
backend: 'local',
|
|
550
|
+
limit: 50,
|
|
551
|
+
};
|
|
552
|
+
// Verify input type is accepted by the function signature
|
|
553
|
+
assert.ok(input);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
describe('response structure validation', () => {
|
|
557
|
+
it('should have expected response fields for local backend', () => {
|
|
558
|
+
// Validate the expected response structure
|
|
559
|
+
const expectedFields = ['backend', 'count', 'traces'];
|
|
560
|
+
expectedFields.forEach(field => {
|
|
561
|
+
assert.ok(typeof field === 'string');
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
it('should have trace span fields in response', () => {
|
|
565
|
+
// Validate expected trace span fields
|
|
566
|
+
const expectedSpanFields = ['traceId', 'spanId', 'name', 'kind', 'durationMs', 'status', 'attributes'];
|
|
567
|
+
expectedSpanFields.forEach(field => {
|
|
568
|
+
assert.ok(typeof field === 'string');
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
describe('input options conversion', () => {
|
|
573
|
+
it('should convert input to query options correctly', async () => {
|
|
574
|
+
const input = {
|
|
575
|
+
backend: 'local',
|
|
576
|
+
traceId: 'trace-123',
|
|
577
|
+
serviceName: 'service',
|
|
578
|
+
spanName: 'span',
|
|
579
|
+
minDurationMs: 10,
|
|
580
|
+
maxDurationMs: 1000,
|
|
581
|
+
startDate: '2026-01-20',
|
|
582
|
+
endDate: '2026-01-28',
|
|
583
|
+
limit: 50,
|
|
584
|
+
};
|
|
585
|
+
// Validate all input fields are present
|
|
586
|
+
assert.strictEqual(input.traceId, 'trace-123');
|
|
587
|
+
assert.strictEqual(input.serviceName, 'service');
|
|
588
|
+
assert.strictEqual(input.spanName, 'span');
|
|
589
|
+
assert.strictEqual(input.minDurationMs, 10);
|
|
590
|
+
assert.strictEqual(input.maxDurationMs, 1000);
|
|
591
|
+
assert.strictEqual(input.startDate, '2026-01-20');
|
|
592
|
+
assert.strictEqual(input.endDate, '2026-01-28');
|
|
593
|
+
assert.strictEqual(input.limit, 50);
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
describe('signoz backend with traces', () => {
|
|
597
|
+
beforeEach(() => {
|
|
598
|
+
mockLocalTraces = [];
|
|
599
|
+
mockSignozTraces = [];
|
|
600
|
+
});
|
|
601
|
+
it('should return traces from signoz backend when backend=signoz', async () => {
|
|
602
|
+
mockSignozTraces = [
|
|
603
|
+
{
|
|
604
|
+
traceId: 'signoz-trace-001',
|
|
605
|
+
spanId: 'signoz-span-001',
|
|
606
|
+
name: 'signoz-operation',
|
|
607
|
+
kind: 'SERVER',
|
|
608
|
+
durationMs: 150,
|
|
609
|
+
status: { code: 0 },
|
|
610
|
+
attributes: { 'service.name': 'api-service' },
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
traceId: 'signoz-trace-002',
|
|
614
|
+
spanId: 'signoz-span-002',
|
|
615
|
+
name: 'db-query',
|
|
616
|
+
kind: 'CLIENT',
|
|
617
|
+
durationMs: 50,
|
|
618
|
+
status: { code: 0 },
|
|
619
|
+
attributes: { 'db.system': 'postgresql' },
|
|
620
|
+
},
|
|
621
|
+
];
|
|
622
|
+
const result = await queryTraces({ backend: 'signoz' }, getMockOptions());
|
|
623
|
+
assert.strictEqual(result.backend, 'signoz');
|
|
624
|
+
assert.strictEqual(result.count, 2);
|
|
625
|
+
assert.strictEqual(result.traces.length, 2);
|
|
626
|
+
assert.strictEqual(result.traces[0].traceId, 'signoz-trace-001');
|
|
627
|
+
assert.strictEqual(result.traces[0].spanId, 'signoz-span-001');
|
|
628
|
+
assert.strictEqual(result.traces[0].name, 'signoz-operation');
|
|
629
|
+
assert.strictEqual(result.traces[0].kind, 'SERVER');
|
|
630
|
+
assert.strictEqual(result.traces[0].durationMs, 150);
|
|
631
|
+
assert.deepStrictEqual(result.traces[0].status, { code: 0 });
|
|
632
|
+
assert.deepStrictEqual(result.traces[0].attributes, { 'service.name': 'api-service' });
|
|
633
|
+
});
|
|
634
|
+
it('should return empty traces from signoz backend', async () => {
|
|
635
|
+
mockSignozTraces = [];
|
|
636
|
+
const result = await queryTraces({ backend: 'signoz' }, getMockOptions());
|
|
637
|
+
assert.strictEqual(result.backend, 'signoz');
|
|
638
|
+
assert.strictEqual(result.count, 0);
|
|
639
|
+
assert.strictEqual(result.traces.length, 0);
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
//# sourceMappingURL=query-traces.test.js.map
|