diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/events/__init__.py | 15 | ||||
-rw-r--r-- | tests/events/test_events.py | 217 | ||||
-rw-r--r-- | tests/federation/test_federation.py | 25 | ||||
-rw-r--r-- | tests/handlers/test_federation.py | 55 | ||||
-rw-r--r-- | tests/handlers/test_presence.py | 10 | ||||
-rw-r--r-- | tests/handlers/test_room.py | 320 | ||||
-rw-r--r-- | tests/handlers/test_typing.py | 148 | ||||
-rw-r--r-- | tests/rest/test_events.py | 4 | ||||
-rw-r--r-- | tests/rest/test_rooms.py | 6 | ||||
-rw-r--r-- | tests/rest/test_typing.py | 115 | ||||
-rw-r--r-- | tests/storage/test_redaction.py | 141 | ||||
-rw-r--r-- | tests/storage/test_room.py | 8 | ||||
-rw-r--r-- | tests/storage/test_roommember.py | 52 | ||||
-rw-r--r-- | tests/storage/test_stream.py | 85 | ||||
-rw-r--r-- | tests/test_distributor.py | 27 | ||||
-rw-r--r-- | tests/test_state.py | 102 | ||||
-rw-r--r-- | tests/test_test_utils.py | 70 | ||||
-rw-r--r-- | tests/test_types.py | 18 | ||||
-rw-r--r-- | tests/utils.py | 44 |
19 files changed, 797 insertions, 665 deletions
diff --git a/tests/events/__init__.py b/tests/events/__init__.py deleted file mode 100644 index 9bff9ec169..0000000000 --- a/tests/events/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2014 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. - diff --git a/tests/events/test_events.py b/tests/events/test_events.py deleted file mode 100644 index 91d1d44fee..0000000000 --- a/tests/events/test_events.py +++ /dev/null @@ -1,217 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2014 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 synapse.api.events import SynapseEvent -from synapse.api.events.validator import EventValidator -from synapse.api.errors import SynapseError - -from tests import unittest - - -class SynapseTemplateCheckTestCase(unittest.TestCase): - - def setUp(self): - self.validator = EventValidator(None) - - def tearDown(self): - pass - - def test_top_level_keys(self): - template = { - "person": {}, - "friends": ["string"] - } - - content = { - "person": {"name": "bob"}, - "friends": ["jill", "mike"] - } - - event = MockSynapseEvent(template) - event.content = content - self.assertTrue(self.validator.validate(event)) - - content = { - "person": {"name": "bob"}, - "friends": ["jill"], - "enemies": ["mike"] - } - event.content = content - self.assertTrue(self.validator.validate(event)) - - content = { - "person": {"name": "bob"}, - # missing friends - "enemies": ["mike", "jill"] - } - event.content = content - self.assertRaises( - SynapseError, - self.validator.validate, - event - ) - - def test_lists(self): - template = { - "person": {}, - "friends": [{"name":"string"}] - } - - content = { - "person": {"name": "bob"}, - "friends": ["jill", "mike"] # should be in objects - } - - event = MockSynapseEvent(template) - event.content = content - self.assertRaises( - SynapseError, - self.validator.validate, - event - ) - - content = { - "person": {"name": "bob"}, - "friends": [{"name": "jill"}, {"name": "mike"}] - } - event.content = content - self.assertTrue(self.validator.validate(event)) - - def test_nested_lists(self): - template = { - "results": { - "families": [ - { - "name": "string", - "members": [ - {} - ] - } - ] - } - } - - content = { - "results": { - "families": [ - { - "name": "Smith", - "members": [ - "Alice", "Bob" # wrong types - ] - } - ] - } - } - - event = MockSynapseEvent(template) - event.content = content - self.assertRaises( - SynapseError, - self.validator.validate, - event - ) - - content = { - "results": { - "families": [ - { - "name": "Smith", - "members": [ - {"name": "Alice"}, {"name": "Bob"} - ] - } - ] - } - } - event.content = content - self.assertTrue(self.validator.validate(event)) - - def test_nested_keys(self): - template = { - "person": { - "attributes": { - "hair": "string", - "eye": "string" - }, - "age": 0, - "fav_books": ["string"] - } - } - event = MockSynapseEvent(template) - - content = { - "person": { - "attributes": { - "hair": "brown", - "eye": "green", - "skin": "purple" - }, - "age": 33, - "fav_books": ["lotr", "hobbit"], - "fav_music": ["abba", "beatles"] - } - } - - event.content = content - self.assertTrue(self.validator.validate(event)) - - content = { - "person": { - "attributes": { - "hair": "brown" - # missing eye - }, - "age": 33, - "fav_books": ["lotr", "hobbit"], - "fav_music": ["abba", "beatles"] - } - } - - event.content = content - self.assertRaises( - SynapseError, - self.validator.validate, - event - ) - - content = { - "person": { - "attributes": { - "hair": "brown", - "eye": "green", - "skin": "purple" - }, - "age": 33, - "fav_books": "nothing", # should be a list - } - } - - event.content = content - self.assertRaises( - SynapseError, - self.validator.validate, - event - ) - - -class MockSynapseEvent(SynapseEvent): - - def __init__(self, template): - self.template = template - - def get_content_template(self): - return self.template - diff --git a/tests/federation/test_federation.py b/tests/federation/test_federation.py index 73dd289276..3e484cd303 100644 --- a/tests/federation/test_federation.py +++ b/tests/federation/test_federation.py @@ -23,24 +23,20 @@ from ..utils import MockHttpResource, MockClock, MockKey from synapse.server import HomeServer from synapse.federation import initialize_http_replication -from synapse.api.events import SynapseEvent +from synapse.events import FrozenEvent + +from synapse.storage.transactions import DestinationsTable def make_pdu(prev_pdus=[], **kwargs): """Provide some default fields for making a PduTuple.""" pdu_fields = { - "is_state": False, - "unrecognized_keys": [], - "outlier": False, - "have_processed": True, "state_key": None, - "power_level": None, - "prev_state_id": None, - "prev_state_origin": None, + "prev_events": prev_pdus, } pdu_fields.update(kwargs) - return SynapseEvent(prev_pdus=prev_pdus, **pdu_fields) + return FrozenEvent(pdu_fields) class FederationTestCase(unittest.TestCase): @@ -55,10 +51,16 @@ class FederationTestCase(unittest.TestCase): "delivered_txn", "get_received_txn_response", "set_received_txn_response", + "get_destination_retry_timings", + "get_auth_chain", ]) self.mock_persistence.get_received_txn_response.return_value = ( defer.succeed(None) ) + self.mock_persistence.get_destination_retry_timings.return_value = ( + defer.succeed(DestinationsTable.EntryType("", 0, 0)) + ) + self.mock_persistence.get_auth_chain.return_value = [] self.mock_config = Mock() self.mock_config.signing_key = [MockKey()] self.clock = MockClock() @@ -171,7 +173,7 @@ class FederationTestCase(unittest.TestCase): (200, "OK") ) - pdu = SynapseEvent( + pdu = make_pdu( event_id="abc123def456", origin="red", user_id="@a:red", @@ -180,10 +182,9 @@ class FederationTestCase(unittest.TestCase): origin_server_ts=123456789001, depth=1, content={"text": "Here is the message"}, - destinations=["remote"], ) - yield self.federation.send_pdu(pdu) + yield self.federation.send_pdu(pdu, ["remote"]) self.mock_http_client.put_json.assert_called_with( "remote", diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py index 33016c16ef..ed21defd13 100644 --- a/tests/handlers/test_federation.py +++ b/tests/handlers/test_federation.py @@ -16,11 +16,8 @@ from twisted.internet import defer from tests import unittest -from synapse.api.events.room import ( - MessageEvent, -) - -from synapse.api.events import SynapseEvent +from synapse.api.constants import EventTypes +from synapse.events import FrozenEvent from synapse.handlers.federation import FederationHandler from synapse.server import HomeServer @@ -37,7 +34,7 @@ class FederationTestCase(unittest.TestCase): self.mock_config.signing_key = [MockKey()] self.state_handler = NonCallableMock(spec_set=[ - "annotate_event_with_state", + "compute_event_context", ]) self.auth = NonCallableMock(spec_set=[ @@ -53,6 +50,8 @@ class FederationTestCase(unittest.TestCase): "persist_event", "store_room", "get_room", + "get_destination_retry_timings", + "set_destination_retry_timings", ]), resource_for_federation=NonCallableMock(), http_client=NonCallableMock(spec_set=[]), @@ -76,43 +75,47 @@ class FederationTestCase(unittest.TestCase): @defer.inlineCallbacks def test_msg(self): - pdu = SynapseEvent( - type=MessageEvent.TYPE, - room_id="foo", - content={"msgtype": u"fooo"}, - origin_server_ts=0, - event_id="$a:b", - user_id="@a:b", - origin="b", - auth_events=[], - hashes={"sha256":"AcLrgtUIqqwaGoHhrEvYG1YLDIsVPYJdSRGhkp3jJp8"}, - ) + pdu = FrozenEvent({ + "type": EventTypes.Message, + "room_id": "foo", + "content": {"msgtype": u"fooo"}, + "origin_server_ts": 0, + "event_id": "$a:b", + "user_id":"@a:b", + "origin": "b", + "auth_events": [], + "hashes": {"sha256":"AcLrgtUIqqwaGoHhrEvYG1YLDIsVPYJdSRGhkp3jJp8"}, + }) self.datastore.persist_event.return_value = defer.succeed(None) self.datastore.get_room.return_value = defer.succeed(True) self.auth.check_host_in_room.return_value = defer.succeed(True) def annotate(ev, old_state=None): - ev.old_state_events = [] - return defer.succeed(False) - self.state_handler.annotate_event_with_state.side_effect = annotate + context = Mock() + context.current_state = {} + context.auth_events = {} + return defer.succeed(context) + self.state_handler.compute_event_context.side_effect = annotate yield self.handlers.federation_handler.on_receive_pdu( "fo", pdu, False ) self.datastore.persist_event.assert_called_once_with( - ANY, is_new_state=False, backfilled=False, current_state=None + ANY, + is_new_state=True, + backfilled=False, + current_state=None, + context=ANY, ) - self.state_handler.annotate_event_with_state.assert_called_once_with( - ANY, - old_state=None, + self.state_handler.compute_event_context.assert_called_once_with( + ANY, old_state=None, ) self.auth.check.assert_called_once_with(ANY, auth_events={}) self.notifier.on_new_room_event.assert_called_once_with( - ANY, - extra_users=[] + ANY, extra_users=[] ) diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index fe69ce47eb..b85a89052a 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -30,7 +30,7 @@ from synapse.api.constants import PresenceState from synapse.api.errors import SynapseError from synapse.handlers.presence import PresenceHandler, UserPresenceCache from synapse.streams.config import SourcePaginationConfig - +from synapse.storage.transactions import DestinationsTable OFFLINE = PresenceState.OFFLINE UNAVAILABLE = PresenceState.UNAVAILABLE @@ -528,6 +528,7 @@ class PresencePushTestCase(unittest.TestCase): "delivered_txn", "get_received_txn_response", "set_received_txn_response", + "get_destination_retry_timings", ]), handlers=None, resource_for_client=Mock(), @@ -539,6 +540,9 @@ class PresencePushTestCase(unittest.TestCase): hs.handlers = JustPresenceHandlers(hs) self.datastore = hs.get_datastore() + self.datastore.get_destination_retry_timings.return_value = ( + defer.succeed(DestinationsTable.EntryType("", 0, 0)) + ) def get_received_txn_response(*args): return defer.succeed(None) @@ -1037,6 +1041,7 @@ class PresencePollingTestCase(unittest.TestCase): "delivered_txn", "get_received_txn_response", "set_received_txn_response", + "get_destination_retry_timings", ]), handlers=None, resource_for_client=Mock(), @@ -1048,6 +1053,9 @@ class PresencePollingTestCase(unittest.TestCase): hs.handlers = JustPresenceHandlers(hs) self.datastore = hs.get_datastore() + self.datastore.get_destination_retry_timings.return_value = ( + defer.succeed(DestinationsTable.EntryType("", 0, 0)) + ) def get_received_txn_response(*args): return defer.succeed(None) diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py index 0279ab703a..0cb8aa4fbc 100644 --- a/tests/handlers/test_room.py +++ b/tests/handlers/test_room.py @@ -17,10 +17,7 @@ from twisted.internet import defer from tests import unittest -from synapse.api.events.room import ( - RoomMemberEvent, -) -from synapse.api.constants import Membership +from synapse.api.constants import EventTypes, Membership from synapse.handlers.room import RoomMemberHandler, RoomCreationHandler from synapse.handlers.profile import ProfileHandler from synapse.server import HomeServer @@ -47,7 +44,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase): "get_room_member", "get_room", "store_room", - "snapshot_room", + "get_latest_events_in_room", ]), resource_for_federation=NonCallableMock(), http_client=NonCallableMock(spec_set=[]), @@ -63,7 +60,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase): "check_host_in_room", ]), state_handler=NonCallableMock(spec_set=[ - "annotate_event_with_state", + "compute_event_context", "get_current_state", ]), config=self.mock_config, @@ -91,9 +88,6 @@ class RoomMemberHandlerTestCase(unittest.TestCase): self.handlers.profile_handler = ProfileHandler(self.hs) self.room_member_handler = self.handlers.room_member_handler - self.snapshot = Mock() - self.datastore.snapshot_room.return_value = self.snapshot - self.ratelimiter = hs.get_ratelimiter() self.ratelimiter.send_message.return_value = (True, 0) @@ -104,50 +98,70 @@ class RoomMemberHandlerTestCase(unittest.TestCase): target_user_id = "@red:blue" content = {"membership": Membership.INVITE} - event = self.hs.get_event_factory().create_event( - etype=RoomMemberEvent.TYPE, - user_id=user_id, - state_key=target_user_id, - room_id=room_id, - membership=Membership.INVITE, - content=content, + builder = self.hs.get_event_builder_factory().new({ + "type": EventTypes.Member, + "sender": user_id, + "state_key": target_user_id, + "room_id": room_id, + "content": content, + }) + + self.datastore.get_latest_events_in_room.return_value = ( + defer.succeed([]) ) - self.auth.check_host_in_room.return_value = defer.succeed(True) + def annotate(_): + ctx = Mock() + ctx.current_state = { + (EventTypes.Member, "@alice:green"): self._create_member( + user_id="@alice:green", + room_id=room_id, + ), + (EventTypes.Member, "@bob:red"): self._create_member( + user_id="@bob:red", + room_id=room_id, + ), + } + ctx.prev_state_events = [] - store_id = "store_id_fooo" - self.datastore.persist_event.return_value = defer.succeed(store_id) + return defer.succeed(ctx) - self.datastore.get_room_member.return_value = defer.succeed(None) + self.state_handler.compute_event_context.side_effect = annotate - event.old_state_events = { - (RoomMemberEvent.TYPE, "@alice:green"): self._create_member( - user_id="@alice:green", - room_id=room_id, - ), - (RoomMemberEvent.TYPE, "@bob:red"): self._create_member( - user_id="@bob:red", - room_id=room_id, - ), - } + def add_auth(_, ctx): + ctx.auth_events = ctx.current_state[ + (EventTypes.Member, "@bob:red") + ] - event.state_events = event.old_state_events - event.state_events[(RoomMemberEvent.TYPE, target_user_id)] = event + return defer.succeed(True) + self.auth.add_auth_events.side_effect = add_auth - # Actual invocation - yield self.room_member_handler.change_membership(event) + def send_invite(domain, event): + return defer.succeed(event) - self.federation.handle_new_event.assert_called_once_with( - event, self.snapshot, + self.federation.send_invite.side_effect = send_invite + + room_handler = self.room_member_handler + event, context = yield room_handler._create_new_client_event( + builder + ) + + yield room_handler.change_membership(event, context) + + self.state_handler.compute_event_context.assert_called_once_with( + builder + ) + + self.auth.add_auth_events.assert_called_once_with( + builder, context ) - self.assertEquals( - set(["red", "green"]), - set(event.destinations) + self.federation.send_invite.assert_called_once_with( + "blue", event, ) self.datastore.persist_event.assert_called_once_with( - event + event, context=context, ) self.notifier.on_new_room_event.assert_called_once_with( event, extra_users=[self.hs.parse_userid(target_user_id)] @@ -162,57 +176,58 @@ class RoomMemberHandlerTestCase(unittest.TestCase): user_id = "@bob:red" user = self.hs.parse_userid(user_id) - event = self._create_member( - user_id=user_id, - room_id=room_id, - ) + join_signal_observer = Mock() + self.distributor.observe("user_joined_room", join_signal_observer) - self.auth.check_host_in_room.return_value = defer.succeed(True) + builder = self.hs.get_event_builder_factory().new({ + "type": EventTypes.Member, + "sender": user_id, + "state_key": user_id, + "room_id": room_id, + "content": {"membership": Membership.JOIN}, + }) - store_id = "store_id_fooo" - self.datastore.persist_event.return_value = defer.succeed(store_id) - self.datastore.get_room.return_value = defer.succeed(1) # Not None. + self.datastore.get_latest_events_in_room.return_value = ( + defer.succeed([]) + ) - prev_state = NonCallableMock() - prev_state.membership = Membership.INVITE - prev_state.sender = "@foo:red" - self.datastore.get_room_member.return_value = defer.succeed(prev_state) + def annotate(_): + ctx = Mock() + ctx.current_state = { + (EventTypes.Member, "@bob:red"): self._create_member( + user_id="@bob:red", + room_id=room_id, + membership=Membership.INVITE + ), + } + ctx.prev_state_events = [] - join_signal_observer = Mock() - self.distributor.observe("user_joined_room", join_signal_observer) + return defer.succeed(ctx) + + self.state_handler.compute_event_context.side_effect = annotate - event.state_events = { - (RoomMemberEvent.TYPE, "@alice:green"): self._create_member( - user_id="@alice:green", - room_id=room_id, - ), - (RoomMemberEvent.TYPE, user_id): event, - } + def add_auth(_, ctx): + ctx.auth_events = ctx.current_state[ + (EventTypes.Member, "@bob:red") + ] - event.old_state_events = { - (RoomMemberEvent.TYPE, "@alice:green"): self._create_member( - user_id="@alice:green", - room_id=room_id, - ), - } + return defer.succeed(True) + self.auth.add_auth_events.side_effect = add_auth - event.state_events = event.old_state_events - event.state_events[(RoomMemberEvent.TYPE, user_id)] = event + room_handler = self.room_member_handler + event, context = yield room_handler._create_new_client_event( + builder + ) # Actual invocation - yield self.room_member_handler.change_membership(event) + yield room_handler.change_membership(event, context) self.federation.handle_new_event.assert_called_once_with( - event, self.snapshot - ) - - self.assertEquals( - set(["red", "green"]), - set(event.destinations) + event, None, destinations=set() ) self.datastore.persist_event.assert_called_once_with( - event + event, context=context ) self.notifier.on_new_room_event.assert_called_once_with( event, extra_users=[user] @@ -222,14 +237,82 @@ class RoomMemberHandlerTestCase(unittest.TestCase): user=user, room_id=room_id ) - def _create_member(self, user_id, room_id): - return self.hs.get_event_factory().create_event( - etype=RoomMemberEvent.TYPE, - user_id=user_id, - state_key=user_id, - room_id=room_id, - membership=Membership.JOIN, - content={"membership": Membership.JOIN}, + def _create_member(self, user_id, room_id, membership=Membership.JOIN): + builder = self.hs.get_event_builder_factory().new({ + "type": EventTypes.Member, + "sender": user_id, + "state_key": user_id, + "room_id": room_id, + "content": {"membership": membership}, + }) + + return builder.build() + + @defer.inlineCallbacks + def test_simple_leave(self): + room_id = "!foo:red" + user_id = "@bob:red" + user = self.hs.parse_userid(user_id) + + builder = self.hs.get_event_builder_factory().new({ + "type": EventTypes.Member, + "sender": user_id, + "state_key": user_id, + "room_id": room_id, + "content": {"membership": Membership.LEAVE}, + }) + + self.datastore.get_latest_events_in_room.return_value = ( + defer.succeed([]) + ) + + def annotate(_): + ctx = Mock() + ctx.current_state = { + (EventTypes.Member, "@bob:red"): self._create_member( + user_id="@bob:red", + room_id=room_id, + membership=Membership.JOIN + ), + } + ctx.prev_state_events = [] + + return defer.succeed(ctx) + + self.state_handler.compute_event_context.side_effect = annotate + + def add_auth(_, ctx): + ctx.auth_events = ctx.current_state[ + (EventTypes.Member, "@bob:red") + ] + + return defer.succeed(True) + self.auth.add_auth_events.side_effect = add_auth + + room_handler = self.room_member_handler + event, context = yield room_handler._create_new_client_event( + builder + ) + + leave_signal_observer = Mock() + self.distributor.observe("user_left_room", leave_signal_observer) + + # Actual invocation + yield room_handler.change_membership(event, context) + + self.federation.handle_new_event.assert_called_once_with( + event, None, destinations=set(['red']) + ) + + self.datastore.persist_event.assert_called_once_with( + event, context=context + ) + self.notifier.on_new_room_event.assert_called_once_with( + event, extra_users=[user] + ) + + leave_signal_observer.assert_called_with( + user=user, room_id=room_id ) @@ -254,13 +337,9 @@ class RoomCreationTest(unittest.TestCase): notifier=NonCallableMock(spec_set=["on_new_room_event"]), handlers=NonCallableMock(spec_set=[ "room_creation_handler", - "room_member_handler", - "federation_handler", + "message_handler", ]), auth=NonCallableMock(spec_set=["check", "add_auth_events"]), - state_handler=NonCallableMock(spec_set=[ - "annotate_event_with_state", - ]), ratelimiter=NonCallableMock(spec_set=[ "send_message", ]), @@ -271,30 +350,12 @@ class RoomCreationTest(unittest.TestCase): "handle_new_event", ]) - self.datastore = hs.get_datastore() self.handlers = hs.get_handlers() - self.notifier = hs.get_notifier() - self.state_handler = hs.get_state_handler() - self.hs = hs - self.handlers.federation_handler = self.federation - - self.handlers.room_creation_handler = RoomCreationHandler(self.hs) + self.handlers.room_creation_handler = RoomCreationHandler(hs) self.room_creation_handler = self.handlers.room_creation_handler - self.handlers.room_member_handler = NonCallableMock(spec_set=[ - "change_membership" - ]) - self.room_member_handler = self.handlers.room_member_handler - - def annotate(event): - event.state_events = {} - return defer.succeed(None) - self.state_handler.annotate_event_with_state.side_effect = annotate - - def hosts(room): - return defer.succeed([]) - self.datastore.get_joined_hosts_for_room.side_effect = hosts + self.message_handler = self.handlers.message_handler self.ratelimiter = hs.get_ratelimiter() self.ratelimiter.send_message.return_value = (True, 0) @@ -311,14 +372,37 @@ class RoomCreationTest(unittest.TestCase): config=config, ) - self.assertTrue(self.room_member_handler.change_membership.called) - join_event = self.room_member_handler.change_membership.call_args[0][0] + self.assertTrue(self.message_handler.create_and_send_event.called) - self.assertEquals(RoomMemberEvent.TYPE, join_event.type) - self.assertEquals(room_id, join_event.room_id) - self.assertEquals(user_id, join_event.user_id) - self.assertEquals(user_id, join_event.state_key) + event_dicts = [ + e[0][0] + for e in self.message_handler.create_and_send_event.call_args_list + ] - self.assertTrue(self.state_handler.annotate_event_with_state.called) + self.assertTrue(len(event_dicts) > 3) + + self.assertDictContainsSubset( + { + "type": EventTypes.Create, + "sender": user_id, + "room_id": room_id, + }, + event_dicts[0] + ) - self.assertTrue(self.federation.handle_new_event.called) + self.assertEqual(user_id, event_dicts[0]["content"]["creator"]) + + self.assertDictContainsSubset( + { + "type": EventTypes.Member, + "sender": user_id, + "room_id": room_id, + "state_key": user_id, + }, + event_dicts[1] + ) + + self.assertEqual( + Membership.JOIN, + event_dicts[1]["content"]["membership"] + ) diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py index adb5148351..0d4b368a43 100644 --- a/tests/handlers/test_typing.py +++ b/tests/handlers/test_typing.py @@ -22,9 +22,12 @@ import json from ..utils import MockHttpResource, MockClock, DeferredMockCallable, MockKey +from synapse.api.errors import AuthError from synapse.server import HomeServer from synapse.handlers.typing import TypingNotificationHandler +from synapse.storage.transactions import DestinationsTable + def _expect_edu(destination, edu_type, content, origin="test"): return { @@ -63,7 +66,13 @@ class TypingNotificationsTestCase(unittest.TestCase): self.mock_config = Mock() self.mock_config.signing_key = [MockKey()] + mock_notifier = Mock(spec=["on_new_user_event"]) + self.on_new_user_event = mock_notifier.on_new_user_event + + self.auth = Mock(spec=[]) + hs = HomeServer("test", + auth=self.auth, clock=self.clock, db_pool=None, datastore=Mock(spec=[ @@ -72,8 +81,10 @@ class TypingNotificationsTestCase(unittest.TestCase): "delivered_txn", "get_received_txn_response", "set_received_txn_response", + "get_destination_retry_timings", ]), handlers=None, + notifier=mock_notifier, resource_for_client=Mock(), resource_for_federation=self.mock_federation_resource, http_client=self.mock_http_client, @@ -82,13 +93,14 @@ class TypingNotificationsTestCase(unittest.TestCase): ) hs.handlers = JustTypingNotificationHandlers(hs) - self.mock_update_client = Mock() - self.mock_update_client.return_value = defer.succeed(None) - self.handler = hs.get_handlers().typing_notification_handler - self.handler.push_update_to_clients = self.mock_update_client + + self.event_source = hs.get_event_sources().sources["typing"] self.datastore = hs.get_datastore() + self.datastore.get_destination_retry_timings.return_value = ( + defer.succeed(DestinationsTable.EntryType("", 0, 0)) + ) def get_received_txn_response(*args): return defer.succeed(None) @@ -125,7 +137,7 @@ class TypingNotificationsTestCase(unittest.TestCase): if ignore_user is not None and member == ignore_user: continue - if member.is_mine: + if hs.is_mine(member): if localusers is not None: localusers.add(member) else: @@ -134,6 +146,12 @@ class TypingNotificationsTestCase(unittest.TestCase): self.room_member_handler.fetch_room_distributions_into = ( fetch_room_distributions_into) + def check_joined_room(room_id, user_id): + if user_id not in [u.to_string() for u in self.room_members]: + raise AuthError(401, "User is not in the room") + + self.auth.check_joined_room = check_joined_room + # Some local users to test with self.u_apple = hs.parse_userid("@apple:test") self.u_banana = hs.parse_userid("@banana:test") @@ -145,6 +163,8 @@ class TypingNotificationsTestCase(unittest.TestCase): def test_started_typing_local(self): self.room_members = [self.u_apple, self.u_banana] + self.assertEquals(self.event_source.get_current_key(), 0) + yield self.handler.started_typing( target_user=self.u_apple, auth_user=self.u_apple, @@ -152,13 +172,22 @@ class TypingNotificationsTestCase(unittest.TestCase): timeout=20000, ) - self.mock_update_client.assert_has_calls([ - call(observer_user=self.u_banana, - observed_user=self.u_apple, - room_id=self.room_id, - typing=True), + self.on_new_user_event.assert_has_calls([ + call(rooms=[self.room_id]), ]) + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals( + self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [self.u_apple.to_string()], + }}, + ] + ) + @defer.inlineCallbacks def test_started_typing_remote_send(self): self.room_members = [self.u_apple, self.u_onion] @@ -192,6 +221,8 @@ class TypingNotificationsTestCase(unittest.TestCase): def test_started_typing_remote_recv(self): self.room_members = [self.u_apple, self.u_onion] + self.assertEquals(self.event_source.get_current_key(), 0) + yield self.mock_federation_resource.trigger("PUT", "/_matrix/federation/v1/send/1000000/", _make_edu_json("farm", "m.typing", @@ -203,13 +234,22 @@ class TypingNotificationsTestCase(unittest.TestCase): ) ) - self.mock_update_client.assert_has_calls([ - call(observer_user=self.u_apple, - observed_user=self.u_onion, - room_id=self.room_id, - typing=True), + self.on_new_user_event.assert_has_calls([ + call(rooms=[self.room_id]), ]) + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals( + self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [self.u_onion.to_string()], + }}, + ] + ) + @defer.inlineCallbacks def test_stopped_typing(self): self.room_members = [self.u_apple, self.u_banana, self.u_onion] @@ -232,9 +272,14 @@ class TypingNotificationsTestCase(unittest.TestCase): # Gut-wrenching from synapse.handlers.typing import RoomMember - self.handler._member_typing_until[ - RoomMember(self.room_id, self.u_apple) - ] = 1002000 + member = RoomMember(self.room_id, self.u_apple) + self.handler._member_typing_until[member] = 1002000 + self.handler._member_typing_timer[member] = ( + self.clock.call_later(1002, lambda: 0) + ) + self.handler._room_typing[self.room_id] = set((self.u_apple,)) + + self.assertEquals(self.event_source.get_current_key(), 0) yield self.handler.stopped_typing( target_user=self.u_apple, @@ -242,11 +287,68 @@ class TypingNotificationsTestCase(unittest.TestCase): room_id=self.room_id, ) - self.mock_update_client.assert_has_calls([ - call(observer_user=self.u_banana, - observed_user=self.u_apple, - room_id=self.room_id, - typing=False), + self.on_new_user_event.assert_has_calls([ + call(rooms=[self.room_id]), ]) yield put_json.await_calls() + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals( + self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [], + }}, + ] + ) + + @defer.inlineCallbacks + def test_typing_timeout(self): + self.room_members = [self.u_apple, self.u_banana] + + self.assertEquals(self.event_source.get_current_key(), 0) + + yield self.handler.started_typing( + target_user=self.u_apple, + auth_user=self.u_apple, + room_id=self.room_id, + timeout=10000, + ) + + self.on_new_user_event.assert_has_calls([ + call(rooms=[self.room_id]), + ]) + self.on_new_user_event.reset_mock() + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals( + self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [self.u_apple.to_string()], + }}, + ] + ) + + self.clock.advance_time(11) + + self.on_new_user_event.assert_has_calls([ + call(rooms=[self.room_id]), + ]) + + self.assertEquals(self.event_source.get_current_key(), 2) + self.assertEquals( + self.event_source.get_new_events_for_user(self.u_apple, 1, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [], + }}, + ] + ) diff --git a/tests/rest/test_events.py b/tests/rest/test_events.py index 4a3234c332..d3159e2cf4 100644 --- a/tests/rest/test_events.py +++ b/tests/rest/test_events.py @@ -113,9 +113,6 @@ class EventStreamPermissionsTestCase(RestTestCase): def setUp(self): self.mock_resource = MockHttpResource(prefix=PATH_PREFIX) - persistence_service = Mock(spec=["get_latest_pdus_in_context"]) - persistence_service.get_latest_pdus_in_context.return_value = [] - self.mock_config = NonCallableMock() self.mock_config.signing_key = [MockKey()] @@ -127,7 +124,6 @@ class EventStreamPermissionsTestCase(RestTestCase): db_pool=db_pool, http_client=None, replication_layer=Mock(), - persistence_service=persistence_service, clock=Mock(spec=[ "call_later", "cancel_call_later", diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py index ff7c9f0530..84fd730afc 100644 --- a/tests/rest/test_rooms.py +++ b/tests/rest/test_rooms.py @@ -503,7 +503,7 @@ class RoomsMemberListTestCase(RestTestCase): @defer.inlineCallbacks def test_get_member_list_mixed_memberships(self): - room_creator = "@some_other_guy:blue" + room_creator = "@some_other_guy:red" room_id = yield self.create_room_as(room_creator) room_path = "/rooms/%s/members" % room_id yield self.invite(room=room_id, src=room_creator, @@ -1066,7 +1066,3 @@ class RoomInitialSyncTestCase(RestTestCase): } self.assertTrue(self.user_id in presence_by_user) self.assertEquals("m.presence", presence_by_user[self.user_id]["type"]) - -# (code, response) = yield self.mock_resource.trigger("GET", path, None) -# self.assertEquals(200, code, msg=str(response)) -# self.assert_dict(json.loads(content), response) diff --git a/tests/rest/test_typing.py b/tests/rest/test_typing.py new file mode 100644 index 0000000000..c550294d59 --- /dev/null +++ b/tests/rest/test_typing.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# Copyright 2014 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. + +"""Tests REST events for /rooms paths.""" + +# twisted imports +from twisted.internet import defer + +import synapse.rest.room +from synapse.server import HomeServer + +from ..utils import MockHttpResource, SQLiteMemoryDbPool, MockKey +from .utils import RestTestCase + +from mock import Mock, NonCallableMock + + +PATH_PREFIX = "/_matrix/client/api/v1" + + +class RoomTypingTestCase(RestTestCase): + """ Tests /rooms/$room_id/typing/$user_id REST API. """ + user_id = "@sid:red" + + @defer.inlineCallbacks + def setUp(self): + self.mock_resource = MockHttpResource(prefix=PATH_PREFIX) + self.auth_user_id = self.user_id + + self.mock_config = NonCallableMock() + self.mock_config.signing_key = [MockKey()] + + db_pool = SQLiteMemoryDbPool() + yield db_pool.prepare() + + hs = HomeServer( + "red", + db_pool=db_pool, + http_client=None, + replication_layer=Mock(), + ratelimiter=NonCallableMock(spec_set=[ + "send_message", + ]), + config=self.mock_config, + ) + self.hs = hs + + self.event_source = hs.get_event_sources().sources["typing"] + + self.ratelimiter = hs.get_ratelimiter() + self.ratelimiter.send_message.return_value = (True, 0) + + hs.get_handlers().federation_handler = Mock() + + def _get_user_by_token(token=None): + return { + "user": hs.parse_userid(self.auth_user_id), + "admin": False, + "device_id": None, + } + + hs.get_auth().get_user_by_token = _get_user_by_token + + def _insert_client_ip(*args, **kwargs): + return defer.succeed(None) + hs.get_datastore().insert_client_ip = _insert_client_ip + + synapse.rest.room.register_servlets(hs, self.mock_resource) + + self.room_id = yield self.create_room_as(self.user_id) + # Need another user to make notifications actually work + yield self.join(self.room_id, user="@jim:red") + + def tearDown(self): + self.hs.get_handlers().typing_notification_handler.tearDown() + + @defer.inlineCallbacks + def test_set_typing(self): + (code, _) = yield self.mock_resource.trigger("PUT", + "/rooms/%s/typing/%s" % (self.room_id, self.user_id), + '{"typing": true, "timeout": 30000}' + ) + self.assertEquals(200, code) + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals( + self.event_source.get_new_events_for_user(self.user_id, 0, None)[0], + [ + {"type": "m.typing", + "room_id": self.room_id, + "content": { + "user_ids": [self.user_id], + }}, + ] + ) + + @defer.inlineCallbacks + def test_set_not_typing(self): + (code, _) = yield self.mock_resource.trigger("PUT", + "/rooms/%s/typing/%s" % (self.room_id, self.user_id), + '{"typing": false}' + ) + self.assertEquals(200, code) diff --git a/tests/storage/test_redaction.py b/tests/storage/test_redaction.py index adfe64a980..9806fbc69b 100644 --- a/tests/storage/test_redaction.py +++ b/tests/storage/test_redaction.py @@ -18,12 +18,11 @@ from tests import unittest from twisted.internet import defer from synapse.server import HomeServer -from synapse.api.constants import Membership -from synapse.api.events.room import ( - RoomMemberEvent, MessageEvent, RoomRedactionEvent, -) +from synapse.api.constants import EventTypes, Membership -from tests.utils import SQLiteMemoryDbPool +from tests.utils import SQLiteMemoryDbPool, MockKey + +from mock import Mock class RedactionTestCase(unittest.TestCase): @@ -33,13 +32,21 @@ class RedactionTestCase(unittest.TestCase): db_pool = SQLiteMemoryDbPool() yield db_pool.prepare() + self.mock_config = Mock() + self.mock_config.signing_key = [MockKey()] + hs = HomeServer( "test", db_pool=db_pool, + config=self.mock_config, + resource_for_federation=Mock(), + http_client=None, ) self.store = hs.get_datastore() - self.event_factory = hs.get_event_factory() + self.event_builder_factory = hs.get_event_builder_factory() + self.handlers = hs.get_handlers() + self.message_handler = self.handlers.message_handler self.u_alice = hs.parse_userid("@alice:test") self.u_bob = hs.parse_userid("@bob:test") @@ -49,35 +56,23 @@ class RedactionTestCase(unittest.TestCase): self.depth = 1 @defer.inlineCallbacks - def inject_room_member(self, room, user, membership, prev_state=None, + def inject_room_member(self, room, user, membership, replaces_state=None, extra_content={}): - self.depth += 1 - - event = self.event_factory.create_event( - etype=RoomMemberEvent.TYPE, - user_id=user.to_string(), - state_key=user.to_string(), - room_id=room.to_string(), - membership=membership, - content={"membership": membership}, - depth=self.depth, - prev_events=[], + content = {"membership": membership} + content.update(extra_content) + builder = self.event_builder_factory.new({ + "type": EventTypes.Member, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": content, + }) + + event, context = yield self.message_handler._create_new_client_event( + builder ) - event.content.update(extra_content) - - if prev_state: - event.prev_state = prev_state - - event.state_events = None - event.hashes = {} - event.prev_state = [] - event.auth_events = [] - - # Have to create a join event using the eventfactory - yield self.store.persist_event( - event - ) + yield self.store.persist_event(event, context) defer.returnValue(event) @@ -85,46 +80,38 @@ class RedactionTestCase(unittest.TestCase): def inject_message(self, room, user, body): self.depth += 1 - event = self.event_factory.create_event( - etype=MessageEvent.TYPE, - user_id=user.to_string(), - room_id=room.to_string(), - content={"body": body, "msgtype": u"message"}, - depth=self.depth, - prev_events=[], - ) + builder = self.event_builder_factory.new({ + "type": EventTypes.Message, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": {"body": body, "msgtype": u"message"}, + }) - event.state_events = None - event.hashes = {} - event.auth_events = [] - - yield self.store.persist_event( - event + event, context = yield self.message_handler._create_new_client_event( + builder ) + yield self.store.persist_event(event, context) + defer.returnValue(event) @defer.inlineCallbacks def inject_redaction(self, room, event_id, user, reason): - event = self.event_factory.create_event( - etype=RoomRedactionEvent.TYPE, - user_id=user.to_string(), - room_id=room.to_string(), - content={"reason": reason}, - depth=self.depth, - redacts=event_id, - prev_events=[], + builder = self.event_builder_factory.new({ + "type": EventTypes.Redaction, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": {"reason": reason}, + "redacts": event_id, + }) + + event, context = yield self.message_handler._create_new_client_event( + builder ) - event.state_events = None - event.hashes = {} - event.auth_events = [] - - yield self.store.persist_event( - event - ) - - defer.returnValue(event) + yield self.store.persist_event(event, context) @defer.inlineCallbacks def test_redact(self): @@ -152,14 +139,14 @@ class RedactionTestCase(unittest.TestCase): self.assertObjectHasAttributes( { - "type": MessageEvent.TYPE, + "type": EventTypes.Message, "user_id": self.u_alice.to_string(), "content": {"body": "t", "msgtype": "message"}, }, event, ) - self.assertFalse(hasattr(event, "redacted_because")) + self.assertFalse("redacted_because" in event.unsigned) # Redact event reason = "Because I said so" @@ -180,24 +167,26 @@ class RedactionTestCase(unittest.TestCase): event = results[0] + self.assertEqual(msg_event.event_id, event.event_id) + + self.assertTrue("redacted_because" in event.unsigned) + self.assertObjectHasAttributes( { - "type": MessageEvent.TYPE, + "type": EventTypes.Message, "user_id": self.u_alice.to_string(), "content": {}, }, event, ) - self.assertTrue(hasattr(event, "redacted_because")) - self.assertObjectHasAttributes( { - "type": RoomRedactionEvent.TYPE, + "type": EventTypes.Redaction, "user_id": self.u_alice.to_string(), "content": {"reason": reason}, }, - event.redacted_because, + event.unsigned["redacted_because"], ) @defer.inlineCallbacks @@ -229,7 +218,7 @@ class RedactionTestCase(unittest.TestCase): self.assertObjectHasAttributes( { - "type": RoomMemberEvent.TYPE, + "type": EventTypes.Member, "user_id": self.u_bob.to_string(), "content": {"membership": Membership.JOIN, "blue": "red"}, }, @@ -257,22 +246,22 @@ class RedactionTestCase(unittest.TestCase): event = results[0] + self.assertTrue("redacted_because" in event.unsigned) + self.assertObjectHasAttributes( { - "type": RoomMemberEvent.TYPE, + "type": EventTypes.Member, "user_id": self.u_bob.to_string(), "content": {"membership": Membership.JOIN}, }, event, ) - self.assertTrue(hasattr(event, "redacted_because")) - self.assertObjectHasAttributes( { - "type": RoomRedactionEvent.TYPE, + "type": EventTypes.Redaction, "user_id": self.u_alice.to_string(), "content": {"reason": reason}, }, - event.redacted_because, + event.unsigned["redacted_because"], ) diff --git a/tests/storage/test_room.py b/tests/storage/test_room.py index 4ff02c306b..11761fe29a 100644 --- a/tests/storage/test_room.py +++ b/tests/storage/test_room.py @@ -18,9 +18,7 @@ from tests import unittest from twisted.internet import defer from synapse.server import HomeServer -from synapse.api.events.room import ( - RoomNameEvent, RoomTopicEvent -) +from synapse.api.constants import EventTypes from tests.utils import SQLiteMemoryDbPool @@ -131,7 +129,7 @@ class RoomEventsStoreTestCase(unittest.TestCase): name = u"A-Room-Name" yield self.inject_room_event( - etype=RoomNameEvent.TYPE, + etype=EventTypes.Name, name=name, content={"name": name}, depth=1, @@ -154,7 +152,7 @@ class RoomEventsStoreTestCase(unittest.TestCase): topic = u"A place for things" yield self.inject_room_event( - etype=RoomTopicEvent.TYPE, + etype=EventTypes.Topic, topic=topic, content={"topic": topic}, depth=1, diff --git a/tests/storage/test_roommember.py b/tests/storage/test_roommember.py index 8614e5ca9d..a23a8189df 100644 --- a/tests/storage/test_roommember.py +++ b/tests/storage/test_roommember.py @@ -18,10 +18,11 @@ from tests import unittest from twisted.internet import defer from synapse.server import HomeServer -from synapse.api.constants import Membership -from synapse.api.events.room import RoomMemberEvent +from synapse.api.constants import EventTypes, Membership -from tests.utils import SQLiteMemoryDbPool +from tests.utils import SQLiteMemoryDbPool, MockKey + +from mock import Mock class RoomMemberStoreTestCase(unittest.TestCase): @@ -31,14 +32,22 @@ class RoomMemberStoreTestCase(unittest.TestCase): db_pool = SQLiteMemoryDbPool() yield db_pool.prepare() - hs = HomeServer("test", + self.mock_config = Mock() + self.mock_config.signing_key = [MockKey()] + + hs = HomeServer( + "test", db_pool=db_pool, + config=self.mock_config, + resource_for_federation=Mock(), + http_client=None, ) - # We can't test the RoomMemberStore on its own without the other event # storage logic self.store = hs.get_datastore() - self.event_factory = hs.get_event_factory() + self.event_builder_factory = hs.get_event_builder_factory() + self.handlers = hs.get_handlers() + self.message_handler = self.handlers.message_handler self.u_alice = hs.parse_userid("@alice:test") self.u_bob = hs.parse_userid("@bob:test") @@ -49,27 +58,22 @@ class RoomMemberStoreTestCase(unittest.TestCase): self.room = hs.parse_roomid("!abc123:test") @defer.inlineCallbacks - def inject_room_member(self, room, user, membership): - # Have to create a join event using the eventfactory - event = self.event_factory.create_event( - etype=RoomMemberEvent.TYPE, - user_id=user.to_string(), - state_key=user.to_string(), - room_id=room.to_string(), - membership=membership, - content={"membership": membership}, - depth=1, - prev_events=[], + def inject_room_member(self, room, user, membership, replaces_state=None): + builder = self.event_builder_factory.new({ + "type": EventTypes.Member, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": {"membership": membership}, + }) + + event, context = yield self.message_handler._create_new_client_event( + builder ) - event.state_events = None - event.hashes = {} - event.prev_state = {} - event.auth_events = {} + yield self.store.persist_event(event, context) - yield self.store.persist_event( - event - ) + defer.returnValue(event) @defer.inlineCallbacks def test_one_member(self): diff --git a/tests/storage/test_stream.py b/tests/storage/test_stream.py index 5038546aee..9247fc579e 100644 --- a/tests/storage/test_stream.py +++ b/tests/storage/test_stream.py @@ -18,10 +18,11 @@ from tests import unittest from twisted.internet import defer from synapse.server import HomeServer -from synapse.api.constants import Membership -from synapse.api.events.room import RoomMemberEvent, MessageEvent +from synapse.api.constants import EventTypes, Membership -from tests.utils import SQLiteMemoryDbPool +from tests.utils import SQLiteMemoryDbPool, MockKey + +from mock import Mock class StreamStoreTestCase(unittest.TestCase): @@ -31,13 +32,21 @@ class StreamStoreTestCase(unittest.TestCase): db_pool = SQLiteMemoryDbPool() yield db_pool.prepare() + self.mock_config = Mock() + self.mock_config.signing_key = [MockKey()] + hs = HomeServer( "test", db_pool=db_pool, + config=self.mock_config, + resource_for_federation=Mock(), + http_client=None, ) self.store = hs.get_datastore() - self.event_factory = hs.get_event_factory() + self.event_builder_factory = hs.get_event_builder_factory() + self.handlers = hs.get_handlers() + self.message_handler = self.handlers.message_handler self.u_alice = hs.parse_userid("@alice:test") self.u_bob = hs.parse_userid("@bob:test") @@ -48,58 +57,43 @@ class StreamStoreTestCase(unittest.TestCase): self.depth = 1 @defer.inlineCallbacks - def inject_room_member(self, room, user, membership, replaces_state=None): + def inject_room_member(self, room, user, membership): self.depth += 1 - event = self.event_factory.create_event( - etype=RoomMemberEvent.TYPE, - user_id=user.to_string(), - state_key=user.to_string(), - room_id=room.to_string(), - membership=membership, - content={"membership": membership}, - depth=self.depth, - prev_events=[], - ) - - event.state_events = None - event.hashes = {} - event.prev_state = [] - event.auth_events = [] - - if replaces_state: - event.prev_state = [(replaces_state, "hash")] - event.replaces_state = replaces_state + builder = self.event_builder_factory.new({ + "type": EventTypes.Member, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": {"membership": membership}, + }) - # Have to create a join event using the eventfactory - yield self.store.persist_event( - event + event, context = yield self.message_handler._create_new_client_event( + builder ) + yield self.store.persist_event(event, context) + defer.returnValue(event) @defer.inlineCallbacks def inject_message(self, room, user, body): self.depth += 1 - event = self.event_factory.create_event( - etype=MessageEvent.TYPE, - user_id=user.to_string(), - room_id=room.to_string(), - content={"body": body, "msgtype": u"message"}, - depth=self.depth, - prev_events=[], - ) - - event.state_events = None - event.hashes = {} - event.auth_events = [] + builder = self.event_builder_factory.new({ + "type": EventTypes.Message, + "sender": user.to_string(), + "state_key": user.to_string(), + "room_id": room.to_string(), + "content": {"body": body, "msgtype": u"message"}, + }) - # Have to create a join event using the eventfactory - yield self.store.persist_event( - event + event, context = yield self.message_handler._create_new_client_event( + builder ) + yield self.store.persist_event(event, context) + @defer.inlineCallbacks def test_event_stream_get_other(self): # Both bob and alice joins the room @@ -130,7 +124,7 @@ class StreamStoreTestCase(unittest.TestCase): self.assertObjectHasAttributes( { - "type": MessageEvent.TYPE, + "type": EventTypes.Message, "user_id": self.u_alice.to_string(), "content": {"body": "test", "msgtype": "message"}, }, @@ -167,7 +161,7 @@ class StreamStoreTestCase(unittest.TestCase): self.assertObjectHasAttributes( { - "type": MessageEvent.TYPE, + "type": EventTypes.Message, "user_id": self.u_alice.to_string(), "content": {"body": "test", "msgtype": "message"}, }, @@ -220,7 +214,6 @@ class StreamStoreTestCase(unittest.TestCase): event2 = yield self.inject_room_member( self.room1, self.u_alice, Membership.JOIN, - replaces_state=event1.event_id, ) end = yield self.store.get_room_events_max_id() @@ -238,6 +231,6 @@ class StreamStoreTestCase(unittest.TestCase): event = results[0] self.assertTrue( - hasattr(event, "prev_content"), + "prev_content" in event.unsigned, msg="No prev_content key" ) diff --git a/tests/test_distributor.py b/tests/test_distributor.py index 39c5b8dff2..6a0095d850 100644 --- a/tests/test_distributor.py +++ b/tests/test_distributor.py @@ -13,12 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from tests import unittest +from . import unittest from twisted.internet import defer from mock import Mock, patch from synapse.util.distributor import Distributor +from synapse.util.async import run_on_reactor class DistributorTestCase(unittest.TestCase): @@ -26,6 +27,7 @@ class DistributorTestCase(unittest.TestCase): def setUp(self): self.dist = Distributor() + @defer.inlineCallbacks def test_signal_dispatch(self): self.dist.declare("alert") @@ -33,10 +35,11 @@ class DistributorTestCase(unittest.TestCase): self.dist.observe("alert", observer) d = self.dist.fire("alert", 1, 2, 3) - + yield d self.assertTrue(d.called) observer.assert_called_with(1, 2, 3) + @defer.inlineCallbacks def test_signal_dispatch_deferred(self): self.dist.declare("whine") @@ -50,8 +53,10 @@ class DistributorTestCase(unittest.TestCase): self.assertFalse(d_outer.called) d_inner.callback(None) + yield d_outer self.assertTrue(d_outer.called) + @defer.inlineCallbacks def test_signal_catch(self): self.dist.declare("alarm") @@ -65,6 +70,7 @@ class DistributorTestCase(unittest.TestCase): spec=["warning"] ) as mock_logger: d = self.dist.fire("alarm", "Go") + yield d self.assertTrue(d.called) observers[0].assert_called_once("Go") @@ -81,23 +87,28 @@ class DistributorTestCase(unittest.TestCase): self.dist.declare("whail") - observer = Mock() - observer.return_value = defer.fail( - Exception("Oopsie") - ) + class MyException(Exception): + pass + + @defer.inlineCallbacks + def observer(): + yield run_on_reactor() + raise MyException("Oopsie") self.dist.observe("whail", observer) d = self.dist.fire("whail") - yield self.assertFailure(d, Exception) + yield self.assertFailure(d, MyException) + self.dist.suppress_failures = True + @defer.inlineCallbacks def test_signal_prereg(self): observer = Mock() self.dist.observe("flare", observer) self.dist.declare("flare") - self.dist.fire("flare", 4, 5) + yield self.dist.fire("flare", 4, 5) observer.assert_called_with(4, 5) diff --git a/tests/test_state.py b/tests/test_state.py index 7979b54a35..98ad9e54cd 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -26,6 +26,7 @@ class StateTestCase(unittest.TestCase): self.store = Mock( spec_set=[ "get_state_groups", + "add_event_hashes", ] ) hs = Mock(spec=["get_datastore"]) @@ -44,17 +45,20 @@ class StateTestCase(unittest.TestCase): self.create_event(type="test2", state_key=""), ] - yield self.state.annotate_event_with_state(event, old_state=old_state) + context = yield self.state.compute_event_context( + event, old_state=old_state + ) - for k, v in event.old_state_events.items(): + for k, v in context.current_state.items(): type, state_key = k self.assertEqual(type, v.type) self.assertEqual(state_key, v.state_key) - self.assertEqual(set(old_state), set(event.old_state_events.values())) - self.assertDictEqual(event.old_state_events, event.state_events) + self.assertEqual( + set(old_state), set(context.current_state.values()) + ) - self.assertIsNone(event.state_group) + self.assertIsNone(context.state_group) @defer.inlineCallbacks def test_annotate_with_old_state(self): @@ -66,21 +70,21 @@ class StateTestCase(unittest.TestCase): self.create_event(type="test2", state_key=""), ] - yield self.state.annotate_event_with_state(event, old_state=old_state) + context = yield self.state.compute_event_context( + event, old_state=old_state + ) - for k, v in event.old_state_events.items(): + for k, v in context.current_state.items(): type, state_key = k self.assertEqual(type, v.type) self.assertEqual(state_key, v.state_key) self.assertEqual( - set(old_state + [event]), - set(event.old_state_events.values()) + set(old_state), + set(context.current_state.values()) ) - self.assertDictEqual(event.old_state_events, event.state_events) - - self.assertIsNone(event.state_group) + self.assertIsNone(context.state_group) @defer.inlineCallbacks def test_trivial_annotate_message(self): @@ -99,30 +103,19 @@ class StateTestCase(unittest.TestCase): group_name: old_state, } - yield self.state.annotate_event_with_state(event) + context = yield self.state.compute_event_context(event) - for k, v in event.old_state_events.items(): + for k, v in context.current_state.items(): type, state_key = k self.assertEqual(type, v.type) self.assertEqual(state_key, v.state_key) self.assertEqual( set([e.event_id for e in old_state]), - set([e.event_id for e in event.old_state_events.values()]) + set([e.event_id for e in context.current_state.values()]) ) - self.assertDictEqual( - { - k: v.event_id - for k, v in event.old_state_events.items() - }, - { - k: v.event_id - for k, v in event.state_events.items() - } - ) - - self.assertEqual(group_name, event.state_group) + self.assertEqual(group_name, context.state_group) @defer.inlineCallbacks def test_trivial_annotate_state(self): @@ -141,38 +134,19 @@ class StateTestCase(unittest.TestCase): group_name: old_state, } - yield self.state.annotate_event_with_state(event) + context = yield self.state.compute_event_context(event) - for k, v in event.old_state_events.items(): + for k, v in context.current_state.items(): type, state_key = k self.assertEqual(type, v.type) self.assertEqual(state_key, v.state_key) self.assertEqual( set([e.event_id for e in old_state]), - set([e.event_id for e in event.old_state_events.values()]) - ) - - self.assertEqual( - set([e.event_id for e in old_state] + [event.event_id]), - set([e.event_id for e in event.state_events.values()]) - ) - - new_state = { - k: v.event_id - for k, v in event.state_events.items() - } - old_state = { - k: v.event_id - for k, v in event.old_state_events.items() - } - old_state[(event.type, event.state_key)] = event.event_id - self.assertDictEqual( - old_state, - new_state + set([e.event_id for e in context.current_state.values()]) ) - self.assertIsNone(event.state_group) + self.assertIsNone(context.state_group) @defer.inlineCallbacks def test_resolve_message_conflict(self): @@ -199,16 +173,11 @@ class StateTestCase(unittest.TestCase): group_name_2: old_state_2, } - yield self.state.annotate_event_with_state(event) + context = yield self.state.compute_event_context(event) - self.assertEqual(len(event.old_state_events), 5) - - self.assertEqual( - set([e.event_id for e in event.state_events.values()]), - set([e.event_id for e in event.old_state_events.values()]) - ) + self.assertEqual(len(context.current_state), 5) - self.assertIsNone(event.state_group) + self.assertIsNone(context.state_group) @defer.inlineCallbacks def test_resolve_state_conflict(self): @@ -235,19 +204,11 @@ class StateTestCase(unittest.TestCase): group_name_2: old_state_2, } - yield self.state.annotate_event_with_state(event) + context = yield self.state.compute_event_context(event) - self.assertEqual(len(event.old_state_events), 5) + self.assertEqual(len(context.current_state), 5) - expected_new = event.old_state_events - expected_new[(event.type, event.state_key)] = event - - self.assertEqual( - set([e.event_id for e in expected_new.values()]), - set([e.event_id for e in event.state_events.values()]), - ) - - self.assertIsNone(event.state_group) + self.assertIsNone(context.state_group) def create_event(self, name=None, type=None, state_key=None): self.event_id += 1 @@ -266,6 +227,9 @@ class StateTestCase(unittest.TestCase): event.state_key = state_key event.event_id = event_id + event.is_state = lambda: (state_key is not None) + event.unsigned = {} + event.user_id = "@user_id:example.com" event.room_id = "!room_id:example.com" diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py new file mode 100644 index 0000000000..b42787dd25 --- /dev/null +++ b/tests/test_test_utils.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Copyright 2014 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 tests import unittest + +from tests.utils import MockClock + +class MockClockTestCase(unittest.TestCase): + + def setUp(self): + self.clock = MockClock() + + def test_advance_time(self): + start_time = self.clock.time() + + self.clock.advance_time(20) + + self.assertEquals(20, self.clock.time() - start_time) + + def test_later(self): + invoked = [0, 0] + + def _cb0(): + invoked[0] = 1 + self.clock.call_later(10, _cb0) + + def _cb1(): + invoked[1] = 1 + self.clock.call_later(20, _cb1) + + self.assertFalse(invoked[0]) + + self.clock.advance_time(15) + + self.assertTrue(invoked[0]) + self.assertFalse(invoked[1]) + + self.clock.advance_time(5) + + self.assertTrue(invoked[1]) + + def test_cancel_later(self): + invoked = [0, 0] + + def _cb0(): + invoked[0] = 1 + t0 = self.clock.call_later(10, _cb0) + + def _cb1(): + invoked[1] = 1 + t1 = self.clock.call_later(20, _cb1) + + self.clock.cancel_call_later(t0) + + self.clock.advance_time(30) + + self.assertFalse(invoked[0]) + self.assertTrue(invoked[1]) diff --git a/tests/test_types.py b/tests/test_types.py index 276ecc91fd..bfb9e6f548 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -23,21 +23,21 @@ mock_homeserver = BaseHomeServer(hostname="my.domain") class UserIDTestCase(unittest.TestCase): def test_parse(self): - user = UserID.from_string("@1234abcd:my.domain", hs=mock_homeserver) + user = UserID.from_string("@1234abcd:my.domain") self.assertEquals("1234abcd", user.localpart) self.assertEquals("my.domain", user.domain) - self.assertEquals(True, user.is_mine) + self.assertEquals(True, mock_homeserver.is_mine(user)) def test_build(self): - user = UserID("5678efgh", "my.domain", True) + user = UserID("5678efgh", "my.domain") self.assertEquals(user.to_string(), "@5678efgh:my.domain") def test_compare(self): - userA = UserID.from_string("@userA:my.domain", hs=mock_homeserver) - userAagain = UserID.from_string("@userA:my.domain", hs=mock_homeserver) - userB = UserID.from_string("@userB:my.domain", hs=mock_homeserver) + userA = UserID.from_string("@userA:my.domain") + userAagain = UserID.from_string("@userA:my.domain") + userB = UserID.from_string("@userB:my.domain") self.assertTrue(userA == userAagain) self.assertTrue(userA != userB) @@ -52,14 +52,14 @@ class UserIDTestCase(unittest.TestCase): class RoomAliasTestCase(unittest.TestCase): def test_parse(self): - room = RoomAlias.from_string("#channel:my.domain", hs=mock_homeserver) + room = RoomAlias.from_string("#channel:my.domain") self.assertEquals("channel", room.localpart) self.assertEquals("my.domain", room.domain) - self.assertEquals(True, room.is_mine) + self.assertEquals(True, mock_homeserver.is_mine(room)) def test_build(self): - room = RoomAlias("channel", "my.domain", True) + room = RoomAlias("channel", "my.domain") self.assertEquals(room.to_string(), "#channel:my.domain") diff --git a/tests/utils.py b/tests/utils.py index d8be73dba8..731e03f517 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,19 +15,17 @@ from synapse.http.server import HttpServer from synapse.api.errors import cs_error, CodeMessageException, StoreError -from synapse.api.constants import Membership +from synapse.api.constants import EventTypes from synapse.storage import prepare_database -from synapse.api.events.room import ( - RoomMemberEvent, MessageEvent -) +from synapse.util.logcontext import LoggingContext from twisted.internet import defer, reactor from twisted.enterprise.adbapi import ConnectionPool from collections import namedtuple from mock import patch, Mock -import json +import urllib import urlparse from inspect import getcallargs @@ -101,9 +99,14 @@ class MockHttpResource(HttpServer): matcher = pattern.match(path) if matcher: try: + args = [ + urllib.unquote(u).decode("UTF-8") + for u in matcher.groups() + ] + (code, response) = yield func( mock_request, - *matcher.groups() + *args ) defer.returnValue((code, response)) except CodeMessageException as e: @@ -134,16 +137,43 @@ class MockKey(object): class MockClock(object): now = 1000 + def __init__(self): + # list of tuples of (absolute_time, callback) in no particular order + self.timers = [] + def time(self): return self.now def time_msec(self): return self.time() * 1000 + def call_later(self, delay, callback): + current_context = LoggingContext.current_context() + + def wrapped_callback(): + LoggingContext.thread_local.current_context = current_context + callback() + + t = (self.now + delay, wrapped_callback) + self.timers.append(t) + return t + + def cancel_call_later(self, timer): + self.timers = [t for t in self.timers if t != timer] + # For unit testing def advance_time(self, secs): self.now += secs + timers = self.timers + self.timers = [] + + for time, callback in timers: + if self.now >= time: + callback() + else: + self.timers.append((time, callback)) + class SQLiteMemoryDbPool(ConnectionPool, object): def __init__(self): @@ -242,7 +272,7 @@ class MemoryDataStore(object): return defer.succeed([]) def persist_event(self, event): - if event.type == RoomMemberEvent.TYPE: + if event.type == EventTypes.Member: room_id = event.room_id user = event.state_key membership = event.membership |