@@ -3,6 +3,12 @@ import { makeBeaconEvent, makeBeaconInfoEvent } from "../test-utils/beacon";
33import { filterEmitCallsByEventType } from "../test-utils/emitter" ;
44import { RoomState , RoomStateEvent } from "../../src/models/room-state" ;
55import { BeaconEvent , getBeaconInfoIdentifier } from "../../src/models/beacon" ;
6+ import { EventType , RelationType } from "../../src/@types/event" ;
7+ import {
8+ MatrixEvent ,
9+ MatrixEventEvent ,
10+ } from "../../src/models/event" ;
11+ import { M_BEACON } from "../../src/@types/beacon" ;
612
713describe ( "RoomState" , function ( ) {
814 const roomId = "!foo:bar" ;
@@ -717,52 +723,238 @@ describe("RoomState", function() {
717723 const beacon1 = makeBeaconInfoEvent ( userA , roomId , { } , '$beacon1' , '$beacon1' ) ;
718724 const beacon2 = makeBeaconInfoEvent ( userB , roomId , { } , '$beacon2' , '$beacon2' ) ;
719725
726+ const mockClient = { decryptEventIfNeeded : jest . fn ( ) } ;
727+
728+ beforeEach ( ( ) => {
729+ mockClient . decryptEventIfNeeded . mockClear ( ) ;
730+ } ) ;
731+
720732 it ( 'does nothing when state has no beacons' , ( ) => {
721733 const emitSpy = jest . spyOn ( state , 'emit' ) ;
722- state . processBeaconEvents ( [ makeBeaconEvent ( userA , { beaconInfoId : '$beacon1' } ) ] ) ;
734+ state . processBeaconEvents ( [ makeBeaconEvent ( userA , { beaconInfoId : '$beacon1' } ) ] , mockClient ) ;
723735 expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
736+ expect ( mockClient . decryptEventIfNeeded ) . not . toHaveBeenCalled ( ) ;
724737 } ) ;
725738
726739 it ( 'does nothing when there are no events' , ( ) => {
727740 state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
728741 const emitSpy = jest . spyOn ( state , 'emit' ) . mockClear ( ) ;
729- state . processBeaconEvents ( [ ] ) ;
742+ state . processBeaconEvents ( [ ] , mockClient ) ;
730743 expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
744+ expect ( mockClient . decryptEventIfNeeded ) . not . toHaveBeenCalled ( ) ;
731745 } ) ;
732746
733- it ( 'discards events for beacons that are not in state' , ( ) => {
734- const location = makeBeaconEvent ( userA , {
735- beaconInfoId : 'some-other-beacon' ,
747+ describe ( 'without encryption' , ( ) => {
748+ it ( 'discards events for beacons that are not in state' , ( ) => {
749+ const location = makeBeaconEvent ( userA , {
750+ beaconInfoId : 'some-other-beacon' ,
751+ } ) ;
752+ const otherRelatedEvent = new MatrixEvent ( {
753+ sender : userA ,
754+ type : EventType . RoomMessage ,
755+ content : {
756+ [ 'm.relates_to' ] : {
757+ event_id : 'whatever' ,
758+ } ,
759+ } ,
760+ } ) ;
761+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
762+ const emitSpy = jest . spyOn ( state , 'emit' ) . mockClear ( ) ;
763+ state . processBeaconEvents ( [ location , otherRelatedEvent ] , mockClient ) ;
764+ expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
765+ } ) ;
766+
767+ it ( 'discards events that are not beacon type' , ( ) => {
768+ // related to beacon1
769+ const otherRelatedEvent = new MatrixEvent ( {
770+ sender : userA ,
771+ type : EventType . RoomMessage ,
772+ content : {
773+ [ 'm.relates_to' ] : {
774+ rel_type : RelationType . Reference ,
775+ event_id : beacon1 . getId ( ) ,
776+ } ,
777+ } ,
778+ } ) ;
779+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
780+ const emitSpy = jest . spyOn ( state , 'emit' ) . mockClear ( ) ;
781+ state . processBeaconEvents ( [ otherRelatedEvent ] , mockClient ) ;
782+ expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
783+ } ) ;
784+
785+ it ( 'adds locations to beacons' , ( ) => {
786+ const location1 = makeBeaconEvent ( userA , {
787+ beaconInfoId : '$beacon1' , timestamp : Date . now ( ) + 1 ,
788+ } ) ;
789+ const location2 = makeBeaconEvent ( userA , {
790+ beaconInfoId : '$beacon1' , timestamp : Date . now ( ) + 2 ,
791+ } ) ;
792+ const location3 = makeBeaconEvent ( userB , {
793+ beaconInfoId : 'some-other-beacon' ,
794+ } ) ;
795+
796+ state . setStateEvents ( [ beacon1 , beacon2 ] , mockClient ) ;
797+
798+ expect ( state . beacons . size ) . toEqual ( 2 ) ;
799+
800+ const beaconInstance = state . beacons . get ( getBeaconInfoIdentifier ( beacon1 ) ) ;
801+ const addLocationsSpy = jest . spyOn ( beaconInstance , 'addLocations' ) ;
802+
803+ state . processBeaconEvents ( [ location1 , location2 , location3 ] , mockClient ) ;
804+
805+ expect ( addLocationsSpy ) . toHaveBeenCalledTimes ( 2 ) ;
806+ // only called with locations for beacon1
807+ expect ( addLocationsSpy ) . toHaveBeenCalledWith ( [ location1 ] ) ;
808+ expect ( addLocationsSpy ) . toHaveBeenCalledWith ( [ location2 ] ) ;
736809 } ) ;
737- state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
738- const emitSpy = jest . spyOn ( state , 'emit' ) . mockClear ( ) ;
739- state . processBeaconEvents ( [ location ] ) ;
740- expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
741810 } ) ;
742811
743- it ( 'adds locations to beacons' , ( ) => {
744- const location1 = makeBeaconEvent ( userA , {
745- beaconInfoId : '$beacon1' , timestamp : Date . now ( ) + 1 ,
812+ describe ( 'with encryption' , ( ) => {
813+ const beacon1RelationContent = { [ 'm.relates_to' ] : {
814+ rel_type : RelationType . Reference ,
815+ event_id : beacon1 . getId ( ) ,
816+ } } ;
817+ const relatedEncryptedEvent = new MatrixEvent ( {
818+ sender : userA ,
819+ type : EventType . RoomMessageEncrypted ,
820+ content : beacon1RelationContent ,
746821 } ) ;
747- const location2 = makeBeaconEvent ( userA , {
748- beaconInfoId : '$beacon1' , timestamp : Date . now ( ) + 2 ,
822+ const decryptingRelatedEvent = new MatrixEvent ( {
823+ sender : userA ,
824+ type : EventType . RoomMessageEncrypted ,
825+ content : beacon1RelationContent ,
749826 } ) ;
750- const location3 = makeBeaconEvent ( userB , {
751- beaconInfoId : 'some-other-beacon' ,
827+ jest . spyOn ( decryptingRelatedEvent , 'isBeingDecrypted' ) . mockReturnValue ( true ) ;
828+
829+ const failedDecryptionRelatedEvent = new MatrixEvent ( {
830+ sender : userA ,
831+ type : EventType . RoomMessageEncrypted ,
832+ content : beacon1RelationContent ,
833+ } ) ;
834+ jest . spyOn ( failedDecryptionRelatedEvent , 'isDecryptionFailure' ) . mockReturnValue ( true ) ;
835+
836+ it ( 'discards events without relations' , ( ) => {
837+ const unrelatedEvent = new MatrixEvent ( {
838+ sender : userA ,
839+ type : EventType . RoomMessageEncrypted ,
840+ } ) ;
841+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
842+ const emitSpy = jest . spyOn ( state , 'emit' ) . mockClear ( ) ;
843+ state . processBeaconEvents ( [ unrelatedEvent ] , mockClient ) ;
844+ expect ( emitSpy ) . not . toHaveBeenCalled ( ) ;
845+ // discard unrelated events early
846+ expect ( mockClient . decryptEventIfNeeded ) . not . toHaveBeenCalled ( ) ;
752847 } ) ;
753848
754- state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
849+ it ( 'discards events for beacons that are not in state' , ( ) => {
850+ const location = makeBeaconEvent ( userA , {
851+ beaconInfoId : 'some-other-beacon' ,
852+ } ) ;
853+ const otherRelatedEvent = new MatrixEvent ( {
854+ sender : userA ,
855+ type : EventType . RoomMessageEncrypted ,
856+ content : {
857+ [ 'm.relates_to' ] : {
858+ rel_type : RelationType . Reference ,
859+ event_id : 'whatever' ,
860+ } ,
861+ } ,
862+ } ) ;
863+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
864+
865+ const beacon = state . beacons . get ( getBeaconInfoIdentifier ( beacon1 ) ) ;
866+ const addLocationsSpy = jest . spyOn ( beacon , 'addLocations' ) . mockClear ( ) ;
867+ state . processBeaconEvents ( [ location , otherRelatedEvent ] , mockClient ) ;
868+ expect ( addLocationsSpy ) . not . toHaveBeenCalled ( ) ;
869+ // discard unrelated events early
870+ expect ( mockClient . decryptEventIfNeeded ) . not . toHaveBeenCalled ( ) ;
871+ } ) ;
872+
873+ it ( 'decrypts related events if needed' , ( ) => {
874+ const location = makeBeaconEvent ( userA , {
875+ beaconInfoId : beacon1 . getId ( ) ,
876+ } ) ;
877+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
878+ state . processBeaconEvents ( [ location , relatedEncryptedEvent ] , mockClient ) ;
879+ // discard unrelated events early
880+ expect ( mockClient . decryptEventIfNeeded ) . toHaveBeenCalledTimes ( 2 ) ;
881+ } ) ;
755882
756- expect ( state . beacons . size ) . toEqual ( 2 ) ;
883+ it ( 'listens for decryption on events that are being decrypted' , ( ) => {
884+ const decryptingRelatedEvent = new MatrixEvent ( {
885+ sender : userA ,
886+ type : EventType . RoomMessageEncrypted ,
887+ content : beacon1RelationContent ,
888+ } ) ;
889+ jest . spyOn ( decryptingRelatedEvent , 'isBeingDecrypted' ) . mockReturnValue ( true ) ;
890+ // spy on event.once
891+ const eventOnceSpy = jest . spyOn ( decryptingRelatedEvent , 'once' ) ;
892+
893+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
894+ state . processBeaconEvents ( [ decryptingRelatedEvent ] , mockClient ) ;
895+
896+ // listener was added
897+ expect ( eventOnceSpy ) . toHaveBeenCalled ( ) ;
898+ } ) ;
757899
758- const beaconInstance = state . beacons . get ( getBeaconInfoIdentifier ( beacon1 ) ) ;
759- const addLocationsSpy = jest . spyOn ( beaconInstance , 'addLocations' ) ;
900+ it ( 'listens for decryption on events that have decryption failure' , ( ) => {
901+ const failedDecryptionRelatedEvent = new MatrixEvent ( {
902+ sender : userA ,
903+ type : EventType . RoomMessageEncrypted ,
904+ content : beacon1RelationContent ,
905+ } ) ;
906+ jest . spyOn ( failedDecryptionRelatedEvent , 'isDecryptionFailure' ) . mockReturnValue ( true ) ;
907+ // spy on event.once
908+ const eventOnceSpy = jest . spyOn ( decryptingRelatedEvent , 'once' ) ;
909+
910+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
911+ state . processBeaconEvents ( [ decryptingRelatedEvent ] , mockClient ) ;
912+
913+ // listener was added
914+ expect ( eventOnceSpy ) . toHaveBeenCalled ( ) ;
915+ } ) ;
760916
761- state . processBeaconEvents ( [ location1 , location2 , location3 ] ) ;
917+ it ( 'discard events that are not m.beacon type after decryption' , ( ) => {
918+ const decryptingRelatedEvent = new MatrixEvent ( {
919+ sender : userA ,
920+ type : EventType . RoomMessageEncrypted ,
921+ content : beacon1RelationContent ,
922+ } ) ;
923+ jest . spyOn ( decryptingRelatedEvent , 'isBeingDecrypted' ) . mockReturnValue ( true ) ;
924+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
925+ const beacon = state . beacons . get ( getBeaconInfoIdentifier ( beacon1 ) ) ;
926+ const addLocationsSpy = jest . spyOn ( beacon , 'addLocations' ) . mockClear ( ) ;
927+ state . processBeaconEvents ( [ decryptingRelatedEvent ] , mockClient ) ;
928+
929+ // this event is a message after decryption
930+ decryptingRelatedEvent . type = EventType . RoomMessage ;
931+ decryptingRelatedEvent . emit ( MatrixEventEvent . Decrypted ) ;
932+
933+ expect ( addLocationsSpy ) . not . toHaveBeenCalled ( ) ;
934+ } ) ;
762935
763- expect ( addLocationsSpy ) . toHaveBeenCalledTimes ( 1 ) ;
764- // only called with locations for beacon1
765- expect ( addLocationsSpy ) . toHaveBeenCalledWith ( [ location1 , location2 ] ) ;
936+ it ( 'adds locations to beacons after decryption' , ( ) => {
937+ const decryptingRelatedEvent = new MatrixEvent ( {
938+ sender : userA ,
939+ type : EventType . RoomMessageEncrypted ,
940+ content : beacon1RelationContent ,
941+ } ) ;
942+ const locationEvent = makeBeaconEvent ( userA , {
943+ beaconInfoId : '$beacon1' , timestamp : Date . now ( ) + 1 ,
944+ } ) ;
945+ jest . spyOn ( decryptingRelatedEvent , 'isBeingDecrypted' ) . mockReturnValue ( true ) ;
946+ state . setStateEvents ( [ beacon1 , beacon2 ] ) ;
947+ const beacon = state . beacons . get ( getBeaconInfoIdentifier ( beacon1 ) ) ;
948+ const addLocationsSpy = jest . spyOn ( beacon , 'addLocations' ) . mockClear ( ) ;
949+ state . processBeaconEvents ( [ decryptingRelatedEvent ] , mockClient ) ;
950+
951+ // update type after '''decryption'''
952+ decryptingRelatedEvent . event . type = M_BEACON . name ;
953+ decryptingRelatedEvent . event . content = locationEvent . content ;
954+ decryptingRelatedEvent . emit ( MatrixEventEvent . Decrypted ) ;
955+
956+ expect ( addLocationsSpy ) . toHaveBeenCalledWith ( [ decryptingRelatedEvent ] ) ;
957+ } ) ;
766958 } ) ;
767959 } ) ;
768960} ) ;
0 commit comments