@@ -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