mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-14 11:57:44 +00: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,
|
event_pos=change.event_pos,
|
||||||
room_version_id=change.room_version_id,
|
room_version_id=change.room_version_id,
|
||||||
# We keep the current state of the room though
|
# We keep the current state of the room though
|
||||||
|
has_known_state=existing_room.has_known_state,
|
||||||
room_type=existing_room.room_type,
|
room_type=existing_room.room_type,
|
||||||
is_encrypted=existing_room.is_encrypted,
|
is_encrypted=existing_room.is_encrypted,
|
||||||
)
|
)
|
||||||
|
@ -270,6 +271,7 @@ class SlidingSyncRoomLists:
|
||||||
event_id=change.event_id,
|
event_id=change.event_id,
|
||||||
event_pos=change.event_pos,
|
event_pos=change.event_pos,
|
||||||
room_version_id=change.room_version_id,
|
room_version_id=change.room_version_id,
|
||||||
|
has_known_state=True,
|
||||||
room_type=room_type,
|
room_type=room_type,
|
||||||
is_encrypted=is_encrypted,
|
is_encrypted=is_encrypted,
|
||||||
)
|
)
|
||||||
|
@ -305,6 +307,7 @@ class SlidingSyncRoomLists:
|
||||||
event_id=None,
|
event_id=None,
|
||||||
event_pos=newly_left_room_map[room_id],
|
event_pos=newly_left_room_map[room_id],
|
||||||
room_version_id=await self.store.get_room_version_id(room_id),
|
room_version_id=await self.store.get_room_version_id(room_id),
|
||||||
|
has_known_state=True,
|
||||||
room_type=room_type,
|
room_type=room_type,
|
||||||
is_encrypted=is_encrypted,
|
is_encrypted=is_encrypted,
|
||||||
)
|
)
|
||||||
|
@ -1630,12 +1633,14 @@ class SlidingSyncRoomLists:
|
||||||
and room_type not in filters.room_types
|
and room_type not in filters.room_types
|
||||||
):
|
):
|
||||||
filtered_room_id_set.remove(room_id)
|
filtered_room_id_set.remove(room_id)
|
||||||
|
continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
filters.not_room_types is not None
|
filters.not_room_types is not None
|
||||||
and room_type in filters.not_room_types
|
and room_type in filters.not_room_types
|
||||||
):
|
):
|
||||||
filtered_room_id_set.remove(room_id)
|
filtered_room_id_set.remove(room_id)
|
||||||
|
continue
|
||||||
|
|
||||||
if filters.room_name_like is not None:
|
if filters.room_name_like is not None:
|
||||||
with start_active_span("filters.room_name_like"):
|
with start_active_span("filters.room_name_like"):
|
||||||
|
@ -1705,7 +1710,10 @@ class SlidingSyncRoomLists:
|
||||||
filtered_room_id_set = {
|
filtered_room_id_set = {
|
||||||
room_id
|
room_id
|
||||||
for room_id in filtered_room_id_set
|
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
|
# 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
|
# Make a copy so we don't run into an error: `Set changed size during
|
||||||
# iteration`, when we filter out and remove items
|
# iteration`, when we filter out and remove items
|
||||||
for room_id in filtered_room_id_set.copy():
|
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
|
room_type = sync_room_map[room_id].room_type
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1741,12 +1754,14 @@ class SlidingSyncRoomLists:
|
||||||
and room_type not in filters.room_types
|
and room_type not in filters.room_types
|
||||||
):
|
):
|
||||||
filtered_room_id_set.remove(room_id)
|
filtered_room_id_set.remove(room_id)
|
||||||
|
continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
filters.not_room_types is not None
|
filters.not_room_types is not None
|
||||||
and room_type in filters.not_room_types
|
and room_type in filters.not_room_types
|
||||||
):
|
):
|
||||||
filtered_room_id_set.remove(room_id)
|
filtered_room_id_set.remove(room_id)
|
||||||
|
continue
|
||||||
|
|
||||||
if filters.room_name_like is not None:
|
if filters.room_name_like is not None:
|
||||||
with start_active_span("filters.room_name_like"):
|
with start_active_span("filters.room_name_like"):
|
||||||
|
|
|
@ -1044,7 +1044,7 @@ class SlidingSyncRestServlet(RestServlet):
|
||||||
serialized_rooms[room_id]["heroes"] = serialized_heroes
|
serialized_rooms[room_id]["heroes"] = serialized_heroes
|
||||||
|
|
||||||
# We should only include the `initial` key if it's `True` to save bandwidth.
|
# 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:
|
if room_result.initial:
|
||||||
serialized_rooms[room_id]["initial"] = 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,
|
SELECT m.room_id, m.sender, m.membership, m.membership_event_id,
|
||||||
r.room_version,
|
r.room_version,
|
||||||
m.event_instance_name, m.event_stream_ordering,
|
m.event_instance_name, m.event_stream_ordering,
|
||||||
|
m.has_known_state,
|
||||||
COALESCE(j.room_type, m.room_type),
|
COALESCE(j.room_type, m.room_type),
|
||||||
COALESCE(j.is_encrypted, m.is_encrypted)
|
COALESCE(j.is_encrypted, m.is_encrypted)
|
||||||
FROM sliding_sync_membership_snapshots AS m
|
FROM sliding_sync_membership_snapshots AS m
|
||||||
|
@ -1437,8 +1438,9 @@ class RoomMemberWorkerStore(EventsWorkerStore, CacheInvalidationWorkerStore):
|
||||||
event_id=row[3],
|
event_id=row[3],
|
||||||
room_version_id=row[4],
|
room_version_id=row[4],
|
||||||
event_pos=PersistedEventPosition(row[5], row[6]),
|
event_pos=PersistedEventPosition(row[5], row[6]),
|
||||||
room_type=row[7],
|
has_known_state=bool(row[7]),
|
||||||
is_encrypted=bool(row[8]),
|
room_type=row[8],
|
||||||
|
is_encrypted=bool(row[9]),
|
||||||
)
|
)
|
||||||
for row in txn
|
for row in txn
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ class RoomsForUserSlidingSync:
|
||||||
event_pos: PersistedEventPosition
|
event_pos: PersistedEventPosition
|
||||||
room_version_id: str
|
room_version_id: str
|
||||||
|
|
||||||
|
has_known_state: bool
|
||||||
room_type: Optional[str]
|
room_type: Optional[str]
|
||||||
is_encrypted: bool
|
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(
|
self.assertEqual(
|
||||||
response_body["rooms"][room_id]["bump_stamp"], invite_pos.stream
|
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
|
import synapse.rest.admin
|
||||||
from synapse.api.constants import (
|
from synapse.api.constants import (
|
||||||
AccountDataTypes,
|
AccountDataTypes,
|
||||||
EventContentFields,
|
|
||||||
EventTypes,
|
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.rest.client import devices, login, receipts, room, sync
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
from synapse.types import (
|
from synapse.types import (
|
||||||
|
@ -141,6 +142,167 @@ class SlidingSyncBase(unittest.HomeserverTestCase):
|
||||||
message=str(actual_required_state),
|
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(
|
def _bump_notifier_wait_for_events(
|
||||||
self,
|
self,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
|
@ -261,93 +423,6 @@ class SlidingSyncTestCase(SlidingSyncBase):
|
||||||
|
|
||||||
super().prepare(reactor, clock, hs)
|
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:
|
def test_sync_list(self) -> None:
|
||||||
"""
|
"""
|
||||||
Test that room IDs show up in the Sliding Sync `lists`
|
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.
|
# There should be no room sent down.
|
||||||
self.assertFalse(channel.json_body["rooms"])
|
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:
|
def test_forgotten_up_to_date(self) -> None:
|
||||||
"""
|
"""
|
||||||
Make sure we get up-to-date `forgotten` status for rooms
|
Make sure we get up-to-date `forgotten` status for rooms
|
||||||
|
|
Loading…
Reference in a new issue