test_api.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. # Copyright 2020 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from unittest.mock import Mock
  15. from synapse.api.constants import EduTypes
  16. from synapse.events import EventBase
  17. from synapse.federation.units import Transaction
  18. from synapse.handlers.presence import UserPresenceState
  19. from synapse.rest import admin
  20. from synapse.rest.client.v1 import login, presence, room
  21. from synapse.types import create_requester
  22. from tests.events.test_presence_router import send_presence_update, sync_presence
  23. from tests.test_utils.event_injection import inject_member_event
  24. from tests.unittest import FederatingHomeserverTestCase, override_config
  25. class ModuleApiTestCase(FederatingHomeserverTestCase):
  26. servlets = [
  27. admin.register_servlets,
  28. login.register_servlets,
  29. room.register_servlets,
  30. presence.register_servlets,
  31. ]
  32. def prepare(self, reactor, clock, homeserver):
  33. self.store = homeserver.get_datastore()
  34. self.module_api = homeserver.get_module_api()
  35. self.event_creation_handler = homeserver.get_event_creation_handler()
  36. self.sync_handler = homeserver.get_sync_handler()
  37. def make_homeserver(self, reactor, clock):
  38. return self.setup_test_homeserver(
  39. federation_transport_client=Mock(spec=["send_transaction"]),
  40. )
  41. def test_can_register_user(self):
  42. """Tests that an external module can register a user"""
  43. # Register a new user
  44. user_id, access_token = self.get_success(
  45. self.module_api.register(
  46. "bob", displayname="Bobberino", emails=["bob@bobinator.bob"]
  47. )
  48. )
  49. # Check that the new user exists with all provided attributes
  50. self.assertEqual(user_id, "@bob:test")
  51. self.assertTrue(access_token)
  52. self.assertTrue(self.get_success(self.store.get_user_by_id(user_id)))
  53. # Check that the email was assigned
  54. emails = self.get_success(self.store.user_get_threepids(user_id))
  55. self.assertEqual(len(emails), 1)
  56. email = emails[0]
  57. self.assertEqual(email["medium"], "email")
  58. self.assertEqual(email["address"], "bob@bobinator.bob")
  59. # Should these be 0?
  60. self.assertEqual(email["validated_at"], 0)
  61. self.assertEqual(email["added_at"], 0)
  62. # Check that the displayname was assigned
  63. displayname = self.get_success(self.store.get_profile_displayname("bob"))
  64. self.assertEqual(displayname, "Bobberino")
  65. def test_sending_events_into_room(self):
  66. """Tests that a module can send events into a room"""
  67. # Mock out create_and_send_nonmember_event to check whether events are being sent
  68. self.event_creation_handler.create_and_send_nonmember_event = Mock(
  69. spec=[],
  70. side_effect=self.event_creation_handler.create_and_send_nonmember_event,
  71. )
  72. # Create a user and room to play with
  73. user_id = self.register_user("summer", "monkey")
  74. tok = self.login("summer", "monkey")
  75. room_id = self.helper.create_room_as(user_id, tok=tok)
  76. # Create and send a non-state event
  77. content = {"body": "I am a puppet", "msgtype": "m.text"}
  78. event_dict = {
  79. "room_id": room_id,
  80. "type": "m.room.message",
  81. "content": content,
  82. "sender": user_id,
  83. }
  84. event = self.get_success(
  85. self.module_api.create_and_send_event_into_room(event_dict)
  86. ) # type: EventBase
  87. self.assertEqual(event.sender, user_id)
  88. self.assertEqual(event.type, "m.room.message")
  89. self.assertEqual(event.room_id, room_id)
  90. self.assertFalse(hasattr(event, "state_key"))
  91. self.assertDictEqual(event.content, content)
  92. expected_requester = create_requester(
  93. user_id, authenticated_entity=self.hs.hostname
  94. )
  95. # Check that the event was sent
  96. self.event_creation_handler.create_and_send_nonmember_event.assert_called_with(
  97. expected_requester,
  98. event_dict,
  99. ratelimit=False,
  100. ignore_shadow_ban=True,
  101. )
  102. # Create and send a state event
  103. content = {
  104. "events_default": 0,
  105. "users": {user_id: 100},
  106. "state_default": 50,
  107. "users_default": 0,
  108. "events": {"test.event.type": 25},
  109. }
  110. event_dict = {
  111. "room_id": room_id,
  112. "type": "m.room.power_levels",
  113. "content": content,
  114. "sender": user_id,
  115. "state_key": "",
  116. }
  117. event = self.get_success(
  118. self.module_api.create_and_send_event_into_room(event_dict)
  119. ) # type: EventBase
  120. self.assertEqual(event.sender, user_id)
  121. self.assertEqual(event.type, "m.room.power_levels")
  122. self.assertEqual(event.room_id, room_id)
  123. self.assertEqual(event.state_key, "")
  124. self.assertDictEqual(event.content, content)
  125. # Check that the event was sent
  126. self.event_creation_handler.create_and_send_nonmember_event.assert_called_with(
  127. expected_requester,
  128. {
  129. "type": "m.room.power_levels",
  130. "content": content,
  131. "room_id": room_id,
  132. "sender": user_id,
  133. "state_key": "",
  134. },
  135. ratelimit=False,
  136. ignore_shadow_ban=True,
  137. )
  138. # Check that we can't send membership events
  139. content = {
  140. "membership": "leave",
  141. }
  142. event_dict = {
  143. "room_id": room_id,
  144. "type": "m.room.member",
  145. "content": content,
  146. "sender": user_id,
  147. "state_key": user_id,
  148. }
  149. self.get_failure(
  150. self.module_api.create_and_send_event_into_room(event_dict), Exception
  151. )
  152. def test_public_rooms(self):
  153. """Tests that a room can be added and removed from the public rooms list,
  154. as well as have its public rooms directory state queried.
  155. """
  156. # Create a user and room to play with
  157. user_id = self.register_user("kermit", "monkey")
  158. tok = self.login("kermit", "monkey")
  159. room_id = self.helper.create_room_as(user_id, tok=tok)
  160. # The room should not currently be in the public rooms directory
  161. is_in_public_rooms = self.get_success(
  162. self.module_api.public_room_list_manager.room_is_in_public_room_list(
  163. room_id
  164. )
  165. )
  166. self.assertFalse(is_in_public_rooms)
  167. # Let's try adding it to the public rooms directory
  168. self.get_success(
  169. self.module_api.public_room_list_manager.add_room_to_public_room_list(
  170. room_id
  171. )
  172. )
  173. # And checking whether it's in there...
  174. is_in_public_rooms = self.get_success(
  175. self.module_api.public_room_list_manager.room_is_in_public_room_list(
  176. room_id
  177. )
  178. )
  179. self.assertTrue(is_in_public_rooms)
  180. # Let's remove it again
  181. self.get_success(
  182. self.module_api.public_room_list_manager.remove_room_from_public_room_list(
  183. room_id
  184. )
  185. )
  186. # Should be gone
  187. is_in_public_rooms = self.get_success(
  188. self.module_api.public_room_list_manager.room_is_in_public_room_list(
  189. room_id
  190. )
  191. )
  192. self.assertFalse(is_in_public_rooms)
  193. # The ability to send federation is required by send_local_online_presence_to.
  194. @override_config({"send_federation": True})
  195. def test_send_local_online_presence_to(self):
  196. """Tests that send_local_presence_to_users sends local online presence to local users."""
  197. # Create a user who will send presence updates
  198. self.presence_receiver_id = self.register_user("presence_receiver", "monkey")
  199. self.presence_receiver_tok = self.login("presence_receiver", "monkey")
  200. # And another user that will send presence updates out
  201. self.presence_sender_id = self.register_user("presence_sender", "monkey")
  202. self.presence_sender_tok = self.login("presence_sender", "monkey")
  203. # Put them in a room together so they will receive each other's presence updates
  204. room_id = self.helper.create_room_as(
  205. self.presence_receiver_id,
  206. tok=self.presence_receiver_tok,
  207. )
  208. self.helper.join(room_id, self.presence_sender_id, tok=self.presence_sender_tok)
  209. # Presence sender comes online
  210. send_presence_update(
  211. self,
  212. self.presence_sender_id,
  213. self.presence_sender_tok,
  214. "online",
  215. "I'm online!",
  216. )
  217. # Presence receiver should have received it
  218. presence_updates, sync_token = sync_presence(self, self.presence_receiver_id)
  219. self.assertEqual(len(presence_updates), 1)
  220. presence_update = presence_updates[0] # type: UserPresenceState
  221. self.assertEqual(presence_update.user_id, self.presence_sender_id)
  222. self.assertEqual(presence_update.state, "online")
  223. # Syncing again should result in no presence updates
  224. presence_updates, sync_token = sync_presence(
  225. self, self.presence_receiver_id, sync_token
  226. )
  227. self.assertEqual(len(presence_updates), 0)
  228. # Trigger sending local online presence
  229. self.get_success(
  230. self.module_api.send_local_online_presence_to(
  231. [
  232. self.presence_receiver_id,
  233. ]
  234. )
  235. )
  236. # Presence receiver should have received online presence again
  237. presence_updates, sync_token = sync_presence(
  238. self, self.presence_receiver_id, sync_token
  239. )
  240. self.assertEqual(len(presence_updates), 1)
  241. presence_update = presence_updates[0] # type: UserPresenceState
  242. self.assertEqual(presence_update.user_id, self.presence_sender_id)
  243. self.assertEqual(presence_update.state, "online")
  244. # Presence sender goes offline
  245. send_presence_update(
  246. self,
  247. self.presence_sender_id,
  248. self.presence_sender_tok,
  249. "offline",
  250. "I slink back into the darkness.",
  251. )
  252. # Trigger sending local online presence
  253. self.get_success(
  254. self.module_api.send_local_online_presence_to(
  255. [
  256. self.presence_receiver_id,
  257. ]
  258. )
  259. )
  260. # Presence receiver should *not* have received offline state
  261. presence_updates, sync_token = sync_presence(
  262. self, self.presence_receiver_id, sync_token
  263. )
  264. self.assertEqual(len(presence_updates), 0)
  265. @override_config({"send_federation": True})
  266. def test_send_local_online_presence_to_federation(self):
  267. """Tests that send_local_presence_to_users sends local online presence to remote users."""
  268. # Create a user who will send presence updates
  269. self.presence_sender_id = self.register_user("presence_sender", "monkey")
  270. self.presence_sender_tok = self.login("presence_sender", "monkey")
  271. # And a room they're a part of
  272. room_id = self.helper.create_room_as(
  273. self.presence_sender_id,
  274. tok=self.presence_sender_tok,
  275. )
  276. # Mark them as online
  277. send_presence_update(
  278. self,
  279. self.presence_sender_id,
  280. self.presence_sender_tok,
  281. "online",
  282. "I'm online!",
  283. )
  284. # Make up a remote user to send presence to
  285. remote_user_id = "@far_away_person:island"
  286. # Create a join membership event for the remote user into the room.
  287. # This allows presence information to flow from one user to the other.
  288. self.get_success(
  289. inject_member_event(
  290. self.hs,
  291. room_id,
  292. sender=remote_user_id,
  293. target=remote_user_id,
  294. membership="join",
  295. )
  296. )
  297. # The remote user would have received the existing room members' presence
  298. # when they joined the room.
  299. #
  300. # Thus we reset the mock, and try sending online local user
  301. # presence again
  302. self.hs.get_federation_transport_client().send_transaction.reset_mock()
  303. # Broadcast local user online presence
  304. self.get_success(
  305. self.module_api.send_local_online_presence_to([remote_user_id])
  306. )
  307. # Check that a presence update was sent as part of a federation transaction
  308. found_update = False
  309. calls = (
  310. self.hs.get_federation_transport_client().send_transaction.call_args_list
  311. )
  312. for call in calls:
  313. call_args = call[0]
  314. federation_transaction = call_args[0] # type: Transaction
  315. # Get the sent EDUs in this transaction
  316. edus = federation_transaction.get_dict()["edus"]
  317. for edu in edus:
  318. # Make sure we're only checking presence-type EDUs
  319. if edu["edu_type"] != EduTypes.Presence:
  320. continue
  321. # EDUs can contain multiple presence updates
  322. for presence_update in edu["content"]["push"]:
  323. if presence_update["user_id"] == self.presence_sender_id:
  324. found_update = True
  325. self.assertTrue(found_update)