1111# See the GNU Affero General Public License for more details:
1212# <https://www.gnu.org/licenses/agpl-3.0.html>.
1313#
14+ import enum
1415import logging
1516
1617from parameterized import parameterized , parameterized_class
1718
1819from twisted .test .proto_helpers import MemoryReactor
1920
2021import synapse .rest .admin
21- from synapse .api .constants import EventTypes , Membership
22+ from synapse .api .constants import EventContentFields , EventTypes , JoinRules , Membership
2223from synapse .handlers .sliding_sync import StateValues
23- from synapse .rest .client import login , room , sync
24+ from synapse .rest .client import knock , login , room , sync
2425from synapse .server import HomeServer
2526from synapse .util import Clock
2627
3031logger = logging .getLogger (__name__ )
3132
3233
34+ # Inherit from `str` so that they show up in the test description when we
35+ # `@parameterized.expand(...)` the first parameter
36+ class MembershipAction (str , enum .Enum ):
37+ INVITE = "invite"
38+ JOIN = "join"
39+ KNOCK = "knock"
40+ LEAVE = "leave"
41+ BAN = "ban"
42+ KICK = "kick"
43+
44+
3345# FIXME: This can be removed once we bump `SCHEMA_COMPAT_VERSION` and run the
3446# foreground update for
3547# `sliding_sync_joined_rooms`/`sliding_sync_membership_snapshots` (tracked by
@@ -52,6 +64,7 @@ class SlidingSyncRoomsRequiredStateTestCase(SlidingSyncBase):
5264 servlets = [
5365 synapse .rest .admin .register_servlets ,
5466 login .register_servlets ,
67+ knock .register_servlets ,
5568 room .register_servlets ,
5669 sync .register_servlets ,
5770 ]
@@ -496,6 +509,153 @@ def test_rooms_required_state_lazy_loading_room_members_incremental_sync(
496509 )
497510 self .assertIsNone (response_body ["rooms" ][room_id1 ].get ("invite_state" ))
498511
512+ @parameterized .expand (
513+ [
514+ (MembershipAction .LEAVE ,),
515+ (MembershipAction .INVITE ,),
516+ (MembershipAction .KNOCK ,),
517+ (MembershipAction .JOIN ,),
518+ (MembershipAction .BAN ,),
519+ (MembershipAction .KICK ,),
520+ ]
521+ )
522+ def test_rooms_required_state_changed_membership_in_timeline_lazy_loading_room_members_incremental_sync (
523+ self ,
524+ room_membership_action : str ,
525+ ) -> None :
526+ """
527+ On incremental sync, test `rooms.required_state` returns people relevant to the
528+ timeline when lazy-loading room members, `["m.room.member","$LAZY"]` **including
529+ changes to membership**.
530+ """
531+ user1_id = self .register_user ("user1" , "pass" )
532+ user1_tok = self .login (user1_id , "pass" )
533+ user2_id = self .register_user ("user2" , "pass" )
534+ user2_tok = self .login (user2_id , "pass" )
535+ user3_id = self .register_user ("user3" , "pass" )
536+ user3_tok = self .login (user3_id , "pass" )
537+ user4_id = self .register_user ("user4" , "pass" )
538+ user4_tok = self .login (user4_id , "pass" )
539+ user5_id = self .register_user ("user5" , "pass" )
540+ user5_tok = self .login (user5_id , "pass" )
541+
542+ room_id1 = self .helper .create_room_as (user2_id , tok = user2_tok , is_public = True )
543+ # If we're testing knocks, set the room to knock
544+ if room_membership_action == MembershipAction .KNOCK :
545+ self .helper .send_state (
546+ room_id1 ,
547+ EventTypes .JoinRules ,
548+ {"join_rule" : JoinRules .KNOCK },
549+ tok = user2_tok ,
550+ )
551+
552+ # Join the test users to the room
553+ self .helper .invite (room_id1 , src = user2_id , targ = user1_id , tok = user2_tok )
554+ self .helper .join (room_id1 , user1_id , tok = user1_tok )
555+ self .helper .invite (room_id1 , src = user2_id , targ = user3_id , tok = user2_tok )
556+ self .helper .join (room_id1 , user3_id , tok = user3_tok )
557+ self .helper .invite (room_id1 , src = user2_id , targ = user4_id , tok = user2_tok )
558+ self .helper .join (room_id1 , user4_id , tok = user4_tok )
559+ if room_membership_action in (
560+ MembershipAction .LEAVE ,
561+ MembershipAction .BAN ,
562+ MembershipAction .JOIN ,
563+ ):
564+ self .helper .invite (room_id1 , src = user2_id , targ = user5_id , tok = user2_tok )
565+ self .helper .join (room_id1 , user5_id , tok = user5_tok )
566+
567+ # Send some messages to fill up the space
568+ self .helper .send (room_id1 , "1" , tok = user2_tok )
569+ self .helper .send (room_id1 , "2" , tok = user2_tok )
570+ self .helper .send (room_id1 , "3" , tok = user2_tok )
571+
572+ # Make the Sliding Sync request with lazy loading for the room members
573+ sync_body = {
574+ "lists" : {
575+ "foo-list" : {
576+ "ranges" : [[0 , 1 ]],
577+ "required_state" : [
578+ [EventTypes .Create , "" ],
579+ [EventTypes .Member , StateValues .LAZY ],
580+ ],
581+ "timeline_limit" : 3 ,
582+ }
583+ }
584+ }
585+ response_body , from_token = self .do_sync (sync_body , tok = user1_tok )
586+
587+ # Send more timeline events into the room
588+ self .helper .send (room_id1 , "4" , tok = user2_tok )
589+ self .helper .send (room_id1 , "5" , tok = user4_tok )
590+ # The third event will be our membership event concerning user5
591+ if room_membership_action == MembershipAction .LEAVE :
592+ # User 5 leaves
593+ self .helper .leave (room_id1 , user5_id , tok = user5_tok )
594+ elif room_membership_action == MembershipAction .INVITE :
595+ # User 5 is invited
596+ self .helper .invite (room_id1 , src = user2_id , targ = user5_id , tok = user2_tok )
597+ elif room_membership_action == MembershipAction .KNOCK :
598+ # User 5 knocks
599+ self .helper .knock (room_id1 , user5_id , tok = user5_tok )
600+ # The admin of the room accepts the knock
601+ self .helper .invite (room_id1 , src = user2_id , targ = user5_id , tok = user2_tok )
602+ elif room_membership_action == MembershipAction .JOIN :
603+ # Update the display name of user5 (causing a membership change)
604+ self .helper .send_state (
605+ room_id1 ,
606+ event_type = EventTypes .Member ,
607+ state_key = user5_id ,
608+ body = {
609+ EventContentFields .MEMBERSHIP : Membership .JOIN ,
610+ EventContentFields .MEMBERSHIP_DISPLAYNAME : "quick changer" ,
611+ },
612+ tok = user5_tok ,
613+ )
614+ elif room_membership_action == MembershipAction .BAN :
615+ self .helper .ban (room_id1 , src = user2_id , targ = user5_id , tok = user2_tok )
616+ elif room_membership_action == MembershipAction .KICK :
617+ # Kick user5 from the room
618+ self .helper .change_membership (
619+ room = room_id1 ,
620+ src = user2_id ,
621+ targ = user5_id ,
622+ tok = user2_tok ,
623+ membership = Membership .LEAVE ,
624+ extra_data = {
625+ "reason" : "Bad manners" ,
626+ },
627+ )
628+ else :
629+ raise AssertionError (
630+ f"Unknown room_membership_action: { room_membership_action } "
631+ )
632+
633+ # Make an incremental Sliding Sync request
634+ response_body , _ = self .do_sync (sync_body , since = from_token , tok = user1_tok )
635+
636+ state_map = self .get_success (
637+ self .storage_controllers .state .get_current_state (room_id1 )
638+ )
639+
640+ # Only user2, user4, and user5 sent events in the last 3 events we see in the
641+ # `timeline`.
642+ self ._assertRequiredStateIncludes (
643+ response_body ["rooms" ][room_id1 ]["required_state" ],
644+ {
645+ # This appears because *some* membership in the room changed and the
646+ # heroes are recalculated and is thrown in because we have it. But this
647+ # is technically optional and not needed because we've already seen user2
648+ # in the last sync (and their membership hasn't changed).
649+ state_map [(EventTypes .Member , user2_id )],
650+ # Appears because there is a message in the timeline from this user
651+ state_map [(EventTypes .Member , user4_id )],
652+ # Appears because there is a membership event in the timeline from this user
653+ state_map [(EventTypes .Member , user5_id )],
654+ },
655+ exact = True ,
656+ )
657+ self .assertIsNone (response_body ["rooms" ][room_id1 ].get ("invite_state" ))
658+
499659 def test_rooms_required_state_expand_lazy_loading_room_members_incremental_sync (
500660 self ,
501661 ) -> None :
@@ -1243,7 +1403,7 @@ def test_rooms_required_state_expand_retract_expand(self) -> None:
12431403
12441404 # Update the room name
12451405 self .helper .send_state (
1246- room_id1 , "m.room.name" , {"name" : "Bar" }, state_key = "" , tok = user1_tok
1406+ room_id1 , EventTypes . Name , {"name" : "Bar" }, state_key = "" , tok = user1_tok
12471407 )
12481408
12491409 # Update the sliding sync requests to exclude the room name again
0 commit comments