Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor

Conflicts:
	tests/handlers/test_room.py
This commit is contained in:
Erik Johnston 2014-12-15 14:45:59 +00:00
commit 57e0e619f3
13 changed files with 449 additions and 56 deletions

View File

@ -1,3 +1,9 @@
Changes in synapse 0.5.4a (2014-12-13)
======================================
* Fix bug while generating the error message when a file path specified in
the config doesn't exist.
Changes in synapse 0.5.4 (2014-12-03) Changes in synapse 0.5.4 (2014-12-03)
===================================== =====================================

View File

@ -1 +1 @@
0.5.4 0.5.4a

View File

@ -16,4 +16,4 @@
""" This is a reference implementation of a synapse home server. """ This is a reference implementation of a synapse home server.
""" """
__version__ = "0.5.4" __version__ = "0.5.4a"

View File

@ -44,9 +44,9 @@ class Config(object):
) )
if not os.path.exists(file_path): if not os.path.exists(file_path):
raise ConfigError( raise ConfigError(
"File % config for %s doesn't exist." "File %s config for %s doesn't exist."
" Try running again with --generate-config" " Try running again with --generate-config"
% (config_name,) % (file_path, config_name,)
) )
return cls.abspath(file_path) return cls.abspath(file_path)

View File

@ -244,6 +244,7 @@ class RoomMemberHandler(BaseHandler):
self.distributor = hs.get_distributor() self.distributor = hs.get_distributor()
self.distributor.declare("user_joined_room") self.distributor.declare("user_joined_room")
self.distributor.declare("user_left_room")
@defer.inlineCallbacks @defer.inlineCallbacks
def get_room_members(self, room_id, membership=Membership.JOIN): def get_room_members(self, room_id, membership=Membership.JOIN):
@ -370,6 +371,12 @@ class RoomMemberHandler(BaseHandler):
do_auth=do_auth, do_auth=do_auth,
) )
if prev_state and prev_state.membership == Membership.JOIN:
user = self.hs.parse_userid(event.user_id)
self.distributor.fire(
"user_left_room", user=user, room_id=event.room_id
)
defer.returnValue({"room_id": room_id}) defer.returnValue({"room_id": room_id})
@defer.inlineCallbacks @defer.inlineCallbacks

View File

@ -43,7 +43,23 @@ class TypingNotificationHandler(BaseHandler):
self.federation.register_edu_handler("m.typing", self._recv_edu) self.federation.register_edu_handler("m.typing", self._recv_edu)
self._member_typing_until = {} hs.get_distributor().observe("user_left_room", self.user_left_room)
self._member_typing_until = {} # clock time we expect to stop
self._member_typing_timer = {} # deferreds to manage theabove
# map room IDs to serial numbers
self._room_serials = {}
self._latest_room_serial = 0
# map room IDs to sets of users currently typing
self._room_typing = {}
def tearDown(self):
"""Cancels all the pending timers.
Normally this shouldn't be needed, but it's required from unit tests
to avoid a "Reactor was unclean" warning."""
for t in self._member_typing_timer.values():
self.clock.cancel_call_later(t)
@defer.inlineCallbacks @defer.inlineCallbacks
def started_typing(self, target_user, auth_user, room_id, timeout): def started_typing(self, target_user, auth_user, room_id, timeout):
@ -53,12 +69,24 @@ class TypingNotificationHandler(BaseHandler):
if target_user != auth_user: if target_user != auth_user:
raise AuthError(400, "Cannot set another user's typing state") raise AuthError(400, "Cannot set another user's typing state")
yield self.auth.check_joined_room(room_id, target_user.to_string())
logger.debug(
"%s has started typing in %s", target_user.to_string(), room_id
)
until = self.clock.time_msec() + timeout until = self.clock.time_msec() + timeout
member = RoomMember(room_id=room_id, user=target_user) member = RoomMember(room_id=room_id, user=target_user)
was_present = member in self._member_typing_until was_present = member in self._member_typing_until
if member in self._member_typing_timer:
self.clock.cancel_call_later(self._member_typing_timer[member])
self._member_typing_until[member] = until self._member_typing_until[member] = until
self._member_typing_timer[member] = self.clock.call_later(
timeout / 1000, lambda: self._stopped_typing(member)
)
if was_present: if was_present:
# No point sending another notification # No point sending another notification
@ -78,18 +106,39 @@ class TypingNotificationHandler(BaseHandler):
if target_user != auth_user: if target_user != auth_user:
raise AuthError(400, "Cannot set another user's typing state") raise AuthError(400, "Cannot set another user's typing state")
yield self.auth.check_joined_room(room_id, target_user.to_string())
logger.debug(
"%s has stopped typing in %s", target_user.to_string(), room_id
)
member = RoomMember(room_id=room_id, user=target_user) member = RoomMember(room_id=room_id, user=target_user)
yield self._stopped_typing(member)
@defer.inlineCallbacks
def user_left_room(self, user, room_id):
if user.is_mine:
member = RoomMember(room_id=room_id, user=user)
yield self._stopped_typing(member)
@defer.inlineCallbacks
def _stopped_typing(self, member):
if member not in self._member_typing_until: if member not in self._member_typing_until:
# No point # No point
defer.returnValue(None) defer.returnValue(None)
yield self._push_update( yield self._push_update(
room_id=room_id, room_id=member.room_id,
user=target_user, user=member.user,
typing=False, typing=False,
) )
del self._member_typing_until[member]
self.clock.cancel_call_later(self._member_typing_timer[member])
del self._member_typing_timer[member]
@defer.inlineCallbacks @defer.inlineCallbacks
def _push_update(self, room_id, user, typing): def _push_update(self, room_id, user, typing):
localusers = set() localusers = set()
@ -97,16 +146,14 @@ class TypingNotificationHandler(BaseHandler):
rm_handler = self.homeserver.get_handlers().room_member_handler rm_handler = self.homeserver.get_handlers().room_member_handler
yield rm_handler.fetch_room_distributions_into( yield rm_handler.fetch_room_distributions_into(
room_id, localusers=localusers, remotedomains=remotedomains, room_id, localusers=localusers, remotedomains=remotedomains
ignore_user=user
) )
for u in localusers: if localusers:
self.push_update_to_clients( self._push_update_local(
room_id=room_id, room_id=room_id,
observer_user=u, user=user,
observed_user=user, typing=typing
typing=typing,
) )
deferreds = [] deferreds = []
@ -135,29 +182,67 @@ class TypingNotificationHandler(BaseHandler):
room_id, localusers=localusers room_id, localusers=localusers
) )
for u in localusers: if localusers:
self.push_update_to_clients( self._push_update_local(
room_id=room_id, room_id=room_id,
observer_user=u, user=user,
observed_user=user,
typing=content["typing"] typing=content["typing"]
) )
def push_update_to_clients(self, room_id, observer_user, observed_user, def _push_update_local(self, room_id, user, typing):
typing): if room_id not in self._room_serials:
# TODO(paul) steal this from presence.py self._room_serials[room_id] = 0
pass self._room_typing[room_id] = set()
room_set = self._room_typing[room_id]
if typing:
room_set.add(user)
elif user in room_set:
room_set.remove(user)
self._latest_room_serial += 1
self._room_serials[room_id] = self._latest_room_serial
self.notifier.on_new_user_event(rooms=[room_id])
class TypingNotificationEventSource(object): class TypingNotificationEventSource(object):
def __init__(self, hs): def __init__(self, hs):
self.hs = hs self.hs = hs
self._handler = None
def handler(self):
# Avoid cyclic dependency in handler setup
if not self._handler:
self._handler = self.hs.get_handlers().typing_notification_handler
return self._handler
def _make_event_for(self, room_id):
typing = self.handler()._room_typing[room_id]
return {
"type": "m.typing",
"room_id": room_id,
"content": {
"user_ids": [u.to_string() for u in typing],
},
}
def get_new_events_for_user(self, user, from_key, limit): def get_new_events_for_user(self, user, from_key, limit):
return ([], from_key) from_key = int(from_key)
handler = self.handler()
events = []
for room_id in handler._room_serials:
if handler._room_serials[room_id] <= from_key:
continue
# TODO: check if user is in room
events.append(self._make_event_for(room_id))
return (events, handler._latest_room_serial)
def get_current_key(self): def get_current_key(self):
return 0 return self.handler()._latest_room_serial
def get_pagination_rows(self, user, pagination_config, key): def get_pagination_rows(self, user, pagination_config, key):
return ([], pagination_config.from_key) return ([], pagination_config.from_key)

View File

@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
class MediaRepositoryResource(Resource): class MediaRepositoryResource(Resource):
"""Profiles file uploading and downloading. """File uploading and downloading.
Uploads are POSTed to a resource which returns a token which is used to GET Uploads are POSTed to a resource which returns a token which is used to GET
the download:: the download::
@ -39,9 +39,9 @@ class MediaRepositoryResource(Resource):
<= HTTP/1.1 200 OK <= HTTP/1.1 200 OK
Content-Type: application/json Content-Type: application/json
{ "token": <media-id> } { "content-uri": "mxc://<server-name>/<media-id>" }
=> GET /_matrix/media/v1/download/<media-id> HTTP/1.1 => GET /_matrix/media/v1/download/<server-name>/<media-id> HTTP/1.1
<= HTTP/1.1 200 OK <= HTTP/1.1 200 OK
Content-Type: <media-type> Content-Type: <media-type>
@ -52,8 +52,8 @@ class MediaRepositoryResource(Resource):
Clients can get thumbnails by supplying a desired width and height and Clients can get thumbnails by supplying a desired width and height and
thumbnailing method:: thumbnailing method::
=> GET /_matrix/media/v1 => GET /_matrix/media/v1/thumbnail/<server_name>
/thumbnail/<media-id>?width=<w>&height=<h>&method=<m> HTTP/1.1 /<media-id>?width=<w>&height=<h>&method=<m> HTTP/1.1
<= HTTP/1.1 200 OK <= HTTP/1.1 200 OK
Content-Type: image/jpeg or image/png Content-Type: image/jpeg or image/png

View File

@ -95,8 +95,10 @@ class UploadResource(BaseMediaResource):
yield self._generate_local_thumbnails(media_id, media_info) yield self._generate_local_thumbnails(media_id, media_info)
content_uri = "mxc://%s/%s" % (self.server_name, media_id)
respond_with_json( respond_with_json(
request, 200, {"content_token": media_id}, send_cors=True request, 200, {"content_uri": content_uri}, send_cors=True
) )
except CodeMessageException as e: except CodeMessageException as e:
logger.exception(e) logger.exception(e)

View File

@ -466,6 +466,37 @@ class RoomRedactEventRestServlet(RestServlet):
defer.returnValue(response) defer.returnValue(response)
class RoomTypingRestServlet(RestServlet):
PATTERN = client_path_pattern("/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$")
@defer.inlineCallbacks
def on_PUT(self, request, room_id, user_id):
auth_user = yield self.auth.get_user_by_req(request)
room_id = urllib.unquote(room_id)
target_user = self.hs.parse_userid(urllib.unquote(user_id))
content = _parse_json(request)
typing_handler = self.handlers.typing_notification_handler
if content["typing"]:
yield typing_handler.started_typing(
target_user=target_user,
auth_user=auth_user,
room_id=room_id,
timeout=content.get("timeout", 30000),
)
else:
yield typing_handler.stopped_typing(
target_user=target_user,
auth_user=auth_user,
room_id=room_id,
)
defer.returnValue((200, {}))
def _parse_json(request): def _parse_json(request):
try: try:
content = json.loads(request.content.read()) content = json.loads(request.content.read())
@ -521,3 +552,4 @@ def register_servlets(hs, http_server):
RoomStateRestServlet(hs).register(http_server) RoomStateRestServlet(hs).register(http_server)
RoomInitialSyncRestServlet(hs).register(http_server) RoomInitialSyncRestServlet(hs).register(http_server)
RoomRedactEventRestServlet(hs).register(http_server) RoomRedactEventRestServlet(hs).register(http_server)
RoomTypingRestServlet(hs).register(http_server)

View File

@ -247,6 +247,60 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
return builder.build() 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": RoomMemberEvent.TYPE,
"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):
ctx.current_state = {
(RoomMemberEvent.TYPE, "@bob:red"): self._create_member(
user_id="@bob:red",
room_id=room_id,
membership=Membership.INVITE
),
}
return defer.succeed(True)
self.state_handler.annotate_context_with_state.side_effect = annotate
def add_auth(_, ctx):
ctx.auth_events = ctx.current_state[
(RoomMemberEvent.TYPE, "@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)
leave_signal_observer.assert_called_with(
user=user, room_id=room_id
)
class RoomCreationTest(unittest.TestCase): class RoomCreationTest(unittest.TestCase):

View File

@ -22,6 +22,7 @@ import json
from ..utils import MockHttpResource, MockClock, DeferredMockCallable, MockKey from ..utils import MockHttpResource, MockClock, DeferredMockCallable, MockKey
from synapse.api.errors import AuthError
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.handlers.typing import TypingNotificationHandler from synapse.handlers.typing import TypingNotificationHandler
@ -65,7 +66,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
self.mock_config = Mock() self.mock_config = Mock()
self.mock_config.signing_key = [MockKey()] 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", hs = HomeServer("test",
auth=self.auth,
clock=self.clock, clock=self.clock,
db_pool=None, db_pool=None,
datastore=Mock(spec=[ datastore=Mock(spec=[
@ -77,6 +84,7 @@ class TypingNotificationsTestCase(unittest.TestCase):
"get_destination_retry_timings", "get_destination_retry_timings",
]), ]),
handlers=None, handlers=None,
notifier=mock_notifier,
resource_for_client=Mock(), resource_for_client=Mock(),
resource_for_federation=self.mock_federation_resource, resource_for_federation=self.mock_federation_resource,
http_client=self.mock_http_client, http_client=self.mock_http_client,
@ -85,11 +93,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
) )
hs.handlers = JustTypingNotificationHandlers(hs) 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 = 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 = hs.get_datastore()
self.datastore.get_destination_retry_timings.return_value = ( self.datastore.get_destination_retry_timings.return_value = (
@ -140,6 +146,12 @@ class TypingNotificationsTestCase(unittest.TestCase):
self.room_member_handler.fetch_room_distributions_into = ( self.room_member_handler.fetch_room_distributions_into = (
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 # Some local users to test with
self.u_apple = hs.parse_userid("@apple:test") self.u_apple = hs.parse_userid("@apple:test")
self.u_banana = hs.parse_userid("@banana:test") self.u_banana = hs.parse_userid("@banana:test")
@ -151,6 +163,8 @@ class TypingNotificationsTestCase(unittest.TestCase):
def test_started_typing_local(self): def test_started_typing_local(self):
self.room_members = [self.u_apple, self.u_banana] self.room_members = [self.u_apple, self.u_banana]
self.assertEquals(self.event_source.get_current_key(), 0)
yield self.handler.started_typing( yield self.handler.started_typing(
target_user=self.u_apple, target_user=self.u_apple,
auth_user=self.u_apple, auth_user=self.u_apple,
@ -158,13 +172,22 @@ class TypingNotificationsTestCase(unittest.TestCase):
timeout=20000, timeout=20000,
) )
self.mock_update_client.assert_has_calls([ self.on_new_user_event.assert_has_calls([
call(observer_user=self.u_banana, call(rooms=[self.room_id]),
observed_user=self.u_apple,
room_id=self.room_id,
typing=True),
]) ])
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 @defer.inlineCallbacks
def test_started_typing_remote_send(self): def test_started_typing_remote_send(self):
self.room_members = [self.u_apple, self.u_onion] self.room_members = [self.u_apple, self.u_onion]
@ -198,6 +221,8 @@ class TypingNotificationsTestCase(unittest.TestCase):
def test_started_typing_remote_recv(self): def test_started_typing_remote_recv(self):
self.room_members = [self.u_apple, self.u_onion] 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", yield self.mock_federation_resource.trigger("PUT",
"/_matrix/federation/v1/send/1000000/", "/_matrix/federation/v1/send/1000000/",
_make_edu_json("farm", "m.typing", _make_edu_json("farm", "m.typing",
@ -209,13 +234,22 @@ class TypingNotificationsTestCase(unittest.TestCase):
) )
) )
self.mock_update_client.assert_has_calls([ self.on_new_user_event.assert_has_calls([
call(observer_user=self.u_apple, call(rooms=[self.room_id]),
observed_user=self.u_onion,
room_id=self.room_id,
typing=True),
]) ])
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 @defer.inlineCallbacks
def test_stopped_typing(self): def test_stopped_typing(self):
self.room_members = [self.u_apple, self.u_banana, self.u_onion] self.room_members = [self.u_apple, self.u_banana, self.u_onion]
@ -238,9 +272,14 @@ class TypingNotificationsTestCase(unittest.TestCase):
# Gut-wrenching # Gut-wrenching
from synapse.handlers.typing import RoomMember from synapse.handlers.typing import RoomMember
self.handler._member_typing_until[ member = RoomMember(self.room_id, self.u_apple)
RoomMember(self.room_id, self.u_apple) self.handler._member_typing_until[member] = 1002000
] = 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( yield self.handler.stopped_typing(
target_user=self.u_apple, target_user=self.u_apple,
@ -248,11 +287,68 @@ class TypingNotificationsTestCase(unittest.TestCase):
room_id=self.room_id, room_id=self.room_id,
) )
self.mock_update_client.assert_has_calls([ self.on_new_user_event.assert_has_calls([
call(observer_user=self.u_banana, call(rooms=[self.room_id]),
observed_user=self.u_apple,
room_id=self.room_id,
typing=False),
]) ])
yield put_json.await_calls() 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": [],
}},
]
)

View File

@ -1066,7 +1066,3 @@ class RoomInitialSyncTestCase(RestTestCase):
} }
self.assertTrue(self.user_id in presence_by_user) self.assertTrue(self.user_id in presence_by_user)
self.assertEquals("m.presence", presence_by_user[self.user_id]["type"]) 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)

115
tests/rest/test_typing.py Normal file
View File

@ -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)