diff --git a/tests/storage/test_event_push_actions.py b/tests/storage/test_event_push_actions.py
index ba40124c8a..62fd4aeb2f 100644
--- a/tests/storage/test_event_push_actions.py
+++ b/tests/storage/test_event_push_actions.py
@@ -135,7 +135,22 @@ class EventPushActionsStoreTestCase(HomeserverTestCase):
_assert_counts(1, 1, 0)
# Delete old event push actions, this should not affect the (summarised) count.
+ #
+ # All event push actions are kept for 24 hours, so need to move forward
+ # in time.
+ self.pump(60 * 60 * 24)
self.get_success(self.store._remove_old_push_actions_that_have_rotated())
+ # Double check that the event push actions have been cleared (i.e. that
+ # any results *must* come from the summary).
+ result = self.get_success(
+ self.store.db_pool.simple_select_list(
+ table="event_push_actions",
+ keyvalues={"1": 1},
+ retcols=("event_id",),
+ desc="",
+ )
+ )
+ self.assertEqual(result, [])
_assert_counts(1, 1, 0)
_mark_read(last_event_id)
diff --git a/tests/storage/test_events.py b/tests/storage/test_events.py
index 2ff88e64a5..3ce4f35cb7 100644
--- a/tests/storage/test_events.py
+++ b/tests/storage/test_events.py
@@ -70,7 +70,11 @@ class ExtremPruneTestCase(HomeserverTestCase):
def persist_event(self, event, state=None):
"""Persist the event, with optional state"""
context = self.get_success(
- self.state.compute_event_context(event, state_ids_before_event=state)
+ self.state.compute_event_context(
+ event,
+ state_ids_before_event=state,
+ partial_state=None if state is None else False,
+ )
)
self.get_success(self._persistence.persist_event(event, context))
@@ -148,6 +152,7 @@ class ExtremPruneTestCase(HomeserverTestCase):
self.state.compute_event_context(
remote_event_2,
state_ids_before_event=state_before_gap,
+ partial_state=False,
)
)
diff --git a/tests/storage/test_receipts.py b/tests/storage/test_receipts.py
new file mode 100644
index 0000000000..191c957fb5
--- /dev/null
+++ b/tests/storage/test_receipts.py
@@ -0,0 +1,282 @@
+# Copyright 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from parameterized import parameterized
+
+from synapse.api.constants import ReceiptTypes
+from synapse.types import UserID, create_requester
+
+from tests.test_utils.event_injection import create_event
+from tests.unittest import HomeserverTestCase
+
+OTHER_USER_ID = "@other:test"
+OUR_USER_ID = "@our:test"
+
+
+class ReceiptTestCase(HomeserverTestCase):
+ def prepare(self, reactor, clock, homeserver) -> None:
+ super().prepare(reactor, clock, homeserver)
+
+ self.store = homeserver.get_datastores().main
+
+ self.room_creator = homeserver.get_room_creation_handler()
+ self.persist_event_storage_controller = (
+ self.hs.get_storage_controllers().persistence
+ )
+
+ # Create a test user
+ self.ourUser = UserID.from_string(OUR_USER_ID)
+ self.ourRequester = create_requester(self.ourUser)
+
+ # Create a second test user
+ self.otherUser = UserID.from_string(OTHER_USER_ID)
+ self.otherRequester = create_requester(self.otherUser)
+
+ # Create a test room
+ info, _ = self.get_success(self.room_creator.create_room(self.ourRequester, {}))
+ self.room_id1 = info["room_id"]
+
+ # Create a second test room
+ info, _ = self.get_success(self.room_creator.create_room(self.ourRequester, {}))
+ self.room_id2 = info["room_id"]
+
+ # Join the second user to the first room
+ memberEvent, memberEventContext = self.get_success(
+ create_event(
+ self.hs,
+ room_id=self.room_id1,
+ type="m.room.member",
+ sender=self.otherRequester.user.to_string(),
+ state_key=self.otherRequester.user.to_string(),
+ content={"membership": "join"},
+ )
+ )
+ self.get_success(
+ self.persist_event_storage_controller.persist_event(
+ memberEvent, memberEventContext
+ )
+ )
+
+ # Join the second user to the second room
+ memberEvent, memberEventContext = self.get_success(
+ create_event(
+ self.hs,
+ room_id=self.room_id2,
+ type="m.room.member",
+ sender=self.otherRequester.user.to_string(),
+ state_key=self.otherRequester.user.to_string(),
+ content={"membership": "join"},
+ )
+ )
+ self.get_success(
+ self.persist_event_storage_controller.persist_event(
+ memberEvent, memberEventContext
+ )
+ )
+
+ def test_return_empty_with_no_data(self) -> None:
+ res = self.get_success(
+ self.store.get_receipts_for_user(
+ OUR_USER_ID,
+ [
+ ReceiptTypes.READ,
+ ReceiptTypes.READ_PRIVATE,
+ ReceiptTypes.UNSTABLE_READ_PRIVATE,
+ ],
+ )
+ )
+ self.assertEqual(res, {})
+
+ res = self.get_success(
+ self.store.get_receipts_for_user_with_orderings(
+ OUR_USER_ID,
+ [
+ ReceiptTypes.READ,
+ ReceiptTypes.READ_PRIVATE,
+ ReceiptTypes.UNSTABLE_READ_PRIVATE,
+ ],
+ )
+ )
+ self.assertEqual(res, {})
+
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID,
+ self.room_id1,
+ [
+ ReceiptTypes.READ,
+ ReceiptTypes.READ_PRIVATE,
+ ReceiptTypes.UNSTABLE_READ_PRIVATE,
+ ],
+ )
+ )
+ self.assertEqual(res, None)
+
+ @parameterized.expand(
+ [ReceiptTypes.READ_PRIVATE, ReceiptTypes.UNSTABLE_READ_PRIVATE]
+ )
+ def test_get_receipts_for_user(self, receipt_type: str) -> None:
+ # Send some events into the first room
+ event1_1_id = self.create_and_send_event(
+ self.room_id1, UserID.from_string(OTHER_USER_ID)
+ )
+ event1_2_id = self.create_and_send_event(
+ self.room_id1, UserID.from_string(OTHER_USER_ID)
+ )
+
+ # Send public read receipt for the first event
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, ReceiptTypes.READ, OUR_USER_ID, [event1_1_id], {}
+ )
+ )
+ # Send private read receipt for the second event
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, receipt_type, OUR_USER_ID, [event1_2_id], {}
+ )
+ )
+
+ # Test we get the latest event when we want both private and public receipts
+ res = self.get_success(
+ self.store.get_receipts_for_user(
+ OUR_USER_ID, [ReceiptTypes.READ, receipt_type]
+ )
+ )
+ self.assertEqual(res, {self.room_id1: event1_2_id})
+
+ # Test we get the older event when we want only public receipt
+ res = self.get_success(
+ self.store.get_receipts_for_user(OUR_USER_ID, [ReceiptTypes.READ])
+ )
+ self.assertEqual(res, {self.room_id1: event1_1_id})
+
+ # Test we get the latest event when we want only the public receipt
+ res = self.get_success(
+ self.store.get_receipts_for_user(OUR_USER_ID, [receipt_type])
+ )
+ self.assertEqual(res, {self.room_id1: event1_2_id})
+
+ # Test receipt updating
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, ReceiptTypes.READ, OUR_USER_ID, [event1_2_id], {}
+ )
+ )
+ res = self.get_success(
+ self.store.get_receipts_for_user(OUR_USER_ID, [ReceiptTypes.READ])
+ )
+ self.assertEqual(res, {self.room_id1: event1_2_id})
+
+ # Send some events into the second room
+ event2_1_id = self.create_and_send_event(
+ self.room_id2, UserID.from_string(OTHER_USER_ID)
+ )
+
+ # Test new room is reflected in what the method returns
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id2, receipt_type, OUR_USER_ID, [event2_1_id], {}
+ )
+ )
+ res = self.get_success(
+ self.store.get_receipts_for_user(
+ OUR_USER_ID, [ReceiptTypes.READ, receipt_type]
+ )
+ )
+ self.assertEqual(res, {self.room_id1: event1_2_id, self.room_id2: event2_1_id})
+
+ @parameterized.expand(
+ [ReceiptTypes.READ_PRIVATE, ReceiptTypes.UNSTABLE_READ_PRIVATE]
+ )
+ def test_get_last_receipt_event_id_for_user(self, receipt_type: str) -> None:
+ # Send some events into the first room
+ event1_1_id = self.create_and_send_event(
+ self.room_id1, UserID.from_string(OTHER_USER_ID)
+ )
+ event1_2_id = self.create_and_send_event(
+ self.room_id1, UserID.from_string(OTHER_USER_ID)
+ )
+
+ # Send public read receipt for the first event
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, ReceiptTypes.READ, OUR_USER_ID, [event1_1_id], {}
+ )
+ )
+ # Send private read receipt for the second event
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, receipt_type, OUR_USER_ID, [event1_2_id], {}
+ )
+ )
+
+ # Test we get the latest event when we want both private and public receipts
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID,
+ self.room_id1,
+ [ReceiptTypes.READ, receipt_type],
+ )
+ )
+ self.assertEqual(res, event1_2_id)
+
+ # Test we get the older event when we want only public receipt
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID, self.room_id1, [ReceiptTypes.READ]
+ )
+ )
+ self.assertEqual(res, event1_1_id)
+
+ # Test we get the latest event when we want only the private receipt
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID, self.room_id1, [receipt_type]
+ )
+ )
+ self.assertEqual(res, event1_2_id)
+
+ # Test receipt updating
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id1, ReceiptTypes.READ, OUR_USER_ID, [event1_2_id], {}
+ )
+ )
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID, self.room_id1, [ReceiptTypes.READ]
+ )
+ )
+ self.assertEqual(res, event1_2_id)
+
+ # Send some events into the second room
+ event2_1_id = self.create_and_send_event(
+ self.room_id2, UserID.from_string(OTHER_USER_ID)
+ )
+
+ # Test new room is reflected in what the method returns
+ self.get_success(
+ self.store.insert_receipt(
+ self.room_id2, receipt_type, OUR_USER_ID, [event2_1_id], {}
+ )
+ )
+ res = self.get_success(
+ self.store.get_last_receipt_event_id_for_user(
+ OUR_USER_ID,
+ self.room_id2,
+ [ReceiptTypes.READ, receipt_type],
+ )
+ )
+ self.assertEqual(res, event2_1_id)
diff --git a/tests/storage/test_roommember.py b/tests/storage/test_roommember.py
index 240b02cb9f..ceec690285 100644
--- a/tests/storage/test_roommember.py
+++ b/tests/storage/test_roommember.py
@@ -23,6 +23,7 @@ from synapse.util import Clock
from tests import unittest
from tests.server import TestHomeServer
+from tests.test_utils import event_injection
class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
@@ -157,6 +158,75 @@ class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
# Check that alice's display name is now None
self.assertEqual(row[0]["display_name"], None)
+ def test_room_is_locally_forgotten(self):
+ """Test that when the last local user has forgotten a room it is known as forgotten."""
+ # join two local and one remote user
+ self.room = self.helper.create_room_as(self.u_alice, tok=self.t_alice)
+ self.get_success(
+ event_injection.inject_member_event(self.hs, self.room, self.u_bob, "join")
+ )
+ self.get_success(
+ event_injection.inject_member_event(
+ self.hs, self.room, self.u_charlie.to_string(), "join"
+ )
+ )
+ self.assertFalse(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ # local users leave the room and the room is not forgotten
+ self.get_success(
+ event_injection.inject_member_event(
+ self.hs, self.room, self.u_alice, "leave"
+ )
+ )
+ self.get_success(
+ event_injection.inject_member_event(self.hs, self.room, self.u_bob, "leave")
+ )
+ self.assertFalse(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ # first user forgets the room, room is not forgotten
+ self.get_success(self.store.forget(self.u_alice, self.room))
+ self.assertFalse(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ # second (last local) user forgets the room and the room is forgotten
+ self.get_success(self.store.forget(self.u_bob, self.room))
+ self.assertTrue(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ def test_join_locally_forgotten_room(self):
+ """Tests if a user joins a forgotten room the room is not forgotten anymore."""
+ self.room = self.helper.create_room_as(self.u_alice, tok=self.t_alice)
+ self.assertFalse(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ # after leaving and forget the room, it is forgotten
+ self.get_success(
+ event_injection.inject_member_event(
+ self.hs, self.room, self.u_alice, "leave"
+ )
+ )
+ self.get_success(self.store.forget(self.u_alice, self.room))
+ self.assertTrue(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
+ # after rejoin the room is not forgotten anymore
+ self.get_success(
+ event_injection.inject_member_event(
+ self.hs, self.room, self.u_alice, "join"
+ )
+ )
+ self.assertFalse(
+ self.get_success(self.store.is_locally_forgotten_room(self.room))
+ )
+
class CurrentStateMembershipUpdateTestCase(unittest.HomeserverTestCase):
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
diff --git a/tests/storage/test_state.py b/tests/storage/test_state.py
index 8043bdbde2..5564161750 100644
--- a/tests/storage/test_state.py
+++ b/tests/storage/test_state.py
@@ -369,8 +369,8 @@ class StateStoreTestCase(HomeserverTestCase):
state_dict_ids = cache_entry.value
self.assertEqual(cache_entry.full, False)
- self.assertEqual(cache_entry.known_absent, {(e1.type, e1.state_key)})
- self.assertDictEqual(state_dict_ids, {(e1.type, e1.state_key): e1.event_id})
+ self.assertEqual(cache_entry.known_absent, set())
+ self.assertDictEqual(state_dict_ids, {})
############################################
# test that things work with a partial cache
@@ -387,7 +387,7 @@ class StateStoreTestCase(HomeserverTestCase):
)
self.assertEqual(is_all, False)
- self.assertDictEqual({(e1.type, e1.state_key): e1.event_id}, state_dict)
+ self.assertDictEqual({}, state_dict)
room_id = self.room.to_string()
(state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
@@ -412,7 +412,7 @@ class StateStoreTestCase(HomeserverTestCase):
)
self.assertEqual(is_all, False)
- self.assertDictEqual({(e1.type, e1.state_key): e1.event_id}, state_dict)
+ self.assertDictEqual({}, state_dict)
(state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
self.state_datastore._state_group_members_cache,
@@ -443,7 +443,7 @@ class StateStoreTestCase(HomeserverTestCase):
)
self.assertEqual(is_all, False)
- self.assertDictEqual({(e1.type, e1.state_key): e1.event_id}, state_dict)
+ self.assertDictEqual({}, state_dict)
(state_dict, is_all,) = self.state_datastore._get_state_for_group_using_cache(
self.state_datastore._state_group_members_cache,
|