@@ -22,6 +22,9 @@ import { type ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTra
22
22
import { KeyTransportEvents , type KeyTransportEventsHandlerMap } from "../../../src/matrixrtc/IKeyTransport.ts" ;
23
23
import { membershipTemplate , mockCallMembership } from "./mocks.ts" ;
24
24
import { 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" ;
25
28
26
29
describe ( "RTCEncryptionManager" , ( ) => {
27
30
// The manager being tested
@@ -505,6 +508,86 @@ describe("RTCEncryptionManager", () => {
505
508
) ;
506
509
} ) ;
507
510
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
+
508
591
function aCallMembership ( userId : string , deviceId : string , ts : number = 1000 ) : CallMembership {
509
592
return mockCallMembership (
510
593
Object . assign ( { } , membershipTemplate , { device_id : deviceId , created_ts : ts } ) ,
0 commit comments