@@ -824,4 +824,243 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
824824 expect ( roleIndexes . find ( idx => idx . name === 'name_1' ) ) . toBeDefined ( ) ;
825825 } ) ;
826826 } ) ;
827+
828+ describe ( 'logClientEvents' , ( ) => {
829+ it ( 'should log MongoDB client events when configured' , async ( ) => {
830+ const logger = require ( '../lib/logger' ) . logger ;
831+ const logSpy = spyOn ( logger , 'warn' ) ;
832+
833+ const logClientEvents = [
834+ {
835+ name : 'serverDescriptionChanged' ,
836+ keys : [ 'address' ] ,
837+ logLevel : 'warn' ,
838+ } ,
839+ ] ;
840+
841+ const adapter = new MongoStorageAdapter ( {
842+ uri : databaseURI ,
843+ mongoOptions : { logClientEvents } ,
844+ } ) ;
845+
846+ // Connect to trigger event listeners setup
847+ await adapter . connect ( ) ;
848+
849+ // Manually trigger the event to test the listener
850+ const mockEvent = {
851+ address : 'localhost:27017' ,
852+ previousDescription : { type : 'Unknown' } ,
853+ newDescription : { type : 'Standalone' } ,
854+ } ;
855+
856+ adapter . client . emit ( 'serverDescriptionChanged' , mockEvent ) ;
857+
858+ // Verify the log was called with the correct message
859+ expect ( logSpy ) . toHaveBeenCalledWith (
860+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t s e r v e r D e s c r i p t i o n C h a n g e d : .* " a d d r e s s " : " l o c a l h o s t : 2 7 0 1 7 " / )
861+ ) ;
862+
863+ await adapter . handleShutdown ( ) ;
864+ } ) ;
865+
866+ it ( 'should log entire event when keys are not specified' , async ( ) => {
867+ const logger = require ( '../lib/logger' ) . logger ;
868+ const logSpy = spyOn ( logger , 'info' ) ;
869+
870+ const logClientEvents = [
871+ {
872+ name : 'connectionPoolReady' ,
873+ logLevel : 'info' ,
874+ } ,
875+ ] ;
876+
877+ const adapter = new MongoStorageAdapter ( {
878+ uri : databaseURI ,
879+ mongoOptions : { logClientEvents } ,
880+ } ) ;
881+
882+ await adapter . connect ( ) ;
883+
884+ const mockEvent = {
885+ address : 'localhost:27017' ,
886+ options : { maxPoolSize : 100 } ,
887+ } ;
888+
889+ adapter . client . emit ( 'connectionPoolReady' , mockEvent ) ;
890+
891+ expect ( logSpy ) . toHaveBeenCalledWith (
892+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t c o n n e c t i o n P o o l R e a d y : .* " a d d r e s s " : " l o c a l h o s t : 2 7 0 1 7 " .* " o p t i o n s " / )
893+ ) ;
894+
895+ await adapter . handleShutdown ( ) ;
896+ } ) ;
897+
898+ it ( 'should extract nested keys using dot notation' , async ( ) => {
899+ const logger = require ( '../lib/logger' ) . logger ;
900+ const logSpy = spyOn ( logger , 'warn' ) ;
901+
902+ const logClientEvents = [
903+ {
904+ name : 'topologyDescriptionChanged' ,
905+ keys : [ 'previousDescription.type' , 'newDescription.type' , 'newDescription.servers.size' ] ,
906+ logLevel : 'warn' ,
907+ } ,
908+ ] ;
909+
910+ const adapter = new MongoStorageAdapter ( {
911+ uri : databaseURI ,
912+ mongoOptions : { logClientEvents } ,
913+ } ) ;
914+
915+ await adapter . connect ( ) ;
916+
917+ const mockEvent = {
918+ topologyId : 1 ,
919+ previousDescription : { type : 'Unknown' } ,
920+ newDescription : {
921+ type : 'ReplicaSetWithPrimary' ,
922+ servers : { size : 3 } ,
923+ } ,
924+ } ;
925+
926+ adapter . client . emit ( 'topologyDescriptionChanged' , mockEvent ) ;
927+
928+ expect ( logSpy ) . toHaveBeenCalledWith (
929+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t t o p o l o g y D e s c r i p t i o n C h a n g e d : .* " p r e v i o u s D e s c r i p t i o n .t y p e " : " U n k n o w n " .* " n e w D e s c r i p t i o n .t y p e " : " R e p l i c a S e t W i t h P r i m a r y " .* " n e w D e s c r i p t i o n .s e r v e r s .s i z e " : 3 / )
930+ ) ;
931+
932+ await adapter . handleShutdown ( ) ;
933+ } ) ;
934+
935+ it ( 'should handle invalid log level gracefully' , async ( ) => {
936+ const logger = require ( '../lib/logger' ) . logger ;
937+ const infoSpy = spyOn ( logger , 'info' ) ;
938+
939+ const logClientEvents = [
940+ {
941+ name : 'connectionPoolReady' ,
942+ keys : [ 'address' ] ,
943+ logLevel : 'invalidLogLevel' , // Invalid log level
944+ } ,
945+ ] ;
946+
947+ const adapter = new MongoStorageAdapter ( {
948+ uri : databaseURI ,
949+ mongoOptions : { logClientEvents } ,
950+ } ) ;
951+
952+ await adapter . connect ( ) ;
953+
954+ const mockEvent = {
955+ address : 'localhost:27017' ,
956+ } ;
957+
958+ adapter . client . emit ( 'connectionPoolReady' , mockEvent ) ;
959+
960+ // Should fallback to 'info' level
961+ expect ( infoSpy ) . toHaveBeenCalledWith (
962+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t c o n n e c t i o n P o o l R e a d y : .* " a d d r e s s " : " l o c a l h o s t : 2 7 0 1 7 " / )
963+ ) ;
964+
965+ await adapter . handleShutdown ( ) ;
966+ } ) ;
967+
968+ it ( 'should handle Map and Set instances in events' , async ( ) => {
969+ const logger = require ( '../lib/logger' ) . logger ;
970+ const warnSpy = spyOn ( logger , 'warn' ) ;
971+
972+ const logClientEvents = [
973+ {
974+ name : 'customEvent' ,
975+ logLevel : 'warn' ,
976+ } ,
977+ ] ;
978+
979+ const adapter = new MongoStorageAdapter ( {
980+ uri : databaseURI ,
981+ mongoOptions : { logClientEvents } ,
982+ } ) ;
983+
984+ await adapter . connect ( ) ;
985+
986+ const mockEvent = {
987+ mapData : new Map ( [ [ 'key1' , 'value1' ] , [ 'key2' , 'value2' ] ] ) ,
988+ setData : new Set ( [ 1 , 2 , 3 ] ) ,
989+ } ;
990+
991+ adapter . client . emit ( 'customEvent' , mockEvent ) ;
992+
993+ // Should serialize Map and Set properly
994+ expect ( warnSpy ) . toHaveBeenCalledWith (
995+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t c u s t o m E v e n t : .* " m a p D a t a " : \{ " k e y 1 " : " v a l u e 1 " , " k e y 2 " : " v a l u e 2 " \} .* " s e t D a t a " : \[ 1 , 2 , 3 \] / )
996+ ) ;
997+
998+ await adapter . handleShutdown ( ) ;
999+ } ) ;
1000+
1001+ it ( 'should handle missing keys in event object' , async ( ) => {
1002+ const logger = require ( '../lib/logger' ) . logger ;
1003+ const infoSpy = spyOn ( logger , 'info' ) ;
1004+
1005+ const logClientEvents = [
1006+ {
1007+ name : 'testEvent' ,
1008+ keys : [ 'nonexistent.nested.key' , 'another.missing' ] ,
1009+ logLevel : 'info' ,
1010+ } ,
1011+ ] ;
1012+
1013+ const adapter = new MongoStorageAdapter ( {
1014+ uri : databaseURI ,
1015+ mongoOptions : { logClientEvents } ,
1016+ } ) ;
1017+
1018+ await adapter . connect ( ) ;
1019+
1020+ const mockEvent = {
1021+ actualField : 'value' ,
1022+ } ;
1023+
1024+ adapter . client . emit ( 'testEvent' , mockEvent ) ;
1025+
1026+ // Should handle missing keys gracefully with undefined values
1027+ expect ( infoSpy ) . toHaveBeenCalledWith (
1028+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t t e s t E v e n t : / )
1029+ ) ;
1030+
1031+ await adapter . handleShutdown ( ) ;
1032+ } ) ;
1033+
1034+ it ( 'should handle circular references gracefully' , async ( ) => {
1035+ const logger = require ( '../lib/logger' ) . logger ;
1036+ const infoSpy = spyOn ( logger , 'info' ) ;
1037+
1038+ const logClientEvents = [
1039+ {
1040+ name : 'circularEvent' ,
1041+ logLevel : 'info' ,
1042+ } ,
1043+ ] ;
1044+
1045+ const adapter = new MongoStorageAdapter ( {
1046+ uri : databaseURI ,
1047+ mongoOptions : { logClientEvents } ,
1048+ } ) ;
1049+
1050+ await adapter . connect ( ) ;
1051+
1052+ // Create circular reference
1053+ const mockEvent = { name : 'test' } ;
1054+ mockEvent . self = mockEvent ;
1055+
1056+ adapter . client . emit ( 'circularEvent' , mockEvent ) ;
1057+
1058+ // Should handle circular reference with [Circular] marker
1059+ expect ( infoSpy ) . toHaveBeenCalledWith (
1060+ jasmine . stringMatching ( / M o n g o D B c l i e n t e v e n t c i r c u l a r E v e n t : .* \[ C i r c u l a r \] / )
1061+ ) ;
1062+
1063+ await adapter . handleShutdown ( ) ;
1064+ } ) ;
1065+ } ) ;
8271066} ) ;
0 commit comments