test_events.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2020 The Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from synapse.api.constants import EventTypes, Membership
  16. from synapse.api.room_versions import RoomVersions
  17. from synapse.federation.federation_base import event_from_pdu_json
  18. from synapse.rest import admin
  19. from synapse.rest.client.v1 import login, room
  20. from tests.unittest import HomeserverTestCase
  21. class ExtremPruneTestCase(HomeserverTestCase):
  22. servlets = [
  23. admin.register_servlets,
  24. room.register_servlets,
  25. login.register_servlets,
  26. ]
  27. def prepare(self, reactor, clock, homeserver):
  28. self.state = self.hs.get_state_handler()
  29. self.persistence = self.hs.get_storage().persistence
  30. self.store = self.hs.get_datastore()
  31. self.register_user("user", "pass")
  32. self.token = self.login("user", "pass")
  33. self.room_id = self.helper.create_room_as(
  34. "user", room_version=RoomVersions.V6.identifier, tok=self.token
  35. )
  36. body = self.helper.send(self.room_id, body="Test", tok=self.token)
  37. local_message_event_id = body["event_id"]
  38. # Fudge a remote event and persist it. This will be the extremity before
  39. # the gap.
  40. self.remote_event_1 = event_from_pdu_json(
  41. {
  42. "type": EventTypes.Message,
  43. "state_key": "@user:other",
  44. "content": {},
  45. "room_id": self.room_id,
  46. "sender": "@user:other",
  47. "depth": 5,
  48. "prev_events": [local_message_event_id],
  49. "auth_events": [],
  50. "origin_server_ts": self.clock.time_msec(),
  51. },
  52. RoomVersions.V6,
  53. )
  54. self.persist_event(self.remote_event_1)
  55. # Check that the current extremities is the remote event.
  56. self.assert_extremities([self.remote_event_1.event_id])
  57. def persist_event(self, event, state=None):
  58. """Persist the event, with optional state
  59. """
  60. context = self.get_success(
  61. self.state.compute_event_context(event, old_state=state)
  62. )
  63. self.get_success(self.persistence.persist_event(event, context))
  64. def assert_extremities(self, expected_extremities):
  65. """Assert the current extremities for the room
  66. """
  67. extremities = self.get_success(
  68. self.store.get_prev_events_for_room(self.room_id)
  69. )
  70. self.assertCountEqual(extremities, expected_extremities)
  71. def test_prune_gap(self):
  72. """Test that we drop extremities after a gap when we see an event from
  73. the same domain.
  74. """
  75. # Fudge a second event which points to an event we don't have. This is a
  76. # state event so that the state changes (otherwise we won't prune the
  77. # extremity as they'll have the same state group).
  78. remote_event_2 = event_from_pdu_json(
  79. {
  80. "type": EventTypes.Member,
  81. "state_key": "@user:other",
  82. "content": {"membership": Membership.JOIN},
  83. "room_id": self.room_id,
  84. "sender": "@user:other",
  85. "depth": 50,
  86. "prev_events": ["$some_unknown_message"],
  87. "auth_events": [],
  88. "origin_server_ts": self.clock.time_msec(),
  89. },
  90. RoomVersions.V6,
  91. )
  92. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  93. self.persist_event(remote_event_2, state=state_before_gap.values())
  94. # Check the new extremity is just the new remote event.
  95. self.assert_extremities([remote_event_2.event_id])
  96. def test_do_not_prune_gap_if_state_different(self):
  97. """Test that we don't prune extremities after a gap if the resolved
  98. state is different.
  99. """
  100. # Fudge a second event which points to an event we don't have.
  101. remote_event_2 = event_from_pdu_json(
  102. {
  103. "type": EventTypes.Message,
  104. "state_key": "@user:other",
  105. "content": {},
  106. "room_id": self.room_id,
  107. "sender": "@user:other",
  108. "depth": 10,
  109. "prev_events": ["$some_unknown_message"],
  110. "auth_events": [],
  111. "origin_server_ts": self.clock.time_msec(),
  112. },
  113. RoomVersions.V6,
  114. )
  115. # Now we persist it with state with a dropped history visibility
  116. # setting. The state resolution across the old and new event will then
  117. # include it, and so the resolved state won't match the new state.
  118. state_before_gap = dict(
  119. self.get_success(self.state.get_current_state(self.room_id))
  120. )
  121. state_before_gap.pop(("m.room.history_visibility", ""))
  122. context = self.get_success(
  123. self.state.compute_event_context(
  124. remote_event_2, old_state=state_before_gap.values()
  125. )
  126. )
  127. self.get_success(self.persistence.persist_event(remote_event_2, context))
  128. # Check that we haven't dropped the old extremity.
  129. self.assert_extremities([self.remote_event_1.event_id, remote_event_2.event_id])
  130. def test_prune_gap_if_old(self):
  131. """Test that we drop extremities after a gap when the previous extremity
  132. is "old"
  133. """
  134. # Advance the clock for many days to make the old extremity "old". We
  135. # also set the depth to "lots".
  136. self.reactor.advance(7 * 24 * 60 * 60)
  137. # Fudge a second event which points to an event we don't have. This is a
  138. # state event so that the state changes (otherwise we won't prune the
  139. # extremity as they'll have the same state group).
  140. remote_event_2 = event_from_pdu_json(
  141. {
  142. "type": EventTypes.Member,
  143. "state_key": "@user:other2",
  144. "content": {"membership": Membership.JOIN},
  145. "room_id": self.room_id,
  146. "sender": "@user:other2",
  147. "depth": 10000,
  148. "prev_events": ["$some_unknown_message"],
  149. "auth_events": [],
  150. "origin_server_ts": self.clock.time_msec(),
  151. },
  152. RoomVersions.V6,
  153. )
  154. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  155. self.persist_event(remote_event_2, state=state_before_gap.values())
  156. # Check the new extremity is just the new remote event.
  157. self.assert_extremities([remote_event_2.event_id])
  158. def test_do_not_prune_gap_if_other_server(self):
  159. """Test that we do not drop extremities after a gap when we see an event
  160. from a different domain.
  161. """
  162. # Fudge a second event which points to an event we don't have. This is a
  163. # state event so that the state changes (otherwise we won't prune the
  164. # extremity as they'll have the same state group).
  165. remote_event_2 = event_from_pdu_json(
  166. {
  167. "type": EventTypes.Member,
  168. "state_key": "@user:other2",
  169. "content": {"membership": Membership.JOIN},
  170. "room_id": self.room_id,
  171. "sender": "@user:other2",
  172. "depth": 10,
  173. "prev_events": ["$some_unknown_message"],
  174. "auth_events": [],
  175. "origin_server_ts": self.clock.time_msec(),
  176. },
  177. RoomVersions.V6,
  178. )
  179. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  180. self.persist_event(remote_event_2, state=state_before_gap.values())
  181. # Check the new extremity is just the new remote event.
  182. self.assert_extremities([self.remote_event_1.event_id, remote_event_2.event_id])
  183. def test_prune_gap_if_dummy_remote(self):
  184. """Test that we drop extremities after a gap when the previous extremity
  185. is a local dummy event and only points to remote events.
  186. """
  187. body = self.helper.send_event(
  188. self.room_id, type=EventTypes.Dummy, content={}, tok=self.token
  189. )
  190. local_message_event_id = body["event_id"]
  191. self.assert_extremities([local_message_event_id])
  192. # Advance the clock for many days to make the old extremity "old". We
  193. # also set the depth to "lots".
  194. self.reactor.advance(7 * 24 * 60 * 60)
  195. # Fudge a second event which points to an event we don't have. This is a
  196. # state event so that the state changes (otherwise we won't prune the
  197. # extremity as they'll have the same state group).
  198. remote_event_2 = event_from_pdu_json(
  199. {
  200. "type": EventTypes.Member,
  201. "state_key": "@user:other2",
  202. "content": {"membership": Membership.JOIN},
  203. "room_id": self.room_id,
  204. "sender": "@user:other2",
  205. "depth": 10000,
  206. "prev_events": ["$some_unknown_message"],
  207. "auth_events": [],
  208. "origin_server_ts": self.clock.time_msec(),
  209. },
  210. RoomVersions.V6,
  211. )
  212. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  213. self.persist_event(remote_event_2, state=state_before_gap.values())
  214. # Check the new extremity is just the new remote event.
  215. self.assert_extremities([remote_event_2.event_id])
  216. def test_prune_gap_if_dummy_local(self):
  217. """Test that we don't drop extremities after a gap when the previous
  218. extremity is a local dummy event and points to local events.
  219. """
  220. body = self.helper.send(self.room_id, body="Test", tok=self.token)
  221. body = self.helper.send_event(
  222. self.room_id, type=EventTypes.Dummy, content={}, tok=self.token
  223. )
  224. local_message_event_id = body["event_id"]
  225. self.assert_extremities([local_message_event_id])
  226. # Advance the clock for many days to make the old extremity "old". We
  227. # also set the depth to "lots".
  228. self.reactor.advance(7 * 24 * 60 * 60)
  229. # Fudge a second event which points to an event we don't have. This is a
  230. # state event so that the state changes (otherwise we won't prune the
  231. # extremity as they'll have the same state group).
  232. remote_event_2 = event_from_pdu_json(
  233. {
  234. "type": EventTypes.Member,
  235. "state_key": "@user:other2",
  236. "content": {"membership": Membership.JOIN},
  237. "room_id": self.room_id,
  238. "sender": "@user:other2",
  239. "depth": 10000,
  240. "prev_events": ["$some_unknown_message"],
  241. "auth_events": [],
  242. "origin_server_ts": self.clock.time_msec(),
  243. },
  244. RoomVersions.V6,
  245. )
  246. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  247. self.persist_event(remote_event_2, state=state_before_gap.values())
  248. # Check the new extremity is just the new remote event.
  249. self.assert_extremities([remote_event_2.event_id, local_message_event_id])
  250. def test_do_not_prune_gap_if_not_dummy(self):
  251. """Test that we do not drop extremities after a gap when the previous extremity
  252. is not a dummy event.
  253. """
  254. body = self.helper.send(self.room_id, body="test", tok=self.token)
  255. local_message_event_id = body["event_id"]
  256. self.assert_extremities([local_message_event_id])
  257. # Fudge a second event which points to an event we don't have. This is a
  258. # state event so that the state changes (otherwise we won't prune the
  259. # extremity as they'll have the same state group).
  260. remote_event_2 = event_from_pdu_json(
  261. {
  262. "type": EventTypes.Member,
  263. "state_key": "@user:other2",
  264. "content": {"membership": Membership.JOIN},
  265. "room_id": self.room_id,
  266. "sender": "@user:other2",
  267. "depth": 10000,
  268. "prev_events": ["$some_unknown_message"],
  269. "auth_events": [],
  270. "origin_server_ts": self.clock.time_msec(),
  271. },
  272. RoomVersions.V6,
  273. )
  274. state_before_gap = self.get_success(self.state.get_current_state(self.room_id))
  275. self.persist_event(remote_event_2, state=state_before_gap.values())
  276. # Check the new extremity is just the new remote event.
  277. self.assert_extremities([local_message_event_id, remote_event_2.event_id])