Newer
Older
Paul "LeoNerd" Evans
committed
# -*- coding: utf-8 -*-
Paul "LeoNerd" Evans
committed
#
# 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.
Paul "LeoNerd" Evans
committed
from tests import unittest
Paul "LeoNerd" Evans
committed
from twisted.internet import defer
from mock import Mock, call, ANY
import json
from ..utils import (
MockHttpResource, MockClock, DeferredMockCallable, setup_test_homeserver
)
Paul "LeoNerd" Evans
committed
Paul "LeoNerd" Evans
committed
from synapse.api.errors import AuthError
Paul "LeoNerd" Evans
committed
from synapse.handlers.typing import TypingNotificationHandler
from synapse.types import UserID
Matthew Hodgson
committed
Paul "LeoNerd" Evans
committed
def _expect_edu(destination, edu_type, content, origin="test"):
return {
"origin": origin,
Paul "LeoNerd" Evans
committed
"pdus": [],
"edus": [
{
"edu_type": edu_type,
"content": content,
}
],
Paul "LeoNerd" Evans
committed
}
def _make_edu_json(origin, edu_type, content):
return json.dumps(_expect_edu("test", edu_type, content, origin=origin))
class JustTypingNotificationHandlers(object):
def __init__(self, hs):
self.typing_notification_handler = TypingNotificationHandler(hs)
Paul "LeoNerd" Evans
committed
class TypingNotificationsTestCase(unittest.TestCase):
"""Tests typing notifications to rooms."""
@defer.inlineCallbacks
Paul "LeoNerd" Evans
committed
def setUp(self):
self.clock = MockClock()
self.mock_http_client = Mock(spec=[])
self.mock_http_client.put_json = DeferredMockCallable()
self.mock_federation_resource = MockHttpResource()
mock_notifier = Mock(spec=["on_new_event"])
self.on_new_event = mock_notifier.on_new_event
Paul "LeoNerd" Evans
committed
self.auth = Mock(spec=[])
hs = yield setup_test_homeserver(
auth=self.auth,
clock=self.clock,
datastore=Mock(spec=[
# Bits that Federation needs
"prep_send_transaction",
"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,
keyring=Mock(),
)
Paul "LeoNerd" Evans
committed
hs.handlers = JustTypingNotificationHandlers(hs)
self.handler = hs.get_handlers().typing_notification_handler
self.event_source = hs.get_event_sources().sources["typing"]
Paul "LeoNerd" Evans
committed
self.datastore = hs.get_datastore()
retry_timings_res = {
"destination": "",
"retry_last_ts": 0,
"retry_interval": 0,
}
Matthew Hodgson
committed
self.datastore.get_destination_retry_timings.return_value = (
defer.succeed(retry_timings_res)
Matthew Hodgson
committed
)
Paul "LeoNerd" Evans
committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def get_received_txn_response(*args):
return defer.succeed(None)
self.datastore.get_received_txn_response = get_received_txn_response
self.room_id = "a-room"
# Mock the RoomMemberHandler
hs.handlers.room_member_handler = Mock(spec=[])
self.room_member_handler = hs.handlers.room_member_handler
self.room_members = []
def get_rooms_for_user(user):
if user in self.room_members:
return defer.succeed([self.room_id])
else:
return defer.succeed([])
self.room_member_handler.get_rooms_for_user = get_rooms_for_user
def get_room_members(room_id):
if room_id == self.room_id:
return defer.succeed(self.room_members)
else:
return defer.succeed([])
self.room_member_handler.get_room_members = get_room_members
Paul "LeoNerd" Evans
committed
def get_joined_rooms_for_user(user):
if user in self.room_members:
return defer.succeed([self.room_id])
else:
return defer.succeed([])
self.room_member_handler.get_joined_rooms_for_user = get_joined_rooms_for_user
Paul "LeoNerd" Evans
committed
@defer.inlineCallbacks
def fetch_room_distributions_into(
room_id, localusers=None, remotedomains=None, ignore_user=None
):
Paul "LeoNerd" Evans
committed
members = yield get_room_members(room_id)
for member in members:
if ignore_user is not None and member == ignore_user:
continue
Paul "LeoNerd" Evans
committed
if localusers is not None:
localusers.add(member)
else:
if remotedomains is not None:
remotedomains.add(member.domain)
self.room_member_handler.fetch_room_distributions_into = (
Paul "LeoNerd" Evans
committed
Paul "LeoNerd" Evans
committed
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
Paul "LeoNerd" Evans
committed
# Some local users to test with
self.u_apple = UserID.from_string("@apple:test")
self.u_banana = UserID.from_string("@banana:test")
Paul "LeoNerd" Evans
committed
# Remote user
self.u_onion = UserID.from_string("@onion:farm")
Paul "LeoNerd" Evans
committed
@defer.inlineCallbacks
def test_started_typing_local(self):
self.room_members = [self.u_apple, self.u_banana]
self.assertEquals(self.event_source.get_current_key(), 0)
Paul "LeoNerd" Evans
committed
yield self.handler.started_typing(
target_user=self.u_apple,
auth_user=self.u_apple,
room_id=self.room_id,
timeout=20000,
)
self.on_new_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 1, rooms=[self.room_id]),
Paul "LeoNerd" Evans
committed
])
self.assertEquals(self.event_source.get_current_key(), 1)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=0,
)
self.assertEquals(
Paul "LeoNerd" Evans
committed
events[0],
[
{"type": "m.typing",
"room_id": self.room_id,
Paul "LeoNerd" Evans
committed
"content": {
"user_ids": [self.u_apple.to_string()],
}},
Paul "LeoNerd" Evans
committed
@defer.inlineCallbacks
def test_started_typing_remote_send(self):
self.room_members = [self.u_apple, self.u_onion]
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu(
"farm",
"m.typing",
Paul "LeoNerd" Evans
committed
content={
"room_id": self.room_id,
"user_id": self.u_apple.to_string(),
"typing": True,
}
json_data_callback=ANY,
Paul "LeoNerd" Evans
committed
),
defer.succeed((200, "OK"))
)
yield self.handler.started_typing(
target_user=self.u_apple,
auth_user=self.u_apple,
room_id=self.room_id,
timeout=20000,
)
yield put_json.await_calls()
@defer.inlineCallbacks
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",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
content={
"room_id": self.room_id,
"user_id": self.u_onion.to_string(),
"typing": True,
}
)
)
self.on_new_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 1, rooms=[self.room_id]),
Paul "LeoNerd" Evans
committed
])
self.assertEquals(self.event_source.get_current_key(), 1)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=0
)
self.assertEquals(events[0], [{
"type": "m.typing",
"room_id": self.room_id,
"content": {
"user_ids": [self.u_onion.to_string()],
},
}])
Paul "LeoNerd" Evans
committed
@defer.inlineCallbacks
def test_stopped_typing(self):
self.room_members = [self.u_apple, self.u_banana, self.u_onion]
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu(
"farm",
"m.typing",
Paul "LeoNerd" Evans
committed
content={
"room_id": self.room_id,
"user_id": self.u_apple.to_string(),
"typing": False,
}
json_data_callback=ANY,
Paul "LeoNerd" Evans
committed
),
defer.succeed((200, "OK"))
)
# Gut-wrenching
from synapse.handlers.typing import RoomMember
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,))
Paul "LeoNerd" Evans
committed
self.assertEquals(self.event_source.get_current_key(), 0)
Paul "LeoNerd" Evans
committed
yield self.handler.stopped_typing(
target_user=self.u_apple,
auth_user=self.u_apple,
room_id=self.room_id,
)
self.on_new_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 1, rooms=[self.room_id]),
Paul "LeoNerd" Evans
committed
])
yield put_json.await_calls()
self.assertEquals(self.event_source.get_current_key(), 1)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=0,
)
self.assertEquals(events[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_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 1, rooms=[self.room_id]),
])
self.assertEquals(self.event_source.get_current_key(), 1)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=0,
)
self.assertEquals(events[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_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 2, rooms=[self.room_id]),
])
self.assertEquals(self.event_source.get_current_key(), 2)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=1,
)
self.assertEquals(events[0], [{
"type": "m.typing",
"room_id": self.room_id,
"content": {
"user_ids": [],
},
}])
Paul "LeoNerd" Evans
committed
# SYN-230 - see if we can still set after timeout
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_event.assert_has_calls([
Mark Haines
committed
call('typing_key', 3, rooms=[self.room_id]),
Paul "LeoNerd" Evans
committed
])
Paul "LeoNerd" Evans
committed
self.assertEquals(self.event_source.get_current_key(), 3)
events = yield self.event_source.get_new_events(
room_ids=[self.room_id],
from_key=0,
)
self.assertEquals(events[0], [{
"type": "m.typing",
"room_id": self.room_id,
"content": {
"user_ids": [self.u_apple.to_string()],
},
}])