Skip to content

Commit 2dcf2a6

Browse files
dhensbyclaude
andcommitted
test: verify instrumentation fires end-to-end
The existing diagnostics tests only exercised the helpers in isolation — they never drove a Request or Transaction through the real classes, so a regression in the tracePromise/publish call sites would pass silently. Stub the underlying driver callbacks (_query, _execute, _begin, etc.) and verify through real instances that: - request.query emits TRACE_QUERY start and asyncEnd with the command, parameter names, and requestId in context - request.query rejection propagates to TRACE_QUERY error with the original Error preserved on ctx.error - _internal requests are suppressed from TRACE_QUERY - request.execute populates procedure in context - request.cancel emits REQUEST_CANCEL for user requests and not for _internal ones - transaction begin/commit/rollback emit through their channels, with the new isolationLevel + isolationLevelName pair and the aborted flag Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 66bb3ac commit 2dcf2a6

1 file changed

Lines changed: 179 additions & 0 deletions

File tree

test/common/diagnostics.js

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,183 @@ describe('Diagnostics Channel', () => {
192192
assert.strictEqual(req._internal, false)
193193
})
194194
})
195+
196+
// Integration tests: drive the real Request / Transaction / ConnectionPool
197+
// classes (with stubbed drivers) to verify the diagnostics_channel
198+
// instrumentation fires end-to-end — not just that the helpers work.
199+
describe('Instrumentation integration', () => {
200+
const sql = require('../../')
201+
202+
function collect (channel) {
203+
const events = []
204+
const handler = (msg) => events.push(msg)
205+
dc.subscribe(channel, handler)
206+
return {
207+
events,
208+
stop () { dc.unsubscribe(channel, handler) }
209+
}
210+
}
211+
212+
function collectTraces (tracingChannel) {
213+
const events = []
214+
const handlers = {
215+
start: (ctx) => events.push({ event: 'start', ctx }),
216+
end: (ctx) => events.push({ event: 'end', ctx }),
217+
asyncStart: (ctx) => events.push({ event: 'asyncStart', ctx }),
218+
asyncEnd: (ctx) => events.push({ event: 'asyncEnd', ctx }),
219+
error: (ctx) => events.push({ event: 'error', ctx })
220+
}
221+
tracingChannel.subscribe(handlers)
222+
return {
223+
events,
224+
stop () { tracingChannel.unsubscribe(handlers) }
225+
}
226+
}
227+
228+
it('request.query() emits TRACE_QUERY start/asyncEnd with context', async () => {
229+
const req = new sql.Request()
230+
req._query = (cmd, cb) => setImmediate(cb, null, [[{ x: 1 }]], {}, 1)
231+
req.input('id', sql.Int, 42)
232+
233+
const tc = tracingChannels[CHANNELS.TRACE_QUERY]
234+
const { events, stop } = collectTraces(tc)
235+
try {
236+
await req.query('SELECT @id')
237+
const starts = events.filter(e => e.event === 'start')
238+
const asyncEnds = events.filter(e => e.event === 'asyncEnd')
239+
assert.strictEqual(starts.length, 1)
240+
assert.strictEqual(asyncEnds.length, 1)
241+
assert.strictEqual(starts[0].ctx.command, 'SELECT @id')
242+
assert.deepStrictEqual(starts[0].ctx.parameters, ['id'])
243+
assert.strictEqual(typeof starts[0].ctx.requestId, 'number')
244+
} finally {
245+
stop()
246+
}
247+
})
248+
249+
it('request.query() emits TRACE_QUERY error on rejection', async () => {
250+
const req = new sql.Request()
251+
const boom = new Error('boom')
252+
req._query = (cmd, cb) => setImmediate(cb, boom)
253+
254+
const tc = tracingChannels[CHANNELS.TRACE_QUERY]
255+
const { events, stop } = collectTraces(tc)
256+
try {
257+
await assert.rejects(() => req.query('SELECT 1'), { message: 'boom' })
258+
const errors = events.filter(e => e.event === 'error')
259+
assert.strictEqual(errors.length, 1)
260+
assert.strictEqual(errors[0].ctx.error, boom)
261+
} finally {
262+
stop()
263+
}
264+
})
265+
266+
it('request marked as _internal does not emit TRACE_QUERY', async () => {
267+
const req = new sql.Request()
268+
req._internal = true
269+
req._query = (cmd, cb) => setImmediate(cb, null, [[]], {}, 0)
270+
271+
const tc = tracingChannels[CHANNELS.TRACE_QUERY]
272+
const { events, stop } = collectTraces(tc)
273+
try {
274+
await req.query('SELECT 1')
275+
assert.strictEqual(events.length, 0)
276+
} finally {
277+
stop()
278+
}
279+
})
280+
281+
it('request.execute() emits TRACE_EXECUTE with procedure name', async () => {
282+
const req = new sql.Request()
283+
req._execute = (cmd, cb) => setImmediate(cb, null, [[]], {}, 0, 0)
284+
285+
const tc = tracingChannels[CHANNELS.TRACE_EXECUTE]
286+
const { events, stop } = collectTraces(tc)
287+
try {
288+
await req.execute('sp_test')
289+
const starts = events.filter(e => e.event === 'start')
290+
assert.strictEqual(starts.length, 1)
291+
assert.strictEqual(starts[0].ctx.procedure, 'sp_test')
292+
} finally {
293+
stop()
294+
}
295+
})
296+
297+
it('request.cancel() emits REQUEST_CANCEL for user requests only', () => {
298+
const { events, stop } = collect(CHANNELS.REQUEST_CANCEL)
299+
try {
300+
const userReq = new sql.Request()
301+
userReq._cancel = () => {}
302+
userReq.cancel()
303+
assert.strictEqual(events.length, 1)
304+
assert.strictEqual(typeof events[0].requestId, 'number')
305+
306+
const internalReq = new sql.Request()
307+
internalReq._internal = true
308+
internalReq._cancel = () => {}
309+
internalReq.cancel()
310+
assert.strictEqual(events.length, 1, 'internal cancel should not emit')
311+
} finally {
312+
stop()
313+
}
314+
})
315+
316+
// Stub for Transaction._begin that mirrors what the real driver does:
317+
// assigns the requested isolation level and clears the aborted flag.
318+
function stubTransaction (tx) {
319+
tx._begin = function (level, cb) {
320+
if (level) this.isolationLevel = level
321+
this._aborted = false
322+
setImmediate(cb, null)
323+
}
324+
tx._commit = (cb) => setImmediate(cb, null)
325+
tx._rollback = (cb) => setImmediate(cb, null)
326+
}
327+
328+
it('transaction.begin emits TRANSACTION_BEGIN with numeric + named isolation level', async () => {
329+
const tx = new sql.Transaction()
330+
stubTransaction(tx)
331+
332+
const { events, stop } = collect(CHANNELS.TRANSACTION_BEGIN)
333+
try {
334+
await tx.begin(sql.ISOLATION_LEVEL.SERIALIZABLE)
335+
assert.strictEqual(events.length, 1)
336+
assert.strictEqual(events[0].isolationLevel, sql.ISOLATION_LEVEL.SERIALIZABLE)
337+
assert.strictEqual(events[0].isolationLevelName, 'SERIALIZABLE')
338+
assert.strictEqual(typeof events[0].transactionId, 'number')
339+
} finally {
340+
stop()
341+
}
342+
})
343+
344+
it('transaction.commit emits TRANSACTION_COMMIT', async () => {
345+
const tx = new sql.Transaction()
346+
stubTransaction(tx)
347+
348+
await tx.begin()
349+
const { events, stop } = collect(CHANNELS.TRANSACTION_COMMIT)
350+
try {
351+
await tx.commit()
352+
assert.strictEqual(events.length, 1)
353+
assert.strictEqual(typeof events[0].transactionId, 'number')
354+
} finally {
355+
stop()
356+
}
357+
})
358+
359+
it('transaction.rollback emits TRANSACTION_ROLLBACK with aborted flag', async () => {
360+
const tx = new sql.Transaction()
361+
stubTransaction(tx)
362+
363+
await tx.begin()
364+
const { events, stop } = collect(CHANNELS.TRANSACTION_ROLLBACK)
365+
try {
366+
await tx.rollback()
367+
assert.strictEqual(events.length, 1)
368+
assert.strictEqual(events[0].aborted, false)
369+
} finally {
370+
stop()
371+
}
372+
})
373+
})
195374
})

0 commit comments

Comments
 (0)