33const assert = require ( 'assert' ) ;
44const mongoose = require ( '../index' ) ;
55
6- const { channel } = require ( '../lib/tracing' ) ;
6+ const { channel, cursorNextChannel } = require ( '../lib/tracing' ) ;
77
88describe ( 'TracingChannel' , function ( ) {
99 let conn ;
@@ -383,6 +383,131 @@ describe('TracingChannel', function() {
383383 } ) ;
384384 } ) ;
385385
386+ describe ( 'cursor:next operations' , function ( ) {
387+ it ( 'fires start and asyncEnd for query cursor next()' , async function ( ) {
388+ await Test . create ( [
389+ { name : 'cursor1' , age : 10 } ,
390+ { name : 'cursor2' , age : 20 }
391+ ] ) ;
392+
393+ const events = [ ] ;
394+ const handlers = {
395+ start ( ctx ) { events . push ( { event : 'start' , ...ctx } ) ; } ,
396+ end ( ) { events . push ( { event : 'end' } ) ; } ,
397+ asyncStart ( ctx ) { events . push ( { event : 'asyncStart' , result : ctx . result } ) ; } ,
398+ asyncEnd ( ctx ) { events . push ( { event : 'asyncEnd' , result : ctx . result } ) ; } ,
399+ error ( ctx ) { events . push ( { event : 'error' , error : ctx . error } ) ; }
400+ } ;
401+
402+ cursorNextChannel . subscribe ( handlers ) ;
403+ try {
404+ const cursor = Test . find ( { name : / ^ c u r s o r / } ) . sort ( { name : 1 } ) . cursor ( ) ;
405+ const doc1 = await cursor . next ( ) ;
406+ const doc2 = await cursor . next ( ) ;
407+ const doc3 = await cursor . next ( ) ;
408+
409+ assert . strictEqual ( doc1 . name , 'cursor1' ) ;
410+ assert . strictEqual ( doc2 . name , 'cursor2' ) ;
411+ assert . strictEqual ( doc3 , null ) ;
412+
413+ const starts = events . filter ( e => e . event === 'start' ) ;
414+ assert . strictEqual ( starts . length , 3 , 'should fire start for each next() call' ) ;
415+ assert . strictEqual ( starts [ 0 ] . operation , 'find' ) ;
416+ assert . strictEqual ( starts [ 0 ] . collection , collectionName ) ;
417+ assert . ok ( starts [ 0 ] . database ) ;
418+
419+ const asyncEnds = events . filter ( e => e . event === 'asyncEnd' ) ;
420+ assert . strictEqual ( asyncEnds . length , 3 , 'should fire asyncEnd for each next() call' ) ;
421+ } finally {
422+ cursorNextChannel . unsubscribe ( handlers ) ;
423+ }
424+ } ) ;
425+
426+ it ( 'fires start and asyncEnd for aggregate cursor next()' , async function ( ) {
427+ await Test . create ( [
428+ { name : 'agg-cursor1' , age : 10 } ,
429+ { name : 'agg-cursor2' , age : 20 }
430+ ] ) ;
431+
432+ const events = [ ] ;
433+ const handlers = {
434+ start ( ctx ) { events . push ( { event : 'start' , ...ctx } ) ; } ,
435+ end ( ) { events . push ( { event : 'end' } ) ; } ,
436+ asyncStart ( ) { } ,
437+ asyncEnd ( ctx ) { events . push ( { event : 'asyncEnd' , result : ctx . result } ) ; } ,
438+ error ( ) { }
439+ } ;
440+
441+ cursorNextChannel . subscribe ( handlers ) ;
442+ try {
443+ const cursor = Test . aggregate ( [
444+ { $match : { name : / ^ a g g - c u r s o r / } } ,
445+ { $sort : { name : 1 } }
446+ ] ) . cursor ( ) ;
447+
448+ const doc1 = await cursor . next ( ) ;
449+ const doc2 = await cursor . next ( ) ;
450+ const doc3 = await cursor . next ( ) ;
451+
452+ assert . strictEqual ( doc1 . name , 'agg-cursor1' ) ;
453+ assert . strictEqual ( doc2 . name , 'agg-cursor2' ) ;
454+ assert . strictEqual ( doc3 , null ) ;
455+
456+ const starts = events . filter ( e => e . event === 'start' ) ;
457+ assert . strictEqual ( starts . length , 3 , 'should fire start for each next() call' ) ;
458+ assert . strictEqual ( starts [ 0 ] . operation , 'aggregate' ) ;
459+ assert . strictEqual ( starts [ 0 ] . collection , collectionName ) ;
460+ assert . ok ( starts [ 0 ] . database ) ;
461+ assert . ok ( Array . isArray ( starts [ 0 ] . args . pipeline ) ) ;
462+ } finally {
463+ cursorNextChannel . unsubscribe ( handlers ) ;
464+ }
465+ } ) ;
466+
467+ it ( 'fires error event on cursor next() failure' , async function ( ) {
468+ const events = [ ] ;
469+ const handlers = {
470+ start ( ) { events . push ( { event : 'start' } ) ; } ,
471+ end ( ) { } ,
472+ asyncStart ( ) { } ,
473+ asyncEnd ( ) { } ,
474+ error ( ctx ) { events . push ( { event : 'error' , error : ctx . error } ) ; }
475+ } ;
476+
477+ cursorNextChannel . subscribe ( handlers ) ;
478+ try {
479+ const cursor = Test . find ( { $invalidOperator : true } ) . cursor ( ) ;
480+ await cursor . next ( ) . catch ( ( ) => { } ) ;
481+
482+ assert . ok ( events . some ( e => e . event === 'start' ) , 'start should fire' ) ;
483+ assert . ok ( events . some ( e => e . event === 'error' ) , 'error should fire' ) ;
484+ } finally {
485+ cursorNextChannel . unsubscribe ( handlers ) ;
486+ }
487+ } ) ;
488+
489+ it ( 'does not fire cursor:next events when using regular find()' , async function ( ) {
490+ await Test . create ( { name : 'no-cursor' , age : 5 } ) ;
491+
492+ const events = [ ] ;
493+ const handlers = {
494+ start ( ) { events . push ( { event : 'start' } ) ; } ,
495+ end ( ) { } ,
496+ asyncStart ( ) { } ,
497+ asyncEnd ( ) { } ,
498+ error ( ) { }
499+ } ;
500+
501+ cursorNextChannel . subscribe ( handlers ) ;
502+ try {
503+ await Test . find ( { name : 'no-cursor' } ) ;
504+ assert . strictEqual ( events . length , 0 , 'cursor:next should not fire for regular find()' ) ;
505+ } finally {
506+ cursorNextChannel . unsubscribe ( handlers ) ;
507+ }
508+ } ) ;
509+ } ) ;
510+
386511 describe ( 'zero-cost when no subscribers' , function ( ) {
387512 it ( 'operations work without any subscribers' , async function ( ) {
388513 const doc = new Test ( { name : 'no-sub' , age : 5 } ) ;
@@ -401,5 +526,16 @@ describe('TracingChannel', function() {
401526
402527 await Test . deleteMany ( { name : / ^ n o - s u b / } ) ;
403528 } ) ;
529+
530+ it ( 'cursor next() works without any subscribers' , async function ( ) {
531+ await Test . create ( { name : 'no-sub-cursor' , age : 5 } ) ;
532+
533+ const cursor = Test . find ( { name : 'no-sub-cursor' } ) . cursor ( ) ;
534+ const doc = await cursor . next ( ) ;
535+ assert . strictEqual ( doc . name , 'no-sub-cursor' ) ;
536+
537+ const done = await cursor . next ( ) ;
538+ assert . strictEqual ( done , null ) ;
539+ } ) ;
404540 } ) ;
405541} ) ;
0 commit comments