@@ -83,6 +83,7 @@ describe('BatchLogRecordProcessorBase', () => {
8383 afterEach ( ( ) => {
8484 exporter . reset ( ) ;
8585 sinon . restore ( ) ;
86+ setGlobalErrorHandler ( loggingErrorHandler ) ;
8687 } ) ;
8788
8889 describe ( 'constructor' , ( ) => {
@@ -396,6 +397,37 @@ describe('BatchLogRecordProcessorBase', () => {
396397 setGlobalErrorHandler ( loggingErrorHandler ( ) ) ;
397398 } ) ;
398399
400+ it ( 'should call globalErrorHandler when export exceeds timeout' , async function ( ) {
401+ // arrange
402+ const clock = sinon . useFakeTimers ( ) ;
403+ const exportTimeoutMillis = 1000 ;
404+ sinon . stub ( exporter , 'export' ) . callsFake ( ( _ , callback ) => {
405+ // never call the callback to simulate a hung export
406+ // the timeout should trigger instead.
407+ } ) ;
408+ const errorHandlerSpy = sinon . spy ( ) ;
409+ setGlobalErrorHandler ( errorHandlerSpy ) ;
410+ const processor = new BatchLogRecordProcessor ( exporter , {
411+ maxExportBatchSize : 1 ,
412+ scheduledDelayMillis : 2500 ,
413+ exportTimeoutMillis,
414+ } ) ;
415+
416+ // act
417+ processor . onEmit ( createLogRecord ( ) ) ;
418+ await clock . tickAsync ( exportTimeoutMillis + 100 ) ;
419+
420+ // assert
421+ sinon . assert . calledOnceWithMatch (
422+ errorHandlerSpy ,
423+ sinon . match . instanceOf ( Error )
424+ ) ;
425+ sinon . assert . calledOnceWithMatch (
426+ errorHandlerSpy ,
427+ sinon . match . has ( 'message' , 'Timeout' )
428+ ) ;
429+ } ) ;
430+
399431 it ( 'should drop logRecords when there are more logRecords than "maxQueueSize"' , function ( ) {
400432 // Use a large batch size to prevent automatic exports during this test
401433 const maxQueueSize = 6 ;
@@ -476,6 +508,58 @@ describe('BatchLogRecordProcessorBase', () => {
476508 'fromasync'
477509 ) ;
478510 } ) ;
511+
512+ it ( 'should call forceFlush on exporter when export is in progress' , async ( ) => {
513+ // arrange
514+ let exportCallback : ( ( result : ExportResult ) => void ) | undefined ;
515+ const customExporter : LogRecordExporter = {
516+ export : ( logs , callback ) => {
517+ // keep export pending, so that we can resolve it later
518+ exportCallback = callback ;
519+ } ,
520+ shutdown : async ( ) => { } ,
521+ forceFlush : async ( ) => { } ,
522+ } ;
523+ const forceFlushSpy = sinon . spy ( customExporter , 'forceFlush' ) ;
524+ const processor = new BatchLogRecordProcessor ( customExporter , {
525+ maxExportBatchSize : 1 ,
526+ scheduledDelayMillis : 2500 ,
527+ } ) ;
528+
529+ // emit enough logs to trigger export
530+ processor . onEmit ( createLogRecord ( ) ) ;
531+ // yield to allow export to start
532+ await new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
533+ // sanity check - ensure export is indeed in progress
534+ assert . ok ( exportCallback !== undefined ) ;
535+
536+ // act
537+ const forceFlushPromise = processor . forceFlush ( ) ;
538+ // yield to allow forceFlush to continue
539+ await new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
540+
541+ // assert
542+ sinon . assert . calledOnce ( forceFlushSpy ) ;
543+
544+ if ( exportCallback !== undefined ) {
545+ exportCallback ( { code : ExportResultCode . SUCCESS } ) ;
546+ }
547+ await forceFlushPromise ;
548+ await processor . shutdown ( ) ;
549+ } ) ;
550+
551+ it ( 'should not call forceFlush on exporter when queue is empty and no export in progress' , async function ( ) {
552+ // arrange
553+ const forceFlushSpy = sinon . spy ( exporter , 'forceFlush' ) ;
554+ const processor = new BatchLogRecordProcessor ( exporter ) ;
555+
556+ // act - nothing in the queue and no export in progress
557+ await processor . forceFlush ( ) ;
558+
559+ // assert
560+ sinon . assert . notCalled ( forceFlushSpy ) ;
561+ await processor . shutdown ( ) ;
562+ } ) ;
479563 } ) ;
480564
481565 describe ( 'shutdown' , ( ) => {
0 commit comments