@@ -22,6 +22,9 @@ import { type ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTra
2222import  {  KeyTransportEvents ,  type  KeyTransportEventsHandlerMap  }  from  "../../../src/matrixrtc/IKeyTransport.ts" ; 
2323import  {  membershipTemplate ,  mockCallMembership  }  from  "./mocks.ts" ; 
2424import  {  decodeBase64 ,  TypedEventEmitter  }  from  "../../../src" ; 
25+ import  {  RoomAndToDeviceTransport  }  from  "../../../src/matrixrtc/RoomAndToDeviceKeyTransport.ts" ; 
26+ import  {  type  RoomKeyTransport  }  from  "../../../src/matrixrtc/RoomKeyTransport.ts" ; 
27+ import  type  {  Logger  }  from  "../../../src/logger.ts" ; 
2528
2629describe ( "RTCEncryptionManager" ,  ( )  =>  { 
2730    // The manager being tested 
@@ -505,6 +508,86 @@ describe("RTCEncryptionManager", () => {
505508        ) ; 
506509    } ) ; 
507510
511+     it ( "Should re-distribute key on transport switch" ,  async  ( )  =>  { 
512+         const  toDeviceEmitter  =  new  TypedEventEmitter < KeyTransportEvents ,  KeyTransportEventsHandlerMap > ( ) ; 
513+         const  mockToDeviceTransport  =  { 
514+             start : jest . fn ( ) , 
515+             stop : jest . fn ( ) , 
516+             sendKey : jest . fn ( ) . mockResolvedValue ( undefined ) , 
517+             on : toDeviceEmitter . on . bind ( toDeviceEmitter ) , 
518+             off : toDeviceEmitter . off . bind ( toDeviceEmitter ) , 
519+             emit : toDeviceEmitter . emit . bind ( toDeviceEmitter ) , 
520+             setParentLogger : jest . fn ( ) , 
521+         }  as  unknown  as  Mocked < ToDeviceKeyTransport > ; 
522+ 
523+         const  roomEmitter  =  new  TypedEventEmitter < KeyTransportEvents ,  KeyTransportEventsHandlerMap > ( ) ; 
524+         const  mockRoomTransport  =  { 
525+             start : jest . fn ( ) , 
526+             stop : jest . fn ( ) , 
527+             sendKey : jest . fn ( ) . mockResolvedValue ( undefined ) , 
528+             on : roomEmitter . on . bind ( roomEmitter ) , 
529+             off : roomEmitter . off . bind ( roomEmitter ) , 
530+             emit : roomEmitter . emit . bind ( roomEmitter ) , 
531+             setParentLogger : jest . fn ( ) , 
532+         }  as  unknown  as  Mocked < RoomKeyTransport > ; 
533+ 
534+         const  mockLogger  =  { 
535+             debug : jest . fn ( ) , 
536+             warn : jest . fn ( ) , 
537+         }  as  unknown  as  Mocked < Logger > ; 
538+ 
539+         const  transport  =  new  RoomAndToDeviceTransport ( mockToDeviceTransport ,  mockRoomTransport ,  { 
540+             getChild : jest . fn ( ) . mockReturnValue ( mockLogger ) , 
541+         }  as  unknown  as  Mocked < Logger > ) ; 
542+ 
543+         encryptionManager  =  new  RTCEncryptionManager ( 
544+             "@alice:example.org" , 
545+             "DEVICE01" , 
546+             getMembershipMock , 
547+             transport , 
548+             statistics , 
549+             onEncryptionKeysChanged , 
550+         ) ; 
551+ 
552+         const  members  =  [ 
553+             aCallMembership ( "@bob:example.org" ,  "BOBDEVICE" ) , 
554+             aCallMembership ( "@bob:example.org" ,  "BOBDEVICE2" ) , 
555+             aCallMembership ( "@carl:example.org" ,  "CARLDEVICE" ) , 
556+         ] ; 
557+         getMembershipMock . mockReturnValue ( members ) ; 
558+ 
559+         // Let's join 
560+         encryptionManager . join ( undefined ) ; 
561+         encryptionManager . onMembershipsUpdate ( [ ] ) ; 
562+         await  jest . advanceTimersByTimeAsync ( 10 ) ; 
563+ 
564+         // Should have sent the key to the toDevice transport 
565+         expect ( mockToDeviceTransport . sendKey ) . toHaveBeenCalledTimes ( 1 ) ; 
566+         expect ( mockRoomTransport . sendKey ) . not . toHaveBeenCalled ( ) ; 
567+ 
568+         // Simulate receiving a key by room transport 
569+         roomEmitter . emit ( 
570+             KeyTransportEvents . ReceivedKeys , 
571+             "@bob:example.org" , 
572+             "BOBDEVICE" , 
573+             "AAAAAAAAAAA" , 
574+             0  /* KeyId */ , 
575+             0  /* Timestamp */ , 
576+         ) ; 
577+ 
578+         await  jest . runOnlyPendingTimersAsync ( ) ; 
579+ 
580+         // The key should have beed re-distributed to the room transport 
581+         expect ( mockRoomTransport . sendKey ) . toHaveBeenCalled ( ) ; 
582+         expect ( mockToDeviceTransport . sendKey ) . toHaveBeenCalledWith ( 
583+             expect . any ( String ) , 
584+             // It is the first key re-distributed 
585+             0 , 
586+             // to all the members 
587+             members . map ( ( m )  =>  ( {  userId : m . sender ,  deviceId : m . deviceId ,  membershipTs : m . createdTs ( )  } ) ) , 
588+         ) ; 
589+     } ) ; 
590+ 
508591    function  aCallMembership ( userId : string ,  deviceId : string ,  ts : number  =  1000 ) : CallMembership  { 
509592        return  mockCallMembership ( 
510593            Object . assign ( { } ,  membershipTemplate ,  {  device_id : deviceId ,  created_ts : ts  } ) , 
0 commit comments