test_presence.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 OpenMarket Ltd
  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 tests import unittest
  16. from mock import Mock, call
  17. from synapse.api.constants import PresenceState
  18. from synapse.handlers.presence import (
  19. handle_update, handle_timeout,
  20. IDLE_TIMER, SYNC_ONLINE_TIMEOUT, LAST_ACTIVE_GRANULARITY, FEDERATION_TIMEOUT,
  21. FEDERATION_PING_INTERVAL,
  22. )
  23. from synapse.storage.presence import UserPresenceState
  24. class PresenceUpdateTestCase(unittest.TestCase):
  25. def test_offline_to_online(self):
  26. wheel_timer = Mock()
  27. user_id = "@foo:bar"
  28. now = 5000000
  29. prev_state = UserPresenceState.default(user_id)
  30. new_state = prev_state.copy_and_replace(
  31. state=PresenceState.ONLINE,
  32. last_active_ts=now,
  33. )
  34. state, persist_and_notify, federation_ping = handle_update(
  35. prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
  36. )
  37. self.assertTrue(persist_and_notify)
  38. self.assertTrue(state.currently_active)
  39. self.assertEquals(new_state.state, state.state)
  40. self.assertEquals(new_state.status_msg, state.status_msg)
  41. self.assertEquals(state.last_federation_update_ts, now)
  42. self.assertEquals(wheel_timer.insert.call_count, 3)
  43. wheel_timer.insert.assert_has_calls([
  44. call(
  45. now=now,
  46. obj=user_id,
  47. then=new_state.last_active_ts + IDLE_TIMER
  48. ),
  49. call(
  50. now=now,
  51. obj=user_id,
  52. then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
  53. ),
  54. call(
  55. now=now,
  56. obj=user_id,
  57. then=new_state.last_active_ts + LAST_ACTIVE_GRANULARITY
  58. ),
  59. ], any_order=True)
  60. def test_online_to_online(self):
  61. wheel_timer = Mock()
  62. user_id = "@foo:bar"
  63. now = 5000000
  64. prev_state = UserPresenceState.default(user_id)
  65. prev_state = prev_state.copy_and_replace(
  66. state=PresenceState.ONLINE,
  67. last_active_ts=now,
  68. currently_active=True,
  69. )
  70. new_state = prev_state.copy_and_replace(
  71. state=PresenceState.ONLINE,
  72. last_active_ts=now,
  73. )
  74. state, persist_and_notify, federation_ping = handle_update(
  75. prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
  76. )
  77. self.assertFalse(persist_and_notify)
  78. self.assertTrue(federation_ping)
  79. self.assertTrue(state.currently_active)
  80. self.assertEquals(new_state.state, state.state)
  81. self.assertEquals(new_state.status_msg, state.status_msg)
  82. self.assertEquals(state.last_federation_update_ts, now)
  83. self.assertEquals(wheel_timer.insert.call_count, 3)
  84. wheel_timer.insert.assert_has_calls([
  85. call(
  86. now=now,
  87. obj=user_id,
  88. then=new_state.last_active_ts + IDLE_TIMER
  89. ),
  90. call(
  91. now=now,
  92. obj=user_id,
  93. then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
  94. ),
  95. call(
  96. now=now,
  97. obj=user_id,
  98. then=new_state.last_active_ts + LAST_ACTIVE_GRANULARITY
  99. ),
  100. ], any_order=True)
  101. def test_online_to_online_last_active(self):
  102. wheel_timer = Mock()
  103. user_id = "@foo:bar"
  104. now = 5000000
  105. prev_state = UserPresenceState.default(user_id)
  106. prev_state = prev_state.copy_and_replace(
  107. state=PresenceState.ONLINE,
  108. last_active_ts=now - LAST_ACTIVE_GRANULARITY - 1,
  109. currently_active=True,
  110. )
  111. new_state = prev_state.copy_and_replace(
  112. state=PresenceState.ONLINE,
  113. )
  114. state, persist_and_notify, federation_ping = handle_update(
  115. prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
  116. )
  117. self.assertTrue(persist_and_notify)
  118. self.assertFalse(state.currently_active)
  119. self.assertEquals(new_state.state, state.state)
  120. self.assertEquals(new_state.status_msg, state.status_msg)
  121. self.assertEquals(state.last_federation_update_ts, now)
  122. self.assertEquals(wheel_timer.insert.call_count, 2)
  123. wheel_timer.insert.assert_has_calls([
  124. call(
  125. now=now,
  126. obj=user_id,
  127. then=new_state.last_active_ts + IDLE_TIMER
  128. ),
  129. call(
  130. now=now,
  131. obj=user_id,
  132. then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
  133. )
  134. ], any_order=True)
  135. def test_remote_ping_timer(self):
  136. wheel_timer = Mock()
  137. user_id = "@foo:bar"
  138. now = 5000000
  139. prev_state = UserPresenceState.default(user_id)
  140. prev_state = prev_state.copy_and_replace(
  141. state=PresenceState.ONLINE,
  142. last_active_ts=now,
  143. )
  144. new_state = prev_state.copy_and_replace(
  145. state=PresenceState.ONLINE,
  146. )
  147. state, persist_and_notify, federation_ping = handle_update(
  148. prev_state, new_state, is_mine=False, wheel_timer=wheel_timer, now=now
  149. )
  150. self.assertFalse(persist_and_notify)
  151. self.assertFalse(federation_ping)
  152. self.assertFalse(state.currently_active)
  153. self.assertEquals(new_state.state, state.state)
  154. self.assertEquals(new_state.status_msg, state.status_msg)
  155. self.assertEquals(wheel_timer.insert.call_count, 1)
  156. wheel_timer.insert.assert_has_calls([
  157. call(
  158. now=now,
  159. obj=user_id,
  160. then=new_state.last_federation_update_ts + FEDERATION_TIMEOUT
  161. ),
  162. ], any_order=True)
  163. def test_online_to_offline(self):
  164. wheel_timer = Mock()
  165. user_id = "@foo:bar"
  166. now = 5000000
  167. prev_state = UserPresenceState.default(user_id)
  168. prev_state = prev_state.copy_and_replace(
  169. state=PresenceState.ONLINE,
  170. last_active_ts=now,
  171. currently_active=True,
  172. )
  173. new_state = prev_state.copy_and_replace(
  174. state=PresenceState.OFFLINE,
  175. )
  176. state, persist_and_notify, federation_ping = handle_update(
  177. prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
  178. )
  179. self.assertTrue(persist_and_notify)
  180. self.assertEquals(new_state.state, state.state)
  181. self.assertEquals(state.last_federation_update_ts, now)
  182. self.assertEquals(wheel_timer.insert.call_count, 0)
  183. def test_online_to_idle(self):
  184. wheel_timer = Mock()
  185. user_id = "@foo:bar"
  186. now = 5000000
  187. prev_state = UserPresenceState.default(user_id)
  188. prev_state = prev_state.copy_and_replace(
  189. state=PresenceState.ONLINE,
  190. last_active_ts=now,
  191. currently_active=True,
  192. )
  193. new_state = prev_state.copy_and_replace(
  194. state=PresenceState.UNAVAILABLE,
  195. )
  196. state, persist_and_notify, federation_ping = handle_update(
  197. prev_state, new_state, is_mine=True, wheel_timer=wheel_timer, now=now
  198. )
  199. self.assertTrue(persist_and_notify)
  200. self.assertEquals(new_state.state, state.state)
  201. self.assertEquals(state.last_federation_update_ts, now)
  202. self.assertEquals(new_state.state, state.state)
  203. self.assertEquals(new_state.status_msg, state.status_msg)
  204. self.assertEquals(wheel_timer.insert.call_count, 1)
  205. wheel_timer.insert.assert_has_calls([
  206. call(
  207. now=now,
  208. obj=user_id,
  209. then=new_state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT
  210. )
  211. ], any_order=True)
  212. class PresenceTimeoutTestCase(unittest.TestCase):
  213. def test_idle_timer(self):
  214. user_id = "@foo:bar"
  215. now = 5000000
  216. state = UserPresenceState.default(user_id)
  217. state = state.copy_and_replace(
  218. state=PresenceState.ONLINE,
  219. last_active_ts=now - IDLE_TIMER - 1,
  220. last_user_sync_ts=now,
  221. )
  222. new_state = handle_timeout(
  223. state, is_mine=True, user_to_num_current_syncs={}, now=now
  224. )
  225. self.assertIsNotNone(new_state)
  226. self.assertEquals(new_state.state, PresenceState.UNAVAILABLE)
  227. def test_sync_timeout(self):
  228. user_id = "@foo:bar"
  229. now = 5000000
  230. state = UserPresenceState.default(user_id)
  231. state = state.copy_and_replace(
  232. state=PresenceState.ONLINE,
  233. last_active_ts=now,
  234. last_user_sync_ts=now - SYNC_ONLINE_TIMEOUT - 1,
  235. )
  236. new_state = handle_timeout(
  237. state, is_mine=True, user_to_num_current_syncs={}, now=now
  238. )
  239. self.assertIsNotNone(new_state)
  240. self.assertEquals(new_state.state, PresenceState.OFFLINE)
  241. def test_sync_online(self):
  242. user_id = "@foo:bar"
  243. now = 5000000
  244. state = UserPresenceState.default(user_id)
  245. state = state.copy_and_replace(
  246. state=PresenceState.ONLINE,
  247. last_active_ts=now - SYNC_ONLINE_TIMEOUT - 1,
  248. last_user_sync_ts=now - SYNC_ONLINE_TIMEOUT - 1,
  249. )
  250. new_state = handle_timeout(
  251. state, is_mine=True, user_to_num_current_syncs={
  252. user_id: 1,
  253. }, now=now
  254. )
  255. self.assertIsNotNone(new_state)
  256. self.assertEquals(new_state.state, PresenceState.ONLINE)
  257. def test_federation_ping(self):
  258. user_id = "@foo:bar"
  259. now = 5000000
  260. state = UserPresenceState.default(user_id)
  261. state = state.copy_and_replace(
  262. state=PresenceState.ONLINE,
  263. last_active_ts=now,
  264. last_user_sync_ts=now,
  265. last_federation_update_ts=now - FEDERATION_PING_INTERVAL - 1,
  266. )
  267. new_state = handle_timeout(
  268. state, is_mine=True, user_to_num_current_syncs={}, now=now
  269. )
  270. self.assertIsNotNone(new_state)
  271. self.assertEquals(new_state, new_state)
  272. def test_no_timeout(self):
  273. user_id = "@foo:bar"
  274. now = 5000000
  275. state = UserPresenceState.default(user_id)
  276. state = state.copy_and_replace(
  277. state=PresenceState.ONLINE,
  278. last_active_ts=now,
  279. last_user_sync_ts=now,
  280. last_federation_update_ts=now,
  281. )
  282. new_state = handle_timeout(
  283. state, is_mine=True, user_to_num_current_syncs={}, now=now
  284. )
  285. self.assertIsNone(new_state)
  286. def test_federation_timeout(self):
  287. user_id = "@foo:bar"
  288. now = 5000000
  289. state = UserPresenceState.default(user_id)
  290. state = state.copy_and_replace(
  291. state=PresenceState.ONLINE,
  292. last_active_ts=now,
  293. last_user_sync_ts=now,
  294. last_federation_update_ts=now - FEDERATION_TIMEOUT - 1,
  295. )
  296. new_state = handle_timeout(
  297. state, is_mine=False, user_to_num_current_syncs={}, now=now
  298. )
  299. self.assertIsNotNone(new_state)
  300. self.assertEquals(new_state.state, PresenceState.OFFLINE)
  301. def test_last_active(self):
  302. user_id = "@foo:bar"
  303. now = 5000000
  304. state = UserPresenceState.default(user_id)
  305. state = state.copy_and_replace(
  306. state=PresenceState.ONLINE,
  307. last_active_ts=now - LAST_ACTIVE_GRANULARITY - 1,
  308. last_user_sync_ts=now,
  309. last_federation_update_ts=now,
  310. )
  311. new_state = handle_timeout(
  312. state, is_mine=True, user_to_num_current_syncs={}, now=now
  313. )
  314. self.assertIsNotNone(new_state)
  315. self.assertEquals(state, new_state)