Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 0433e88

Browse files
committed
Add tests
1 parent 0e1866f commit 0433e88

File tree

1 file changed

+185
-2
lines changed

1 file changed

+185
-2
lines changed

tests/handlers/test_user_directory.py

Lines changed: 185 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@
1919

2020
import synapse.rest.admin
2121
from synapse.api.constants import UserTypes
22+
from synapse.api.errors import SynapseError
2223
from synapse.api.room_versions import RoomVersion, RoomVersions
2324
from synapse.appservice import ApplicationService
2425
from synapse.rest.client import login, register, room, user_directory
2526
from synapse.server import HomeServer
2627
from synapse.storage.roommember import ProfileInfo
27-
from synapse.types import UserProfile, create_requester
28+
from synapse.types import JsonDict, UserProfile, create_requester
2829
from synapse.util import Clock
2930

3031
from tests import unittest
3132
from tests.storage.test_user_directory import GetUserDirectoryTables
32-
from tests.test_utils import make_awaitable
33+
from tests.test_utils import event_injection, make_awaitable
3334
from tests.test_utils.event_injection import inject_member_event
3435
from tests.unittest import override_config
3536

@@ -1103,3 +1104,185 @@ def test_disabling_room_list(self) -> None:
11031104
)
11041105
self.assertEqual(200, channel.code, channel.result)
11051106
self.assertTrue(len(channel.json_body["results"]) == 0)
1107+
1108+
1109+
class UserDirectoryRemoteProfileTestCase(unittest.HomeserverTestCase):
1110+
servlets = [
1111+
login.register_servlets,
1112+
synapse.rest.admin.register_servlets,
1113+
register.register_servlets,
1114+
room.register_servlets,
1115+
]
1116+
1117+
def default_config(self) -> JsonDict:
1118+
config = super().default_config()
1119+
# Re-enables updating the user directory, as that functionality is needed below.
1120+
config["update_user_directory_from_worker"] = None
1121+
return config
1122+
1123+
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
1124+
self.store = hs.get_datastores().main
1125+
self.alice = self.register_user("alice", "alice123")
1126+
self.alice_tok = self.login("alice", "alice123")
1127+
self.user_dir_helper = GetUserDirectoryTables(self.store)
1128+
self.user_dir_handler = hs.get_user_directory_handler()
1129+
self.profile_handler = hs.get_profile_handler()
1130+
1131+
# Cancel the startup call: in the steady-state case we can't rely on it anyway.
1132+
assert self.user_dir_handler._refresh_remote_profiles_call_later is not None
1133+
self.user_dir_handler._refresh_remote_profiles_call_later.cancel()
1134+
1135+
def test_public_rooms_have_profiles_collected(self) -> None:
1136+
"""
1137+
In a public room, member state events are treated as reflecting the user's
1138+
real profile and they are accepted.
1139+
(The main motivation for accepting this is to prevent having to query
1140+
*every* single profile change over federation.)
1141+
"""
1142+
room_id = self.helper.create_room_as(
1143+
self.alice, is_public=True, tok=self.alice_tok
1144+
)
1145+
self.get_success(
1146+
event_injection.inject_member_event(
1147+
self.hs,
1148+
room_id,
1149+
"@bruce:remote",
1150+
"join",
1151+
"@bruce:remote",
1152+
extra_content={
1153+
"displayname": "Bruce!",
1154+
"avatar_url": "mxc://remote/123",
1155+
},
1156+
)
1157+
)
1158+
# Sending this event makes the streams move forward after the injection...
1159+
self.helper.send(room_id, "Test", tok=self.alice_tok)
1160+
self.pump(0.1)
1161+
1162+
profiles = self.get_success(
1163+
self.user_dir_helper.get_profiles_in_user_directory()
1164+
)
1165+
self.assertEqual(
1166+
profiles.get("@bruce:remote"),
1167+
ProfileInfo(display_name="Bruce!", avatar_url="mxc://remote/123"),
1168+
)
1169+
1170+
def test_private_rooms_do_not_have_profiles_collected(self) -> None:
1171+
"""
1172+
In a private room, member state events are not pulled out and used to populate
1173+
the user directory.
1174+
"""
1175+
room_id = self.helper.create_room_as(
1176+
self.alice, is_public=False, tok=self.alice_tok
1177+
)
1178+
self.get_success(
1179+
event_injection.inject_member_event(
1180+
self.hs,
1181+
room_id,
1182+
"@bruce:remote",
1183+
"join",
1184+
"@bruce:remote",
1185+
extra_content={
1186+
"displayname": "super-duper bruce",
1187+
"avatar_url": "mxc://remote/456",
1188+
},
1189+
)
1190+
)
1191+
# Sending this event makes the streams move forward after the injection...
1192+
self.helper.send(room_id, "Test", tok=self.alice_tok)
1193+
self.pump(0.1)
1194+
1195+
profiles = self.get_success(
1196+
self.user_dir_helper.get_profiles_in_user_directory()
1197+
)
1198+
self.assertNotIn("@bruce:remote", profiles)
1199+
1200+
def test_private_rooms_have_profiles_requested(self) -> None:
1201+
"""
1202+
When a name changes in a private room, the homeserver instead requests
1203+
the user's global profile over federation.
1204+
"""
1205+
1206+
async def get_remote_profile(
1207+
user_id: str, ignore_backoff: bool = True
1208+
) -> JsonDict:
1209+
if user_id == "@bruce:remote":
1210+
return {
1211+
"displayname": "Sir Bruce Bruceson",
1212+
"avatar_url": "mxc://remote/789",
1213+
}
1214+
else:
1215+
raise ValueError(f"unable to fetch {user_id}")
1216+
1217+
with patch.object(self.profile_handler, "get_profile", get_remote_profile):
1218+
# Continue from the earlier test...
1219+
self.test_private_rooms_do_not_have_profiles_collected()
1220+
1221+
# Advance by a minute
1222+
self.reactor.advance(61.0)
1223+
1224+
profiles = self.get_success(
1225+
self.user_dir_helper.get_profiles_in_user_directory()
1226+
)
1227+
self.assertEqual(
1228+
profiles.get("@bruce:remote"),
1229+
ProfileInfo(
1230+
display_name="Sir Bruce Bruceson", avatar_url="mxc://remote/789"
1231+
),
1232+
)
1233+
1234+
def test_profile_requests_are_retried(self) -> None:
1235+
"""
1236+
When we fail to fetch the user's profile over federation,
1237+
we try again later.
1238+
"""
1239+
has_failed_once = False
1240+
1241+
async def get_remote_profile(
1242+
user_id: str, ignore_backoff: bool = True
1243+
) -> JsonDict:
1244+
nonlocal has_failed_once
1245+
if user_id == "@bruce:remote":
1246+
if not has_failed_once:
1247+
has_failed_once = True
1248+
raise SynapseError(502, "temporary network problem")
1249+
1250+
return {
1251+
"displayname": "Sir Bruce Bruceson",
1252+
"avatar_url": "mxc://remote/789",
1253+
}
1254+
else:
1255+
raise ValueError(f"unable to fetch {user_id}")
1256+
1257+
with patch.object(self.profile_handler, "get_profile", get_remote_profile):
1258+
# Continue from the earlier test...
1259+
self.test_private_rooms_do_not_have_profiles_collected()
1260+
1261+
# Advance by a minute
1262+
self.reactor.advance(61.0)
1263+
1264+
# The request has already failed once
1265+
self.assertTrue(has_failed_once)
1266+
1267+
# The profile has yet to be updated.
1268+
profiles = self.get_success(
1269+
self.user_dir_helper.get_profiles_in_user_directory()
1270+
)
1271+
self.assertNotIn(
1272+
"@bruce:remote",
1273+
profiles,
1274+
)
1275+
1276+
# Advance by five minutes, after the backoff has finished
1277+
self.reactor.advance(301.0)
1278+
1279+
# The profile should have been updated now
1280+
profiles = self.get_success(
1281+
self.user_dir_helper.get_profiles_in_user_directory()
1282+
)
1283+
self.assertEqual(
1284+
profiles.get("@bruce:remote"),
1285+
ProfileInfo(
1286+
display_name="Sir Bruce Bruceson", avatar_url="mxc://remote/789"
1287+
),
1288+
)

0 commit comments

Comments
 (0)