test_event_push_actions.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. # Copyright 2016-2021 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 typing import Optional, Tuple
  15. from twisted.test.proto_helpers import MemoryReactor
  16. from synapse.api.constants import MAIN_TIMELINE, RelationTypes
  17. from synapse.rest import admin
  18. from synapse.rest.client import login, room
  19. from synapse.server import HomeServer
  20. from synapse.storage.databases.main.event_push_actions import NotifCounts
  21. from synapse.types import JsonDict
  22. from synapse.util import Clock
  23. from tests.unittest import HomeserverTestCase
  24. class EventPushActionsStoreTestCase(HomeserverTestCase):
  25. servlets = [
  26. admin.register_servlets,
  27. room.register_servlets,
  28. login.register_servlets,
  29. ]
  30. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  31. self.store = hs.get_datastores().main
  32. persist_events_store = hs.get_datastores().persist_events
  33. assert persist_events_store is not None
  34. self.persist_events_store = persist_events_store
  35. def _create_users_and_room(self) -> Tuple[str, str, str, str, str]:
  36. """
  37. Creates two users and a shared room.
  38. Returns:
  39. Tuple of (user 1 ID, user 1 token, user 2 ID, user 2 token, room ID).
  40. """
  41. # Create a user to receive notifications and send receipts.
  42. user_id = self.register_user("user1235", "pass")
  43. token = self.login("user1235", "pass")
  44. # And another users to send events.
  45. other_id = self.register_user("other", "pass")
  46. other_token = self.login("other", "pass")
  47. # Create a room and put both users in it.
  48. room_id = self.helper.create_room_as(user_id, tok=token)
  49. self.helper.join(room_id, other_id, tok=other_token)
  50. return user_id, token, other_id, other_token, room_id
  51. def test_get_unread_push_actions_for_user_in_range(self) -> None:
  52. """Test getting unread push actions for HTTP and email pushers."""
  53. user_id, token, _, other_token, room_id = self._create_users_and_room()
  54. # Create two events, one of which is a highlight.
  55. first_event_id = self.helper.send_event(
  56. room_id,
  57. type="m.room.message",
  58. content={"msgtype": "m.text", "body": "msg"},
  59. tok=other_token,
  60. )["event_id"]
  61. second_event_id = self.helper.send_event(
  62. room_id,
  63. type="m.room.message",
  64. content={
  65. "msgtype": "m.text",
  66. "body": user_id,
  67. "m.relates_to": {
  68. "rel_type": RelationTypes.THREAD,
  69. "event_id": first_event_id,
  70. },
  71. },
  72. tok=other_token,
  73. )["event_id"]
  74. # Fetch unread actions for HTTP pushers.
  75. http_actions = self.get_success(
  76. self.store.get_unread_push_actions_for_user_in_range_for_http(
  77. user_id, 0, 1000, 20
  78. )
  79. )
  80. self.assertEqual(2, len(http_actions))
  81. # Fetch unread actions for email pushers.
  82. email_actions = self.get_success(
  83. self.store.get_unread_push_actions_for_user_in_range_for_email(
  84. user_id, 0, 1000, 20
  85. )
  86. )
  87. self.assertEqual(2, len(email_actions))
  88. # Send a receipt, which should clear the first action.
  89. self.get_success(
  90. self.store.insert_receipt(
  91. room_id,
  92. "m.read",
  93. user_id=user_id,
  94. event_ids=[first_event_id],
  95. thread_id=None,
  96. data={},
  97. )
  98. )
  99. http_actions = self.get_success(
  100. self.store.get_unread_push_actions_for_user_in_range_for_http(
  101. user_id, 0, 1000, 20
  102. )
  103. )
  104. self.assertEqual(1, len(http_actions))
  105. email_actions = self.get_success(
  106. self.store.get_unread_push_actions_for_user_in_range_for_email(
  107. user_id, 0, 1000, 20
  108. )
  109. )
  110. self.assertEqual(1, len(email_actions))
  111. # Send a thread receipt to clear the thread action.
  112. self.get_success(
  113. self.store.insert_receipt(
  114. room_id,
  115. "m.read",
  116. user_id=user_id,
  117. event_ids=[second_event_id],
  118. thread_id=first_event_id,
  119. data={},
  120. )
  121. )
  122. http_actions = self.get_success(
  123. self.store.get_unread_push_actions_for_user_in_range_for_http(
  124. user_id, 0, 1000, 20
  125. )
  126. )
  127. self.assertEqual([], http_actions)
  128. email_actions = self.get_success(
  129. self.store.get_unread_push_actions_for_user_in_range_for_email(
  130. user_id, 0, 1000, 20
  131. )
  132. )
  133. self.assertEqual([], email_actions)
  134. def test_count_aggregation(self) -> None:
  135. # Create a user to receive notifications and send receipts.
  136. user_id, token, _, other_token, room_id = self._create_users_and_room()
  137. last_event_id: str
  138. def _assert_counts(noitf_count: int, highlight_count: int) -> None:
  139. counts = self.get_success(
  140. self.store.db_pool.runInteraction(
  141. "get-unread-counts",
  142. self.store._get_unread_counts_by_receipt_txn,
  143. room_id,
  144. user_id,
  145. )
  146. )
  147. self.assertEqual(
  148. counts.main_timeline,
  149. NotifCounts(
  150. notify_count=noitf_count,
  151. unread_count=0,
  152. highlight_count=highlight_count,
  153. ),
  154. )
  155. self.assertEqual(counts.threads, {})
  156. def _create_event(highlight: bool = False) -> str:
  157. result = self.helper.send_event(
  158. room_id,
  159. type="m.room.message",
  160. content={"msgtype": "m.text", "body": user_id if highlight else "msg"},
  161. tok=other_token,
  162. )
  163. nonlocal last_event_id
  164. last_event_id = result["event_id"]
  165. return last_event_id
  166. def _rotate() -> None:
  167. self.get_success(self.store._rotate_notifs())
  168. def _mark_read(event_id: str) -> None:
  169. self.get_success(
  170. self.store.insert_receipt(
  171. room_id,
  172. "m.read",
  173. user_id=user_id,
  174. event_ids=[event_id],
  175. thread_id=None,
  176. data={},
  177. )
  178. )
  179. _assert_counts(0, 0)
  180. _create_event()
  181. _assert_counts(1, 0)
  182. _rotate()
  183. _assert_counts(1, 0)
  184. event_id = _create_event()
  185. _assert_counts(2, 0)
  186. _rotate()
  187. _assert_counts(2, 0)
  188. _create_event()
  189. _mark_read(event_id)
  190. _assert_counts(1, 0)
  191. _mark_read(last_event_id)
  192. _assert_counts(0, 0)
  193. _create_event()
  194. _assert_counts(1, 0)
  195. _rotate()
  196. _assert_counts(1, 0)
  197. # Delete old event push actions, this should not affect the (summarised) count.
  198. #
  199. # All event push actions are kept for 24 hours, so need to move forward
  200. # in time.
  201. self.pump(60 * 60 * 24)
  202. self.get_success(self.store._remove_old_push_actions_that_have_rotated())
  203. # Double check that the event push actions have been cleared (i.e. that
  204. # any results *must* come from the summary).
  205. result = self.get_success(
  206. self.store.db_pool.simple_select_list(
  207. table="event_push_actions",
  208. keyvalues={"1": 1},
  209. retcols=("event_id",),
  210. desc="",
  211. )
  212. )
  213. self.assertEqual(result, [])
  214. _assert_counts(1, 0)
  215. _mark_read(last_event_id)
  216. _assert_counts(0, 0)
  217. event_id = _create_event(True)
  218. _assert_counts(1, 1)
  219. _rotate()
  220. _assert_counts(1, 1)
  221. # Check that adding another notification and rotating after highlight
  222. # works.
  223. _create_event()
  224. _rotate()
  225. _assert_counts(2, 1)
  226. # Check that sending read receipts at different points results in the
  227. # right counts.
  228. _mark_read(event_id)
  229. _assert_counts(1, 0)
  230. _mark_read(last_event_id)
  231. _assert_counts(0, 0)
  232. _create_event(True)
  233. _assert_counts(1, 1)
  234. _mark_read(last_event_id)
  235. _assert_counts(0, 0)
  236. _rotate()
  237. _assert_counts(0, 0)
  238. def test_count_aggregation_threads(self) -> None:
  239. """
  240. This is essentially the same test as test_count_aggregation, but adds
  241. events to the main timeline and to a thread.
  242. """
  243. user_id, token, _, other_token, room_id = self._create_users_and_room()
  244. thread_id: str
  245. last_event_id: str
  246. def _assert_counts(
  247. noitf_count: int,
  248. highlight_count: int,
  249. thread_notif_count: int,
  250. thread_highlight_count: int,
  251. ) -> None:
  252. counts = self.get_success(
  253. self.store.db_pool.runInteraction(
  254. "get-unread-counts",
  255. self.store._get_unread_counts_by_receipt_txn,
  256. room_id,
  257. user_id,
  258. )
  259. )
  260. self.assertEqual(
  261. counts.main_timeline,
  262. NotifCounts(
  263. notify_count=noitf_count,
  264. unread_count=0,
  265. highlight_count=highlight_count,
  266. ),
  267. )
  268. if thread_notif_count or thread_highlight_count:
  269. self.assertEqual(
  270. counts.threads,
  271. {
  272. thread_id: NotifCounts(
  273. notify_count=thread_notif_count,
  274. unread_count=0,
  275. highlight_count=thread_highlight_count,
  276. ),
  277. },
  278. )
  279. else:
  280. self.assertEqual(counts.threads, {})
  281. def _create_event(
  282. highlight: bool = False, thread_id: Optional[str] = None
  283. ) -> str:
  284. content: JsonDict = {
  285. "msgtype": "m.text",
  286. "body": user_id if highlight else "msg",
  287. }
  288. if thread_id:
  289. content["m.relates_to"] = {
  290. "rel_type": "m.thread",
  291. "event_id": thread_id,
  292. }
  293. result = self.helper.send_event(
  294. room_id,
  295. type="m.room.message",
  296. content=content,
  297. tok=other_token,
  298. )
  299. nonlocal last_event_id
  300. last_event_id = result["event_id"]
  301. return last_event_id
  302. def _rotate() -> None:
  303. self.get_success(self.store._rotate_notifs())
  304. def _mark_read(event_id: str, thread_id: str = MAIN_TIMELINE) -> None:
  305. self.get_success(
  306. self.store.insert_receipt(
  307. room_id,
  308. "m.read",
  309. user_id=user_id,
  310. event_ids=[event_id],
  311. thread_id=thread_id,
  312. data={},
  313. )
  314. )
  315. _assert_counts(0, 0, 0, 0)
  316. thread_id = _create_event()
  317. _assert_counts(1, 0, 0, 0)
  318. _rotate()
  319. _assert_counts(1, 0, 0, 0)
  320. _create_event(thread_id=thread_id)
  321. _assert_counts(1, 0, 1, 0)
  322. _rotate()
  323. _assert_counts(1, 0, 1, 0)
  324. _create_event()
  325. _assert_counts(2, 0, 1, 0)
  326. _rotate()
  327. _assert_counts(2, 0, 1, 0)
  328. event_id = _create_event(thread_id=thread_id)
  329. _assert_counts(2, 0, 2, 0)
  330. _rotate()
  331. _assert_counts(2, 0, 2, 0)
  332. _create_event()
  333. _create_event(thread_id=thread_id)
  334. _mark_read(event_id)
  335. _assert_counts(1, 0, 3, 0)
  336. _mark_read(event_id, thread_id)
  337. _assert_counts(1, 0, 1, 0)
  338. _mark_read(last_event_id)
  339. _mark_read(last_event_id, thread_id)
  340. _assert_counts(0, 0, 0, 0)
  341. _create_event()
  342. _create_event(thread_id=thread_id)
  343. _assert_counts(1, 0, 1, 0)
  344. _rotate()
  345. _assert_counts(1, 0, 1, 0)
  346. # Delete old event push actions, this should not affect the (summarised) count.
  347. self.get_success(self.store._remove_old_push_actions_that_have_rotated())
  348. _assert_counts(1, 0, 1, 0)
  349. _mark_read(last_event_id)
  350. _mark_read(last_event_id, thread_id)
  351. _assert_counts(0, 0, 0, 0)
  352. _create_event(True)
  353. _assert_counts(1, 1, 0, 0)
  354. _rotate()
  355. _assert_counts(1, 1, 0, 0)
  356. event_id = _create_event(True, thread_id)
  357. _assert_counts(1, 1, 1, 1)
  358. _rotate()
  359. _assert_counts(1, 1, 1, 1)
  360. # Check that adding another notification and rotating after highlight
  361. # works.
  362. _create_event()
  363. _rotate()
  364. _assert_counts(2, 1, 1, 1)
  365. _create_event(thread_id=thread_id)
  366. _rotate()
  367. _assert_counts(2, 1, 2, 1)
  368. # Check that sending read receipts at different points results in the
  369. # right counts.
  370. _mark_read(event_id)
  371. _assert_counts(1, 0, 2, 1)
  372. _mark_read(event_id, thread_id)
  373. _assert_counts(1, 0, 1, 0)
  374. _mark_read(last_event_id)
  375. _assert_counts(0, 0, 1, 0)
  376. _mark_read(last_event_id, thread_id)
  377. _assert_counts(0, 0, 0, 0)
  378. _create_event(True)
  379. _create_event(True, thread_id)
  380. _assert_counts(1, 1, 1, 1)
  381. _mark_read(last_event_id)
  382. _mark_read(last_event_id, thread_id)
  383. _assert_counts(0, 0, 0, 0)
  384. _rotate()
  385. _assert_counts(0, 0, 0, 0)
  386. def test_count_aggregation_mixed(self) -> None:
  387. """
  388. This is essentially the same test as test_count_aggregation_threads, but
  389. sends both unthreaded and threaded receipts.
  390. """
  391. user_id, token, _, other_token, room_id = self._create_users_and_room()
  392. thread_id: str
  393. last_event_id: str
  394. def _assert_counts(
  395. noitf_count: int,
  396. highlight_count: int,
  397. thread_notif_count: int,
  398. thread_highlight_count: int,
  399. ) -> None:
  400. counts = self.get_success(
  401. self.store.db_pool.runInteraction(
  402. "get-unread-counts",
  403. self.store._get_unread_counts_by_receipt_txn,
  404. room_id,
  405. user_id,
  406. )
  407. )
  408. self.assertEqual(
  409. counts.main_timeline,
  410. NotifCounts(
  411. notify_count=noitf_count,
  412. unread_count=0,
  413. highlight_count=highlight_count,
  414. ),
  415. )
  416. if thread_notif_count or thread_highlight_count:
  417. self.assertEqual(
  418. counts.threads,
  419. {
  420. thread_id: NotifCounts(
  421. notify_count=thread_notif_count,
  422. unread_count=0,
  423. highlight_count=thread_highlight_count,
  424. ),
  425. },
  426. )
  427. else:
  428. self.assertEqual(counts.threads, {})
  429. def _create_event(
  430. highlight: bool = False, thread_id: Optional[str] = None
  431. ) -> str:
  432. content: JsonDict = {
  433. "msgtype": "m.text",
  434. "body": user_id if highlight else "msg",
  435. }
  436. if thread_id:
  437. content["m.relates_to"] = {
  438. "rel_type": "m.thread",
  439. "event_id": thread_id,
  440. }
  441. result = self.helper.send_event(
  442. room_id,
  443. type="m.room.message",
  444. content=content,
  445. tok=other_token,
  446. )
  447. nonlocal last_event_id
  448. last_event_id = result["event_id"]
  449. return last_event_id
  450. def _rotate() -> None:
  451. self.get_success(self.store._rotate_notifs())
  452. def _mark_read(event_id: str, thread_id: Optional[str] = None) -> None:
  453. self.get_success(
  454. self.store.insert_receipt(
  455. room_id,
  456. "m.read",
  457. user_id=user_id,
  458. event_ids=[event_id],
  459. thread_id=thread_id,
  460. data={},
  461. )
  462. )
  463. _assert_counts(0, 0, 0, 0)
  464. thread_id = _create_event()
  465. _assert_counts(1, 0, 0, 0)
  466. _rotate()
  467. _assert_counts(1, 0, 0, 0)
  468. _create_event(thread_id=thread_id)
  469. _assert_counts(1, 0, 1, 0)
  470. _rotate()
  471. _assert_counts(1, 0, 1, 0)
  472. _create_event()
  473. _assert_counts(2, 0, 1, 0)
  474. _rotate()
  475. _assert_counts(2, 0, 1, 0)
  476. event_id = _create_event(thread_id=thread_id)
  477. _assert_counts(2, 0, 2, 0)
  478. _rotate()
  479. _assert_counts(2, 0, 2, 0)
  480. _create_event()
  481. _create_event(thread_id=thread_id)
  482. _mark_read(event_id)
  483. _assert_counts(1, 0, 1, 0)
  484. _mark_read(last_event_id, MAIN_TIMELINE)
  485. _mark_read(last_event_id, thread_id)
  486. _assert_counts(0, 0, 0, 0)
  487. _create_event()
  488. _create_event(thread_id=thread_id)
  489. _assert_counts(1, 0, 1, 0)
  490. _rotate()
  491. _assert_counts(1, 0, 1, 0)
  492. # Delete old event push actions, this should not affect the (summarised) count.
  493. self.get_success(self.store._remove_old_push_actions_that_have_rotated())
  494. _assert_counts(1, 0, 1, 0)
  495. _mark_read(last_event_id)
  496. _assert_counts(0, 0, 0, 0)
  497. _create_event(True)
  498. _assert_counts(1, 1, 0, 0)
  499. _rotate()
  500. _assert_counts(1, 1, 0, 0)
  501. event_id = _create_event(True, thread_id)
  502. _assert_counts(1, 1, 1, 1)
  503. _rotate()
  504. _assert_counts(1, 1, 1, 1)
  505. # Check that adding another notification and rotating after highlight
  506. # works.
  507. _create_event()
  508. _rotate()
  509. _assert_counts(2, 1, 1, 1)
  510. _create_event(thread_id=thread_id)
  511. _rotate()
  512. _assert_counts(2, 1, 2, 1)
  513. # Check that sending read receipts at different points results in the
  514. # right counts.
  515. _mark_read(event_id)
  516. _assert_counts(1, 0, 1, 0)
  517. _mark_read(event_id, MAIN_TIMELINE)
  518. _assert_counts(1, 0, 1, 0)
  519. _mark_read(last_event_id, MAIN_TIMELINE)
  520. _assert_counts(0, 0, 1, 0)
  521. _mark_read(last_event_id, thread_id)
  522. _assert_counts(0, 0, 0, 0)
  523. _create_event(True)
  524. _create_event(True, thread_id)
  525. _assert_counts(1, 1, 1, 1)
  526. _mark_read(last_event_id)
  527. _assert_counts(0, 0, 0, 0)
  528. _rotate()
  529. _assert_counts(0, 0, 0, 0)
  530. def test_recursive_thread(self) -> None:
  531. """
  532. Events related to events in a thread should still be considered part of
  533. that thread.
  534. """
  535. # Create a user to receive notifications and send receipts.
  536. user_id = self.register_user("user1235", "pass")
  537. token = self.login("user1235", "pass")
  538. # And another users to send events.
  539. other_id = self.register_user("other", "pass")
  540. other_token = self.login("other", "pass")
  541. # Create a room and put both users in it.
  542. room_id = self.helper.create_room_as(user_id, tok=token)
  543. self.helper.join(room_id, other_id, tok=other_token)
  544. # Update the user's push rules to care about reaction events.
  545. self.get_success(
  546. self.store.add_push_rule(
  547. user_id,
  548. "related_events",
  549. priority_class=5,
  550. conditions=[
  551. {"kind": "event_match", "key": "type", "pattern": "m.reaction"}
  552. ],
  553. actions=["notify"],
  554. )
  555. )
  556. def _create_event(type: str, content: JsonDict) -> str:
  557. result = self.helper.send_event(
  558. room_id, type=type, content=content, tok=other_token
  559. )
  560. return result["event_id"]
  561. def _assert_counts(noitf_count: int, thread_notif_count: int) -> None:
  562. counts = self.get_success(
  563. self.store.db_pool.runInteraction(
  564. "get-unread-counts",
  565. self.store._get_unread_counts_by_receipt_txn,
  566. room_id,
  567. user_id,
  568. )
  569. )
  570. self.assertEqual(
  571. counts.main_timeline,
  572. NotifCounts(
  573. notify_count=noitf_count, unread_count=0, highlight_count=0
  574. ),
  575. )
  576. if thread_notif_count:
  577. self.assertEqual(
  578. counts.threads,
  579. {
  580. thread_id: NotifCounts(
  581. notify_count=thread_notif_count,
  582. unread_count=0,
  583. highlight_count=0,
  584. ),
  585. },
  586. )
  587. else:
  588. self.assertEqual(counts.threads, {})
  589. # Create a root event.
  590. thread_id = _create_event(
  591. "m.room.message", {"msgtype": "m.text", "body": "msg"}
  592. )
  593. _assert_counts(1, 0)
  594. # Reply, creating a thread.
  595. reply_id = _create_event(
  596. "m.room.message",
  597. {
  598. "msgtype": "m.text",
  599. "body": "msg",
  600. "m.relates_to": {
  601. "rel_type": "m.thread",
  602. "event_id": thread_id,
  603. },
  604. },
  605. )
  606. _assert_counts(1, 1)
  607. # Create an event related to a thread event, this should still appear in
  608. # the thread.
  609. _create_event(
  610. type="m.reaction",
  611. content={
  612. "m.relates_to": {
  613. "rel_type": "m.annotation",
  614. "event_id": reply_id,
  615. "key": "A",
  616. }
  617. },
  618. )
  619. _assert_counts(1, 2)
  620. def test_find_first_stream_ordering_after_ts(self) -> None:
  621. def add_event(so: int, ts: int) -> None:
  622. self.get_success(
  623. self.store.db_pool.simple_insert(
  624. "events",
  625. {
  626. "stream_ordering": so,
  627. "received_ts": ts,
  628. "event_id": "event%i" % so,
  629. "type": "",
  630. "room_id": "",
  631. "content": "",
  632. "processed": True,
  633. "outlier": False,
  634. "topological_ordering": 0,
  635. "depth": 0,
  636. },
  637. )
  638. )
  639. # start with the base case where there are no events in the table
  640. r = self.get_success(self.store.find_first_stream_ordering_after_ts(11))
  641. self.assertEqual(r, 0)
  642. # now with one event
  643. add_event(2, 10)
  644. r = self.get_success(self.store.find_first_stream_ordering_after_ts(9))
  645. self.assertEqual(r, 2)
  646. r = self.get_success(self.store.find_first_stream_ordering_after_ts(10))
  647. self.assertEqual(r, 2)
  648. r = self.get_success(self.store.find_first_stream_ordering_after_ts(11))
  649. self.assertEqual(r, 3)
  650. # add a bunch of dummy events to the events table
  651. for (stream_ordering, ts) in (
  652. (3, 110),
  653. (4, 120),
  654. (5, 120),
  655. (10, 130),
  656. (20, 140),
  657. ):
  658. add_event(stream_ordering, ts)
  659. r = self.get_success(self.store.find_first_stream_ordering_after_ts(110))
  660. self.assertEqual(r, 3, "First event after 110ms should be 3, was %i" % r)
  661. # 4 and 5 are both after 120: we want 4 rather than 5
  662. r = self.get_success(self.store.find_first_stream_ordering_after_ts(120))
  663. self.assertEqual(r, 4, "First event after 120ms should be 4, was %i" % r)
  664. r = self.get_success(self.store.find_first_stream_ordering_after_ts(129))
  665. self.assertEqual(r, 10, "First event after 129ms should be 10, was %i" % r)
  666. # check we can get the last event
  667. r = self.get_success(self.store.find_first_stream_ordering_after_ts(140))
  668. self.assertEqual(r, 20, "First event after 14ms should be 20, was %i" % r)
  669. # off the end
  670. r = self.get_success(self.store.find_first_stream_ordering_after_ts(160))
  671. self.assertEqual(r, 21)
  672. # check we can find an event at ordering zero
  673. add_event(0, 5)
  674. r = self.get_success(self.store.find_first_stream_ordering_after_ts(1))
  675. self.assertEqual(r, 0)