@@ -166,7 +166,7 @@ describe('OwnBeaconStore', () => {
166166 geolocation = mockGeolocation ( ) ;
167167 mockClient . getVisibleRooms . mockReturnValue ( [ ] ) ;
168168 mockClient . unstable_setLiveBeacon . mockClear ( ) . mockResolvedValue ( { event_id : '1' } ) ;
169- mockClient . sendEvent . mockClear ( ) . mockResolvedValue ( { event_id : '1' } ) ;
169+ mockClient . sendEvent . mockReset ( ) . mockResolvedValue ( { event_id : '1' } ) ;
170170 jest . spyOn ( global . Date , 'now' ) . mockReturnValue ( now ) ;
171171 jest . spyOn ( OwnBeaconStore . instance , 'emit' ) . mockRestore ( ) ;
172172 jest . spyOn ( logger , 'error' ) . mockRestore ( ) ;
@@ -696,7 +696,7 @@ describe('OwnBeaconStore', () => {
696696 } ) ;
697697 } ) ;
698698
699- describe ( 'sending positions' , ( ) => {
699+ describe ( 'publishing positions' , ( ) => {
700700 it ( 'stops watching position when user has no more live beacons' , async ( ) => {
701701 // geolocation is only going to emit 1 position
702702 geolocation . watchPosition . mockImplementation (
@@ -825,6 +825,136 @@ describe('OwnBeaconStore', () => {
825825 } ) ;
826826 } ) ;
827827
828+ describe ( 'when publishing position fails' , ( ) => {
829+ beforeEach ( ( ) => {
830+ geolocation . watchPosition . mockImplementation (
831+ watchPositionMockImplementation ( [ 0 , 1000 , 3000 , 3000 , 3000 ] ) ,
832+ ) ;
833+
834+ // eat expected console error logs
835+ jest . spyOn ( logger , 'error' ) . mockImplementation ( ( ) => { } ) ;
836+ } ) ;
837+
838+ // we need to advance time and then flush promises
839+ // individually for each call to sendEvent
840+ // otherwise the sendEvent doesn't reject/resolve and update state
841+ // before the next call
842+ // advance and flush every 1000ms
843+ // until given ms is 'elapsed'
844+ const advanceAndFlushPromises = async ( timeMs : number ) => {
845+ while ( timeMs > 0 ) {
846+ jest . advanceTimersByTime ( 1000 ) ;
847+ await flushPromisesWithFakeTimers ( ) ;
848+ timeMs -= 1000 ;
849+ }
850+ } ;
851+
852+ it ( 'continues publishing positions after one publish error' , async ( ) => {
853+ // fail to send first event, then succeed
854+ mockClient . sendEvent . mockRejectedValueOnce ( new Error ( 'oups' ) ) . mockResolvedValue ( { event_id : '1' } ) ;
855+ makeRoomsWithStateEvents ( [
856+ alicesRoom1BeaconInfo ,
857+ ] ) ;
858+ const store = await makeOwnBeaconStore ( ) ;
859+ // wait for store to settle
860+ await flushPromisesWithFakeTimers ( ) ;
861+
862+ await advanceAndFlushPromises ( 50000 ) ;
863+
864+ // called for each position from watchPosition
865+ expect ( mockClient . sendEvent ) . toHaveBeenCalledTimes ( 5 ) ;
866+ expect ( store . hasWireError ( alicesRoom1BeaconInfo . getType ( ) ) ) . toBe ( false ) ;
867+ } ) ;
868+
869+ it ( 'continues publishing positions when a beacon fails intermittently' , async ( ) => {
870+ // every second event rejects
871+ // meaning this beacon has more errors than the threshold
872+ // but they are not consecutive
873+ mockClient . sendEvent
874+ . mockRejectedValueOnce ( new Error ( 'oups' ) )
875+ . mockResolvedValueOnce ( { event_id : '1' } )
876+ . mockRejectedValueOnce ( new Error ( 'oups' ) )
877+ . mockResolvedValueOnce ( { event_id : '1' } )
878+ . mockRejectedValueOnce ( new Error ( 'oups' ) ) ;
879+
880+ makeRoomsWithStateEvents ( [
881+ alicesRoom1BeaconInfo ,
882+ ] ) ;
883+ const store = await makeOwnBeaconStore ( ) ;
884+ const emitSpy = jest . spyOn ( store , 'emit' ) ;
885+ // wait for store to settle
886+ await flushPromisesWithFakeTimers ( ) ;
887+
888+ await advanceAndFlushPromises ( 50000 ) ;
889+
890+ // called for each position from watchPosition
891+ expect ( mockClient . sendEvent ) . toHaveBeenCalledTimes ( 5 ) ;
892+ expect ( store . hasWireError ( alicesRoom1BeaconInfo . getType ( ) ) ) . toBe ( false ) ;
893+ expect ( emitSpy ) . not . toHaveBeenCalledWith (
894+ OwnBeaconStoreEvent . WireError , alicesRoom1BeaconInfo . getType ( ) ,
895+ ) ;
896+ } ) ;
897+
898+ it ( 'stops publishing positions when a beacon fails consistently' , async ( ) => {
899+ // always fails to send events
900+ mockClient . sendEvent . mockRejectedValue ( new Error ( 'oups' ) ) ;
901+ makeRoomsWithStateEvents ( [
902+ alicesRoom1BeaconInfo ,
903+ ] ) ;
904+ const store = await makeOwnBeaconStore ( ) ;
905+ const emitSpy = jest . spyOn ( store , 'emit' ) ;
906+ // wait for store to settle
907+ await flushPromisesWithFakeTimers ( ) ;
908+
909+ // 5 positions from watchPosition in this period
910+ await advanceAndFlushPromises ( 50000 ) ;
911+
912+ // only two allowed failures
913+ expect ( mockClient . sendEvent ) . toHaveBeenCalledTimes ( 2 ) ;
914+ expect ( store . hasWireError ( alicesRoom1BeaconInfo . getType ( ) ) ) . toBe ( true ) ;
915+ expect ( emitSpy ) . toHaveBeenCalledWith (
916+ OwnBeaconStoreEvent . WireError , alicesRoom1BeaconInfo . getType ( ) ,
917+ ) ;
918+ } ) ;
919+
920+ it ( 'restarts publishing a beacon after resetting wire error' , async ( ) => {
921+ // always fails to send events
922+ mockClient . sendEvent . mockRejectedValue ( new Error ( 'oups' ) ) ;
923+ makeRoomsWithStateEvents ( [
924+ alicesRoom1BeaconInfo ,
925+ ] ) ;
926+ const store = await makeOwnBeaconStore ( ) ;
927+ const emitSpy = jest . spyOn ( store , 'emit' ) ;
928+ // wait for store to settle
929+ await flushPromisesWithFakeTimers ( ) ;
930+
931+ // 3 positions from watchPosition in this period
932+ await advanceAndFlushPromises ( 4000 ) ;
933+
934+ // only two allowed failures
935+ expect ( mockClient . sendEvent ) . toHaveBeenCalledTimes ( 2 ) ;
936+ expect ( store . hasWireError ( alicesRoom1BeaconInfo . getType ( ) ) ) . toBe ( true ) ;
937+ expect ( emitSpy ) . toHaveBeenCalledWith (
938+ OwnBeaconStoreEvent . WireError , alicesRoom1BeaconInfo . getType ( ) ,
939+ ) ;
940+
941+ // reset emitSpy mock counts to asser on wireError again
942+ emitSpy . mockClear ( ) ;
943+ store . resetWireError ( alicesRoom1BeaconInfo . getType ( ) ) ;
944+
945+ expect ( store . hasWireError ( alicesRoom1BeaconInfo . getType ( ) ) ) . toBe ( false ) ;
946+
947+ // 2 more positions from watchPosition in this period
948+ await advanceAndFlushPromises ( 10000 ) ;
949+
950+ // 2 from before, 2 new ones
951+ expect ( mockClient . sendEvent ) . toHaveBeenCalledTimes ( 4 ) ;
952+ expect ( emitSpy ) . toHaveBeenCalledWith (
953+ OwnBeaconStoreEvent . WireError , alicesRoom1BeaconInfo . getType ( ) ,
954+ ) ;
955+ } ) ;
956+ } ) ;
957+
828958 it ( 'publishes subsequent positions' , async ( ) => {
829959 // modern fake timers + debounce + promises are not friends
830960 // just testing that positions are published
0 commit comments