mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-21 17:15:38 +03:00
Sliding Sync: Move filters tests to rest layer (#17703)
Move filters tests to rest layer in order to test the new (with sliding sync tables) and fallback paths that Sliding Sync can use. Also found a bug in the new path because it's not being tested which is also fixed in this PR. We now take into account `has_known_state` when filtering. Spawning from https://github.com/element-hq/synapse/pull/17662#discussion_r1755574791. This should have been done when we started using the new sliding sync tables in https://github.com/element-hq/synapse/pull/17630
This commit is contained in:
parent
c5b4be6d07
commit
9b83fb7c16
9 changed files with 1928 additions and 1795 deletions
1
changelog.d/17703.misc
Normal file
1
changelog.d/17703.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Refactor sliding sync filter unit tests so the sliding sync API has better test coverage.
|
|
@ -246,6 +246,7 @@ class SlidingSyncRoomLists:
|
|||
event_pos=change.event_pos,
|
||||
room_version_id=change.room_version_id,
|
||||
# We keep the current state of the room though
|
||||
has_known_state=existing_room.has_known_state,
|
||||
room_type=existing_room.room_type,
|
||||
is_encrypted=existing_room.is_encrypted,
|
||||
)
|
||||
|
@ -270,6 +271,7 @@ class SlidingSyncRoomLists:
|
|||
event_id=change.event_id,
|
||||
event_pos=change.event_pos,
|
||||
room_version_id=change.room_version_id,
|
||||
has_known_state=True,
|
||||
room_type=room_type,
|
||||
is_encrypted=is_encrypted,
|
||||
)
|
||||
|
@ -305,6 +307,7 @@ class SlidingSyncRoomLists:
|
|||
event_id=None,
|
||||
event_pos=newly_left_room_map[room_id],
|
||||
room_version_id=await self.store.get_room_version_id(room_id),
|
||||
has_known_state=True,
|
||||
room_type=room_type,
|
||||
is_encrypted=is_encrypted,
|
||||
)
|
||||
|
@ -1630,12 +1633,14 @@ class SlidingSyncRoomLists:
|
|||
and room_type not in filters.room_types
|
||||
):
|
||||
filtered_room_id_set.remove(room_id)
|
||||
continue
|
||||
|
||||
if (
|
||||
filters.not_room_types is not None
|
||||
and room_type in filters.not_room_types
|
||||
):
|
||||
filtered_room_id_set.remove(room_id)
|
||||
continue
|
||||
|
||||
if filters.room_name_like is not None:
|
||||
with start_active_span("filters.room_name_like"):
|
||||
|
@ -1705,7 +1710,10 @@ class SlidingSyncRoomLists:
|
|||
filtered_room_id_set = {
|
||||
room_id
|
||||
for room_id in filtered_room_id_set
|
||||
if sync_room_map[room_id].is_encrypted == filters.is_encrypted
|
||||
# Remove rooms if we can't figure out what the encryption status is
|
||||
if sync_room_map[room_id].has_known_state
|
||||
# Or remove if it doesn't match the filter
|
||||
and sync_room_map[room_id].is_encrypted == filters.is_encrypted
|
||||
}
|
||||
|
||||
# Filter for rooms that the user has been invited to
|
||||
|
@ -1734,6 +1742,11 @@ class SlidingSyncRoomLists:
|
|||
# Make a copy so we don't run into an error: `Set changed size during
|
||||
# iteration`, when we filter out and remove items
|
||||
for room_id in filtered_room_id_set.copy():
|
||||
# Remove rooms if we can't figure out what room type it is
|
||||
if not sync_room_map[room_id].has_known_state:
|
||||
filtered_room_id_set.remove(room_id)
|
||||
continue
|
||||
|
||||
room_type = sync_room_map[room_id].room_type
|
||||
|
||||
if (
|
||||
|
@ -1741,12 +1754,14 @@ class SlidingSyncRoomLists:
|
|||
and room_type not in filters.room_types
|
||||
):
|
||||
filtered_room_id_set.remove(room_id)
|
||||
continue
|
||||
|
||||
if (
|
||||
filters.not_room_types is not None
|
||||
and room_type in filters.not_room_types
|
||||
):
|
||||
filtered_room_id_set.remove(room_id)
|
||||
continue
|
||||
|
||||
if filters.room_name_like is not None:
|
||||
with start_active_span("filters.room_name_like"):
|
||||
|
|
|
@ -1044,7 +1044,7 @@ class SlidingSyncRestServlet(RestServlet):
|
|||
serialized_rooms[room_id]["heroes"] = serialized_heroes
|
||||
|
||||
# We should only include the `initial` key if it's `True` to save bandwidth.
|
||||
# The absense of this flag means `False`.
|
||||
# The absence of this flag means `False`.
|
||||
if room_result.initial:
|
||||
serialized_rooms[room_id]["initial"] = room_result.initial
|
||||
|
||||
|
|
|
@ -1420,6 +1420,7 @@ class RoomMemberWorkerStore(EventsWorkerStore, CacheInvalidationWorkerStore):
|
|||
SELECT m.room_id, m.sender, m.membership, m.membership_event_id,
|
||||
r.room_version,
|
||||
m.event_instance_name, m.event_stream_ordering,
|
||||
m.has_known_state,
|
||||
COALESCE(j.room_type, m.room_type),
|
||||
COALESCE(j.is_encrypted, m.is_encrypted)
|
||||
FROM sliding_sync_membership_snapshots AS m
|
||||
|
@ -1437,8 +1438,9 @@ class RoomMemberWorkerStore(EventsWorkerStore, CacheInvalidationWorkerStore):
|
|||
event_id=row[3],
|
||||
room_version_id=row[4],
|
||||
event_pos=PersistedEventPosition(row[5], row[6]),
|
||||
room_type=row[7],
|
||||
is_encrypted=bool(row[8]),
|
||||
has_known_state=bool(row[7]),
|
||||
room_type=row[8],
|
||||
is_encrypted=bool(row[9]),
|
||||
)
|
||||
for row in txn
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class RoomsForUserSlidingSync:
|
|||
event_pos: PersistedEventPosition
|
||||
room_version_id: str
|
||||
|
||||
has_known_state: bool
|
||||
room_type: Optional[str]
|
||||
is_encrypted: bool
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
1681
tests/rest/client/sliding_sync/test_lists_filters.py
Normal file
1681
tests/rest/client/sliding_sync/test_lists_filters.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1139,3 +1139,61 @@ class SlidingSyncRoomsMetaTestCase(SlidingSyncBase):
|
|||
self.assertEqual(
|
||||
response_body["rooms"][room_id]["bump_stamp"], invite_pos.stream
|
||||
)
|
||||
|
||||
def test_rooms_meta_is_dm(self) -> None:
|
||||
"""
|
||||
Test `rooms` `is_dm` is correctly set for DM rooms.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
|
||||
# Create a DM room
|
||||
joined_dm_room_id = self._create_dm_room(
|
||||
inviter_user_id=user1_id,
|
||||
inviter_tok=user1_tok,
|
||||
invitee_user_id=user2_id,
|
||||
invitee_tok=user2_tok,
|
||||
should_join_room=True,
|
||||
)
|
||||
invited_dm_room_id = self._create_dm_room(
|
||||
inviter_user_id=user1_id,
|
||||
inviter_tok=user1_tok,
|
||||
invitee_user_id=user2_id,
|
||||
invitee_tok=user2_tok,
|
||||
should_join_room=False,
|
||||
)
|
||||
|
||||
# Create a normal room
|
||||
room_id = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.join(room_id, user1_id, tok=user1_tok)
|
||||
|
||||
# Create a room that user1 is invited to
|
||||
invite_room_id = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.invite(invite_room_id, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
response_body, _ = self.do_sync(sync_body, tok=user1_tok)
|
||||
|
||||
# Ensure DM's are correctly marked
|
||||
self.assertDictEqual(
|
||||
{
|
||||
room_id: room.get("is_dm")
|
||||
for room_id, room in response_body["rooms"].items()
|
||||
},
|
||||
{
|
||||
invite_room_id: None,
|
||||
room_id: None,
|
||||
invited_dm_room_id: True,
|
||||
joined_dm_room_id: True,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -23,11 +23,12 @@ from twisted.test.proto_helpers import MemoryReactor
|
|||
import synapse.rest.admin
|
||||
from synapse.api.constants import (
|
||||
AccountDataTypes,
|
||||
EventContentFields,
|
||||
EventTypes,
|
||||
RoomTypes,
|
||||
Membership,
|
||||
)
|
||||
from synapse.events import EventBase
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.events import EventBase, StrippedStateEvent, make_event_from_dict
|
||||
from synapse.events.snapshot import EventContext
|
||||
from synapse.rest.client import devices, login, receipts, room, sync
|
||||
from synapse.server import HomeServer
|
||||
from synapse.types import (
|
||||
|
@ -141,6 +142,167 @@ class SlidingSyncBase(unittest.HomeserverTestCase):
|
|||
message=str(actual_required_state),
|
||||
)
|
||||
|
||||
def _add_new_dm_to_global_account_data(
|
||||
self, source_user_id: str, target_user_id: str, target_room_id: str
|
||||
) -> None:
|
||||
"""
|
||||
Helper to handle inserting a new DM for the source user into global account data
|
||||
(handles all of the list merging).
|
||||
|
||||
Args:
|
||||
source_user_id: The user ID of the DM mapping we're going to update
|
||||
target_user_id: User ID of the person the DM is with
|
||||
target_room_id: Room ID of the DM
|
||||
"""
|
||||
store = self.hs.get_datastores().main
|
||||
|
||||
# Get the current DM map
|
||||
existing_dm_map = self.get_success(
|
||||
store.get_global_account_data_by_type_for_user(
|
||||
source_user_id, AccountDataTypes.DIRECT
|
||||
)
|
||||
)
|
||||
# Scrutinize the account data since it has no concrete type. We're just copying
|
||||
# everything into a known type. It should be a mapping from user ID to a list of
|
||||
# room IDs. Ignore anything else.
|
||||
new_dm_map: Dict[str, List[str]] = {}
|
||||
if isinstance(existing_dm_map, dict):
|
||||
for user_id, room_ids in existing_dm_map.items():
|
||||
if isinstance(user_id, str) and isinstance(room_ids, list):
|
||||
for room_id in room_ids:
|
||||
if isinstance(room_id, str):
|
||||
new_dm_map[user_id] = new_dm_map.get(user_id, []) + [
|
||||
room_id
|
||||
]
|
||||
|
||||
# Add the new DM to the map
|
||||
new_dm_map[target_user_id] = new_dm_map.get(target_user_id, []) + [
|
||||
target_room_id
|
||||
]
|
||||
# Save the DM map to global account data
|
||||
self.get_success(
|
||||
store.add_account_data_for_user(
|
||||
source_user_id,
|
||||
AccountDataTypes.DIRECT,
|
||||
new_dm_map,
|
||||
)
|
||||
)
|
||||
|
||||
def _create_dm_room(
|
||||
self,
|
||||
inviter_user_id: str,
|
||||
inviter_tok: str,
|
||||
invitee_user_id: str,
|
||||
invitee_tok: str,
|
||||
should_join_room: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Helper to create a DM room as the "inviter" and invite the "invitee" user to the
|
||||
room. The "invitee" user also will join the room. The `m.direct` account data
|
||||
will be set for both users.
|
||||
"""
|
||||
# Create a room and send an invite the other user
|
||||
room_id = self.helper.create_room_as(
|
||||
inviter_user_id,
|
||||
is_public=False,
|
||||
tok=inviter_tok,
|
||||
)
|
||||
self.helper.invite(
|
||||
room_id,
|
||||
src=inviter_user_id,
|
||||
targ=invitee_user_id,
|
||||
tok=inviter_tok,
|
||||
extra_data={"is_direct": True},
|
||||
)
|
||||
if should_join_room:
|
||||
# Person that was invited joins the room
|
||||
self.helper.join(room_id, invitee_user_id, tok=invitee_tok)
|
||||
|
||||
# Mimic the client setting the room as a direct message in the global account
|
||||
# data for both users.
|
||||
self._add_new_dm_to_global_account_data(
|
||||
invitee_user_id, inviter_user_id, room_id
|
||||
)
|
||||
self._add_new_dm_to_global_account_data(
|
||||
inviter_user_id, invitee_user_id, room_id
|
||||
)
|
||||
|
||||
return room_id
|
||||
|
||||
_remote_invite_count: int = 0
|
||||
|
||||
def _create_remote_invite_room_for_user(
|
||||
self,
|
||||
invitee_user_id: str,
|
||||
unsigned_invite_room_state: Optional[List[StrippedStateEvent]],
|
||||
) -> str:
|
||||
"""
|
||||
Create a fake invite for a remote room and persist it.
|
||||
|
||||
We don't have any state for these kind of rooms and can only rely on the
|
||||
stripped state included in the unsigned portion of the invite event to identify
|
||||
the room.
|
||||
|
||||
Args:
|
||||
invitee_user_id: The person being invited
|
||||
unsigned_invite_room_state: List of stripped state events to assist the
|
||||
receiver in identifying the room.
|
||||
|
||||
Returns:
|
||||
The room ID of the remote invite room
|
||||
"""
|
||||
store = self.hs.get_datastores().main
|
||||
|
||||
invite_room_id = f"!test_room{self._remote_invite_count}:remote_server"
|
||||
|
||||
invite_event_dict = {
|
||||
"room_id": invite_room_id,
|
||||
"sender": "@inviter:remote_server",
|
||||
"state_key": invitee_user_id,
|
||||
"depth": 1,
|
||||
"origin_server_ts": 1,
|
||||
"type": EventTypes.Member,
|
||||
"content": {"membership": Membership.INVITE},
|
||||
"auth_events": [],
|
||||
"prev_events": [],
|
||||
}
|
||||
if unsigned_invite_room_state is not None:
|
||||
serialized_stripped_state_events = []
|
||||
for stripped_event in unsigned_invite_room_state:
|
||||
serialized_stripped_state_events.append(
|
||||
{
|
||||
"type": stripped_event.type,
|
||||
"state_key": stripped_event.state_key,
|
||||
"sender": stripped_event.sender,
|
||||
"content": stripped_event.content,
|
||||
}
|
||||
)
|
||||
|
||||
invite_event_dict["unsigned"] = {
|
||||
"invite_room_state": serialized_stripped_state_events
|
||||
}
|
||||
|
||||
invite_event = make_event_from_dict(
|
||||
invite_event_dict,
|
||||
room_version=RoomVersions.V10,
|
||||
)
|
||||
invite_event.internal_metadata.outlier = True
|
||||
invite_event.internal_metadata.out_of_band_membership = True
|
||||
|
||||
self.get_success(
|
||||
store.maybe_store_room_on_outlier_membership(
|
||||
room_id=invite_room_id, room_version=invite_event.room_version
|
||||
)
|
||||
)
|
||||
context = EventContext.for_outlier(self.hs.get_storage_controllers())
|
||||
persist_controller = self.hs.get_storage_controllers().persistence
|
||||
assert persist_controller is not None
|
||||
self.get_success(persist_controller.persist_event(invite_event, context))
|
||||
|
||||
self._remote_invite_count += 1
|
||||
|
||||
return invite_room_id
|
||||
|
||||
def _bump_notifier_wait_for_events(
|
||||
self,
|
||||
user_id: str,
|
||||
|
@ -261,93 +423,6 @@ class SlidingSyncTestCase(SlidingSyncBase):
|
|||
|
||||
super().prepare(reactor, clock, hs)
|
||||
|
||||
def _add_new_dm_to_global_account_data(
|
||||
self, source_user_id: str, target_user_id: str, target_room_id: str
|
||||
) -> None:
|
||||
"""
|
||||
Helper to handle inserting a new DM for the source user into global account data
|
||||
(handles all of the list merging).
|
||||
|
||||
Args:
|
||||
source_user_id: The user ID of the DM mapping we're going to update
|
||||
target_user_id: User ID of the person the DM is with
|
||||
target_room_id: Room ID of the DM
|
||||
"""
|
||||
|
||||
# Get the current DM map
|
||||
existing_dm_map = self.get_success(
|
||||
self.store.get_global_account_data_by_type_for_user(
|
||||
source_user_id, AccountDataTypes.DIRECT
|
||||
)
|
||||
)
|
||||
# Scrutinize the account data since it has no concrete type. We're just copying
|
||||
# everything into a known type. It should be a mapping from user ID to a list of
|
||||
# room IDs. Ignore anything else.
|
||||
new_dm_map: Dict[str, List[str]] = {}
|
||||
if isinstance(existing_dm_map, dict):
|
||||
for user_id, room_ids in existing_dm_map.items():
|
||||
if isinstance(user_id, str) and isinstance(room_ids, list):
|
||||
for room_id in room_ids:
|
||||
if isinstance(room_id, str):
|
||||
new_dm_map[user_id] = new_dm_map.get(user_id, []) + [
|
||||
room_id
|
||||
]
|
||||
|
||||
# Add the new DM to the map
|
||||
new_dm_map[target_user_id] = new_dm_map.get(target_user_id, []) + [
|
||||
target_room_id
|
||||
]
|
||||
# Save the DM map to global account data
|
||||
self.get_success(
|
||||
self.store.add_account_data_for_user(
|
||||
source_user_id,
|
||||
AccountDataTypes.DIRECT,
|
||||
new_dm_map,
|
||||
)
|
||||
)
|
||||
|
||||
def _create_dm_room(
|
||||
self,
|
||||
inviter_user_id: str,
|
||||
inviter_tok: str,
|
||||
invitee_user_id: str,
|
||||
invitee_tok: str,
|
||||
should_join_room: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Helper to create a DM room as the "inviter" and invite the "invitee" user to the
|
||||
room. The "invitee" user also will join the room. The `m.direct` account data
|
||||
will be set for both users.
|
||||
"""
|
||||
|
||||
# Create a room and send an invite the other user
|
||||
room_id = self.helper.create_room_as(
|
||||
inviter_user_id,
|
||||
is_public=False,
|
||||
tok=inviter_tok,
|
||||
)
|
||||
self.helper.invite(
|
||||
room_id,
|
||||
src=inviter_user_id,
|
||||
targ=invitee_user_id,
|
||||
tok=inviter_tok,
|
||||
extra_data={"is_direct": True},
|
||||
)
|
||||
if should_join_room:
|
||||
# Person that was invited joins the room
|
||||
self.helper.join(room_id, invitee_user_id, tok=invitee_tok)
|
||||
|
||||
# Mimic the client setting the room as a direct message in the global account
|
||||
# data for both users.
|
||||
self._add_new_dm_to_global_account_data(
|
||||
invitee_user_id, inviter_user_id, room_id
|
||||
)
|
||||
self._add_new_dm_to_global_account_data(
|
||||
inviter_user_id, invitee_user_id, room_id
|
||||
)
|
||||
|
||||
return room_id
|
||||
|
||||
def test_sync_list(self) -> None:
|
||||
"""
|
||||
Test that room IDs show up in the Sliding Sync `lists`
|
||||
|
@ -547,323 +622,6 @@ class SlidingSyncTestCase(SlidingSyncBase):
|
|||
# There should be no room sent down.
|
||||
self.assertFalse(channel.json_body["rooms"])
|
||||
|
||||
def test_filter_list(self) -> None:
|
||||
"""
|
||||
Test that filters apply to `lists`
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
|
||||
# Create a DM room
|
||||
joined_dm_room_id = self._create_dm_room(
|
||||
inviter_user_id=user1_id,
|
||||
inviter_tok=user1_tok,
|
||||
invitee_user_id=user2_id,
|
||||
invitee_tok=user2_tok,
|
||||
should_join_room=True,
|
||||
)
|
||||
invited_dm_room_id = self._create_dm_room(
|
||||
inviter_user_id=user1_id,
|
||||
inviter_tok=user1_tok,
|
||||
invitee_user_id=user2_id,
|
||||
invitee_tok=user2_tok,
|
||||
should_join_room=False,
|
||||
)
|
||||
|
||||
# Create a normal room
|
||||
room_id = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.join(room_id, user1_id, tok=user1_tok)
|
||||
|
||||
# Create a room that user1 is invited to
|
||||
invite_room_id = self.helper.create_room_as(user2_id, tok=user2_tok)
|
||||
self.helper.invite(invite_room_id, src=user2_id, targ=user1_id, tok=user2_tok)
|
||||
|
||||
# Make the Sliding Sync request
|
||||
sync_body = {
|
||||
"lists": {
|
||||
# Absense of filters does not imply "False" values
|
||||
"all": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {},
|
||||
},
|
||||
# Test single truthy filter
|
||||
"dms": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {"is_dm": True},
|
||||
},
|
||||
# Test single falsy filter
|
||||
"non-dms": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {"is_dm": False},
|
||||
},
|
||||
# Test how multiple filters should stack (AND'd together)
|
||||
"room-invites": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {"is_dm": False, "is_invite": True},
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, _ = self.do_sync(sync_body, tok=user1_tok)
|
||||
|
||||
# Make sure it has the foo-list we requested
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"].keys()),
|
||||
["all", "dms", "non-dms", "room-invites"],
|
||||
response_body["lists"].keys(),
|
||||
)
|
||||
|
||||
# Make sure the lists have the correct rooms
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["all"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [
|
||||
invite_room_id,
|
||||
room_id,
|
||||
invited_dm_room_id,
|
||||
joined_dm_room_id,
|
||||
],
|
||||
}
|
||||
],
|
||||
list(response_body["lists"]["all"]),
|
||||
)
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["dms"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [invited_dm_room_id, joined_dm_room_id],
|
||||
}
|
||||
],
|
||||
list(response_body["lists"]["dms"]),
|
||||
)
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["non-dms"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [invite_room_id, room_id],
|
||||
}
|
||||
],
|
||||
list(response_body["lists"]["non-dms"]),
|
||||
)
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["room-invites"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [invite_room_id],
|
||||
}
|
||||
],
|
||||
list(response_body["lists"]["room-invites"]),
|
||||
)
|
||||
|
||||
# Ensure DM's are correctly marked
|
||||
self.assertDictEqual(
|
||||
{
|
||||
room_id: room.get("is_dm")
|
||||
for room_id, room in response_body["rooms"].items()
|
||||
},
|
||||
{
|
||||
invite_room_id: None,
|
||||
room_id: None,
|
||||
invited_dm_room_id: True,
|
||||
joined_dm_room_id: True,
|
||||
},
|
||||
)
|
||||
|
||||
def test_filter_regardless_of_membership_server_left_room(self) -> None:
|
||||
"""
|
||||
Test that filters apply to rooms regardless of membership. We're also
|
||||
compounding the problem by having all of the local users leave the room causing
|
||||
our server to leave the room.
|
||||
|
||||
We want to make sure that if someone is filtering rooms, and leaves, you still
|
||||
get that final update down sync that you left.
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
user2_id = self.register_user("user2", "pass")
|
||||
user2_tok = self.login(user2_id, "pass")
|
||||
|
||||
# Create a normal room
|
||||
room_id = self.helper.create_room_as(user1_id, tok=user2_tok)
|
||||
self.helper.join(room_id, user1_id, tok=user1_tok)
|
||||
|
||||
# Create an encrypted space room
|
||||
space_room_id = self.helper.create_room_as(
|
||||
user2_id,
|
||||
tok=user2_tok,
|
||||
extra_content={
|
||||
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
|
||||
},
|
||||
)
|
||||
self.helper.send_state(
|
||||
space_room_id,
|
||||
EventTypes.RoomEncryption,
|
||||
{EventContentFields.ENCRYPTION_ALGORITHM: "m.megolm.v1.aes-sha2"},
|
||||
tok=user2_tok,
|
||||
)
|
||||
self.helper.join(space_room_id, user1_id, tok=user1_tok)
|
||||
|
||||
# Make an initial Sliding Sync request
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"all-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
"filters": {},
|
||||
},
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {
|
||||
"is_encrypted": True,
|
||||
"room_types": [RoomTypes.SPACE],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, from_token = self.do_sync(sync_body, tok=user1_tok)
|
||||
|
||||
# Make sure the response has the lists we requested
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"].keys()),
|
||||
["all-list", "foo-list"],
|
||||
response_body["lists"].keys(),
|
||||
)
|
||||
|
||||
# Make sure the lists have the correct rooms
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["all-list"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [space_room_id, room_id],
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["foo-list"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [space_room_id],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
# Everyone leaves the encrypted space room
|
||||
self.helper.leave(space_room_id, user1_id, tok=user1_tok)
|
||||
self.helper.leave(space_room_id, user2_id, tok=user2_tok)
|
||||
|
||||
# Make an incremental Sliding Sync request
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"all-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
"filters": {},
|
||||
},
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 1,
|
||||
"filters": {
|
||||
"is_encrypted": True,
|
||||
"room_types": [RoomTypes.SPACE],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, _ = self.do_sync(sync_body, since=from_token, tok=user1_tok)
|
||||
|
||||
# Make sure the lists have the correct rooms even though we `newly_left`
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["all-list"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [space_room_id, room_id],
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertListEqual(
|
||||
list(response_body["lists"]["foo-list"]["ops"]),
|
||||
[
|
||||
{
|
||||
"op": "SYNC",
|
||||
"range": [0, 99],
|
||||
"room_ids": [space_room_id],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def test_filter_is_encrypted_up_to_date(self) -> None:
|
||||
"""
|
||||
Make sure we get up-to-date `is_encrypted` status for a joined room
|
||||
"""
|
||||
user1_id = self.register_user("user1", "pass")
|
||||
user1_tok = self.login(user1_id, "pass")
|
||||
|
||||
room_id = self.helper.create_room_as(user1_id, tok=user1_tok)
|
||||
|
||||
sync_body = {
|
||||
"lists": {
|
||||
"foo-list": {
|
||||
"ranges": [[0, 99]],
|
||||
"required_state": [],
|
||||
"timeline_limit": 0,
|
||||
"filters": {
|
||||
"is_encrypted": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
response_body, from_token = self.do_sync(sync_body, tok=user1_tok)
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
set(),
|
||||
exact=True,
|
||||
)
|
||||
|
||||
# Update the encryption status
|
||||
self.helper.send_state(
|
||||
room_id,
|
||||
EventTypes.RoomEncryption,
|
||||
{EventContentFields.ENCRYPTION_ALGORITHM: "m.megolm.v1.aes-sha2"},
|
||||
tok=user1_tok,
|
||||
)
|
||||
|
||||
# We should see the room now because it's encrypted
|
||||
response_body, _ = self.do_sync(sync_body, since=from_token, tok=user1_tok)
|
||||
self.assertIncludes(
|
||||
set(response_body["lists"]["foo-list"]["ops"][0]["room_ids"]),
|
||||
{room_id},
|
||||
exact=True,
|
||||
)
|
||||
|
||||
def test_forgotten_up_to_date(self) -> None:
|
||||
"""
|
||||
Make sure we get up-to-date `forgotten` status for rooms
|
||||
|
|
Loading…
Reference in a new issue