@@ -71,6 +71,7 @@ const makeRoom = (overrides = {}) => ({
7171 inRoom : new Map ( ) ,
7272 banned : new Map ( ) ,
7373 registered : new Map ( ) ,
74+ membershipRevision : 7 ,
7475 requireRevealedIdentity : false ,
7576 usersLength : 1 ,
7677 authenticate : jest . fn ( ) . mockResolvedValue ( true ) ,
@@ -378,14 +379,15 @@ describe('room namespace departures and moderation', () => {
378379 roomId : room . _id ,
379380 userId : 101 ,
380381 connectionId : 'host-socket' ,
382+ membershipRevision : room . membershipRevision ,
381383 leaveReason : 'explicit' ,
382384 } ) ;
383385
384386 expect ( connect . roomChannel . emit ) . not . toHaveBeenCalledWith ( Protocol . UPDATE_ADMIN , null ) ;
385387 expect ( connect . roomChannel . emit ) . toHaveBeenCalledWith ( Protocol . USER_LEFT , 101 ) ;
386388 } ) ;
387389
388- it ( 'emits metrics and leave events only for the process that claims a departure ' , async ( ) => {
390+ it ( 'does not let a losing departure remove a later rejoin ' , async ( ) => {
389391 const room = makeRoom ( {
390392 private : false ,
391393 password : null ,
@@ -394,41 +396,47 @@ describe('room namespace departures and moderation', () => {
394396 const updatedRoom = {
395397 ...room ,
396398 inRoom : new Map ( [ [ '101' , false ] ] ) ,
399+ membershipRevision : 8 ,
397400 usersLength : 0 ,
398401 doneWithScramble : jest . fn ( ) . mockReturnValue ( false ) ,
399402 } ;
400- let claimed = false ;
401- room . dropUserAtomically = jest . fn ( async ( ) => {
402- if ( claimed ) {
403- return null ;
404- }
405- claimed = true ;
406- return { room : updatedRoom , adminChanged : false } ;
403+ room . dropUserAtomically = jest . fn ( ) . mockResolvedValue ( {
404+ room : updatedRoom ,
405+ adminChanged : false ,
407406 } ) ;
408407 const connect = setup ( new Map ( [ [ room . _id , room ] ] ) ) ;
409- const persistedRoom = {
408+ let persistedRoom = room ;
409+ Room . findById . mockImplementation ( ( ) => queryResult ( persistedRoom ) ) ;
410+ const reconnectGrace = createReconnectGrace . mock . results [ 0 ] . value ;
411+
412+ await expect ( reconnectGrace . finalizeDeparture ( {
413+ roomId : room . _id ,
414+ userId : 101 ,
415+ connectionId : 'winner-socket' ,
416+ membershipRevision : 7 ,
417+ leaveReason : 'explicit' ,
418+ } ) ) . resolves . toBe ( true ) ;
419+
420+ const rejoinedRoom = {
410421 ...room ,
411- inRoom : new Map ( [ [ '101' , false ] ] ) ,
422+ admin : { id : 101 } ,
423+ inRoom : new Map ( [ [ '101' , true ] ] ) ,
424+ membershipRevision : 9 ,
412425 } ;
413- Room . findById . mockImplementation ( ( ) => queryResult ( claimed ? persistedRoom : room ) ) ;
414- const reconnectGrace = createReconnectGrace . mock . results [ 0 ] . value ;
426+ persistedRoom = rejoinedRoom ;
415427
416- await Promise . all ( [
417- reconnectGrace . finalizeDeparture ( {
418- roomId : room . _id ,
419- userId : 101 ,
420- connectionId : 'socket-on-process-a' ,
421- leaveReason : 'explicit' ,
422- } ) ,
423- reconnectGrace . finalizeDeparture ( {
424- roomId : room . _id ,
425- userId : 101 ,
426- connectionId : 'socket-on-process-b' ,
427- leaveReason : 'explicit' ,
428- } ) ,
429- ] ) ;
428+ await expect ( reconnectGrace . finalizeDeparture ( {
429+ roomId : room . _id ,
430+ userId : 101 ,
431+ connectionId : 'loser-socket' ,
432+ membershipRevision : 7 ,
433+ leaveReason : 'explicit' ,
434+ } ) ) . resolves . toBe ( false ) ;
430435
431- expect ( room . dropUserAtomically ) . toHaveBeenCalledTimes ( 2 ) ;
436+ expect ( room . dropUserAtomically ) . toHaveBeenCalledTimes ( 1 ) ;
437+ expect ( rejoinedRoom . inRoom . get ( '101' ) ) . toBe ( true ) ;
438+ expect ( rejoinedRoom . admin ) . toEqual ( { id : 101 } ) ;
439+ expect ( rejoinedRoom . membershipRevision ) . toBe ( updatedRoom . membershipRevision + 1 ) ;
432440 expect ( metrics . endRoomVisit ) . toHaveBeenCalledTimes ( 1 ) ;
433441 expect ( connect . roomChannel . emit ) . toHaveBeenCalledTimes ( 1 ) ;
434442 expect ( connect . roomChannel . emit ) . toHaveBeenCalledWith ( Protocol . USER_LEFT , 101 ) ;
@@ -468,6 +476,7 @@ describe('room namespace departures and moderation', () => {
468476 expect ( reconnectGrace . finalize ) . toHaveBeenCalledWith ( expect . objectContaining ( {
469477 roomId : room . _id ,
470478 userId : 101 ,
479+ membershipRevision : room . membershipRevision ,
471480 leaveReason : 'explicit' ,
472481 } ) ) ;
473482 } ) ;
0 commit comments