@@ -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