@@ -126,7 +126,7 @@ describe('UserContextManager', () => {
126126 const config = createManagerConfig ( client ) ;
127127 const manager = new UserContextManager ( config ) ;
128128
129- manager . resolveUserContext ( ) ; // no user
129+ manager . resolveUserContext ( { } ) ; // empty object = VUID mode
130130 await flushPromises ( ) ;
131131
132132 // Should be waiting on onReady
@@ -158,7 +158,7 @@ describe('UserContextManager', () => {
158158 const config = createManagerConfig ( client ) ;
159159 const manager = new UserContextManager ( config ) ;
160160
161- manager . resolveUserContext ( ) ;
161+ manager . resolveUserContext ( { } ) ;
162162 await flushPromises ( ) ;
163163
164164 expect ( client . onReady ) . not . toHaveBeenCalled ( ) ;
@@ -239,7 +239,7 @@ describe('UserContextManager', () => {
239239 const config = createManagerConfig ( client ) ;
240240 const manager = new UserContextManager ( config ) ;
241241
242- manager . resolveUserContext ( ) ; // no user
242+ manager . resolveUserContext ( { } ) ; // empty object = VUID mode
243243 await flushPromises ( ) ;
244244
245245 // Waiting on onReady for VUID
@@ -293,7 +293,7 @@ describe('UserContextManager', () => {
293293 const config = createManagerConfig ( client ) ;
294294 const manager = new UserContextManager ( config ) ;
295295
296- manager . resolveUserContext ( undefined , undefined , true ) ; // no user , skipSegments
296+ manager . resolveUserContext ( { } , undefined , true ) ; // empty object = VUID , skipSegments
297297 await flushPromises ( ) ;
298298
299299 expect ( client . onReady ) . toHaveBeenCalledTimes ( 1 ) ;
@@ -326,7 +326,7 @@ describe('UserContextManager', () => {
326326 const config = createManagerConfig ( client ) ;
327327 const manager = new UserContextManager ( config ) ;
328328
329- manager . resolveUserContext ( undefined , undefined , true ) ; // no user , no VUID, skipSegments
329+ manager . resolveUserContext ( { } , undefined , true ) ; // empty object , no VUID, skipSegments
330330 await flushPromises ( ) ;
331331
332332 expect ( client . onReady ) . not . toHaveBeenCalled ( ) ;
@@ -531,12 +531,12 @@ describe('UserContextManager', () => {
531531 const config = createManagerConfig ( client ) ;
532532 const manager = new UserContextManager ( config ) ;
533533
534- // First call — no userId , will wait for onReady
535- manager . resolveUserContext ( ) ;
534+ // First call — empty object (VUID) , will wait for onReady
535+ manager . resolveUserContext ( { } ) ;
536536 await flushPromises ( ) ;
537537
538- // Second call — also no userId , should invalidate first
539- manager . resolveUserContext ( ) ;
538+ // Second call — also empty object , should invalidate first
539+ manager . resolveUserContext ( { } ) ;
540540 await flushPromises ( ) ;
541541
542542 // Resolve onReady — both resume, but first is stale
@@ -564,8 +564,8 @@ describe('UserContextManager', () => {
564564 const config = createManagerConfig ( client ) ;
565565 const manager = new UserContextManager ( config ) ;
566566
567- // First call — no userId , will wait for onReady
568- manager . resolveUserContext ( ) ;
567+ // First call — empty object (VUID) , will wait for onReady
568+ manager . resolveUserContext ( { } ) ;
569569 await flushPromises ( ) ;
570570 expect ( client . onReady ) . toHaveBeenCalled ( ) ;
571571
@@ -648,7 +648,7 @@ describe('UserContextManager', () => {
648648 const config = createManagerConfig ( client ) ;
649649 const manager = new UserContextManager ( config ) ;
650650
651- manager . resolveUserContext ( ) ; // no userId , will await onReady
651+ manager . resolveUserContext ( { } ) ; // empty object (VUID) , will await onReady
652652 await flushPromises ( ) ;
653653
654654 // Dispose before onReady resolves
@@ -701,7 +701,7 @@ describe('UserContextManager', () => {
701701 const config = createManagerConfig ( client ) ;
702702 const manager = new UserContextManager ( config ) ;
703703
704- manager . resolveUserContext ( ) ; // no userId , will await onReady
704+ manager . resolveUserContext ( { } ) ; // empty object (VUID) , will await onReady
705705 await flushPromises ( ) ;
706706
707707 manager . dispose ( ) ;
@@ -727,7 +727,7 @@ describe('UserContextManager', () => {
727727 const config = createManagerConfig ( client ) ;
728728 const manager = new UserContextManager ( config ) ;
729729
730- manager . resolveUserContext ( ) ;
730+ manager . resolveUserContext ( { } ) ;
731731 await flushPromises ( ) ;
732732
733733 onReadyDeferred . reject ( new Error ( 'SDK init failed' ) ) ;
@@ -747,7 +747,7 @@ describe('UserContextManager', () => {
747747 const config = createManagerConfig ( client ) ;
748748 const manager = new UserContextManager ( config ) ;
749749
750- manager . resolveUserContext ( ) ;
750+ manager . resolveUserContext ( { } ) ;
751751 await flushPromises ( ) ;
752752
753753 onReadyDeferred . reject ( 'string error' ) ;
@@ -902,4 +902,165 @@ describe('UserContextManager', () => {
902902 manager . dispose ( ) ;
903903 } ) ;
904904 } ) ;
905+
906+ // ============================================================
907+ // Null / undefined user (no-context guard)
908+ // ============================================================
909+ describe ( 'null / undefined user (no-context guard)' , ( ) => {
910+ it ( 'should not call createUserContext when user is undefined' , async ( ) => {
911+ const { client } = createMockClient ( {
912+ hasOdpManager : false ,
913+ hasVuidManager : false ,
914+ } ) ;
915+ const config = createManagerConfig ( client ) ;
916+ const manager = new UserContextManager ( config ) ;
917+
918+ manager . resolveUserContext ( undefined ) ;
919+ await flushPromises ( ) ;
920+
921+ expect ( client . createUserContext ) . not . toHaveBeenCalled ( ) ;
922+ expect ( config . onUserContextReady ) . toHaveBeenCalledWith ( null ) ;
923+ expect ( config . onError ) . not . toHaveBeenCalled ( ) ;
924+
925+ manager . dispose ( ) ;
926+ } ) ;
927+
928+ it ( 'should not call createUserContext when user is null' , async ( ) => {
929+ const { client } = createMockClient ( {
930+ hasOdpManager : false ,
931+ hasVuidManager : false ,
932+ } ) ;
933+ const config = createManagerConfig ( client ) ;
934+ const manager = new UserContextManager ( config ) ;
935+
936+ manager . resolveUserContext ( null ) ;
937+ await flushPromises ( ) ;
938+
939+ expect ( client . createUserContext ) . not . toHaveBeenCalled ( ) ;
940+ expect ( config . onUserContextReady ) . toHaveBeenCalledWith ( null ) ;
941+ expect ( config . onError ) . not . toHaveBeenCalled ( ) ;
942+
943+ manager . dispose ( ) ;
944+ } ) ;
945+
946+ it ( 'should call onUserContextReady(null) when user transitions from valid to null' , async ( ) => {
947+ const { client } = createMockClient ( {
948+ hasOdpManager : false ,
949+ hasVuidManager : false ,
950+ } ) ;
951+ const config = createManagerConfig ( client ) ;
952+ const manager = new UserContextManager ( config ) ;
953+
954+ manager . resolveUserContext ( { id : 'user-1' } ) ;
955+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
956+ expect ( client . createUserContext ) . toHaveBeenCalledTimes ( 1 ) ;
957+
958+ manager . resolveUserContext ( null ) ;
959+ await flushPromises ( ) ;
960+
961+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 2 ) ;
962+ expect ( config . onUserContextReady ) . toHaveBeenLastCalledWith ( null ) ;
963+ expect ( client . createUserContext ) . toHaveBeenCalledTimes ( 1 ) ;
964+
965+ manager . dispose ( ) ;
966+ } ) ;
967+
968+ it ( 'should cancel in-flight async work when user becomes null' , async ( ) => {
969+ const { client, onReadyDeferred } = createMockClient ( {
970+ hasOdpManager : false ,
971+ hasVuidManager : true ,
972+ } ) ;
973+ const config = createManagerConfig ( client ) ;
974+ const manager = new UserContextManager ( config ) ;
975+
976+ // Start with empty object (VUID flow) — async, waiting on onReady
977+ manager . resolveUserContext ( { } ) ;
978+ await flushPromises ( ) ;
979+ expect ( client . onReady ) . toHaveBeenCalled ( ) ;
980+
981+ // User becomes null before onReady resolves
982+ manager . resolveUserContext ( null ) ;
983+ await flushPromises ( ) ;
984+
985+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
986+ expect ( config . onUserContextReady ) . toHaveBeenCalledWith ( null ) ;
987+
988+ // onReady resolves — stale request should not fire callback
989+ onReadyDeferred . resolve ( undefined ) ;
990+ await flushPromises ( ) ;
991+
992+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
993+ expect ( client . createUserContext ) . not . toHaveBeenCalled ( ) ;
994+
995+ manager . dispose ( ) ;
996+ } ) ;
997+
998+ it ( 'should create context when user transitions from null to valid' , async ( ) => {
999+ const { client, mockUserContext } = createMockClient ( {
1000+ hasOdpManager : false ,
1001+ hasVuidManager : false ,
1002+ } ) ;
1003+ const config = createManagerConfig ( client ) ;
1004+ const manager = new UserContextManager ( config ) ;
1005+
1006+ manager . resolveUserContext ( null ) ;
1007+ await flushPromises ( ) ;
1008+
1009+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
1010+ expect ( config . onUserContextReady ) . toHaveBeenCalledWith ( null ) ;
1011+
1012+ manager . resolveUserContext ( { id : 'user-1' } ) ;
1013+
1014+ expect ( client . createUserContext ) . toHaveBeenCalledWith ( 'user-1' , undefined ) ;
1015+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 2 ) ;
1016+ expect ( config . onUserContextReady ) . toHaveBeenLastCalledWith ( mockUserContext ) ;
1017+
1018+ manager . dispose ( ) ;
1019+ } ) ;
1020+
1021+ it ( 'should short-circuit when user stays null' , async ( ) => {
1022+ const { client } = createMockClient ( {
1023+ hasOdpManager : false ,
1024+ hasVuidManager : false ,
1025+ } ) ;
1026+ const config = createManagerConfig ( client ) ;
1027+ const manager = new UserContextManager ( config ) ;
1028+
1029+ manager . resolveUserContext ( null ) ;
1030+ await flushPromises ( ) ;
1031+
1032+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
1033+
1034+ // Call again with null — should short-circuit
1035+ manager . resolveUserContext ( null ) ;
1036+ await flushPromises ( ) ;
1037+
1038+ expect ( config . onUserContextReady ) . toHaveBeenCalledTimes ( 1 ) ;
1039+ expect ( client . createUserContext ) . not . toHaveBeenCalled ( ) ;
1040+
1041+ manager . dispose ( ) ;
1042+ } ) ;
1043+
1044+ it ( 'should trigger VUID flow when user is empty object' , async ( ) => {
1045+ const { client, mockUserContext, onReadyDeferred } = createMockClient ( {
1046+ hasOdpManager : false ,
1047+ hasVuidManager : true ,
1048+ } ) ;
1049+ const config = createManagerConfig ( client ) ;
1050+ const manager = new UserContextManager ( config ) ;
1051+
1052+ manager . resolveUserContext ( { } ) ;
1053+ await flushPromises ( ) ;
1054+
1055+ expect ( client . onReady ) . toHaveBeenCalled ( ) ;
1056+
1057+ onReadyDeferred . resolve ( undefined ) ;
1058+ await flushPromises ( ) ;
1059+
1060+ expect ( client . createUserContext ) . toHaveBeenCalledWith ( undefined , undefined ) ;
1061+ expect ( config . onUserContextReady ) . toHaveBeenCalledWith ( mockUserContext ) ;
1062+
1063+ manager . dispose ( ) ;
1064+ } ) ;
1065+ } ) ;
9051066} ) ;
0 commit comments