@@ -680,6 +680,80 @@ describe('Integration | Transactions', () => {
680680 expect ( finishedSpans [ 0 ] ?. name ) . toBe ( 'inner span 2' ) ;
681681 } ) ;
682682
683+ it ( 'only considers sent spans, not finished spans, for flushing orphaned spans of sent spans' , async ( ) => {
684+ const timeout = 5 * 60 * 1000 ;
685+ const now = Date . now ( ) ;
686+ vi . useFakeTimers ( ) ;
687+ vi . setSystemTime ( now ) ;
688+
689+ const logs : unknown [ ] = [ ] ;
690+ vi . spyOn ( logger , 'log' ) . mockImplementation ( msg => logs . push ( msg ) ) ;
691+
692+ const transactions : Event [ ] = [ ] ;
693+
694+ mockSdkInit ( {
695+ tracesSampleRate : 1 ,
696+ beforeSendTransaction : event => {
697+ transactions . push ( event ) ;
698+ return null ;
699+ } ,
700+ } ) ;
701+
702+ const provider = getProvider ( ) ;
703+ const multiSpanProcessor = provider ?. activeSpanProcessor as
704+ | ( SpanProcessor & { _spanProcessors ?: SpanProcessor [ ] } )
705+ | undefined ;
706+ const spanProcessor = multiSpanProcessor ?. [ '_spanProcessors' ] ?. find (
707+ spanProcessor => spanProcessor instanceof SentrySpanProcessor ,
708+ ) as SentrySpanProcessor | undefined ;
709+
710+ const exporter = spanProcessor ? spanProcessor [ '_exporter' ] : undefined ;
711+
712+ if ( ! exporter ) {
713+ throw new Error ( 'No exporter found, aborting test...' ) ;
714+ }
715+
716+ /**
717+ * This is our span structure:
718+ * span 1 --------
719+ * span 2 ---
720+ * span 3 -
721+ *
722+ * Where span 2 is finished before span 3 & span 1
723+ */
724+
725+ const [ span1 , span3 ] = startSpanManual ( { name : 'span 1' } , span1 => {
726+ const [ span2 , span3 ] = startSpanManual ( { name : 'span 2' } , span2 => {
727+ const span3 = startInactiveSpan ( { name : 'span 3' } ) ;
728+ return [ span2 , span3 ] ;
729+ } ) ;
730+
731+ // End span 2 before span 3
732+ span2 . end ( ) ;
733+
734+ return [ span1 , span3 ] ;
735+ } ) ;
736+
737+ vi . advanceTimersByTime ( 1 ) ;
738+
739+ // nothing should be sent yet, as span1 is not yet done
740+ expect ( transactions ) . toHaveLength ( 0 ) ;
741+
742+ // Now finish span1, should be sent with only span2 but without span3, as that is not yet finished
743+ span1 . end ( ) ;
744+ vi . advanceTimersByTime ( 1 ) ;
745+
746+ expect ( transactions ) . toHaveLength ( 1 ) ;
747+ expect ( transactions [ 0 ] ?. spans ) . toHaveLength ( 1 ) ;
748+
749+ // now finish span3, which should be sent as transaction too
750+ span3 . end ( ) ;
751+ vi . advanceTimersByTime ( timeout ) ;
752+
753+ expect ( transactions ) . toHaveLength ( 2 ) ;
754+ expect ( transactions [ 1 ] ?. spans ) . toHaveLength ( 0 ) ;
755+ } ) ;
756+
683757 it ( 'uses & inherits DSC on span trace state' , async ( ) => {
684758 const transactionEvents : Event [ ] = [ ] ;
685759 const beforeSendTransaction = vi . fn ( event => {
0 commit comments