# -*- coding: utf-8 -*-
# Copyright 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import contextlib
import json
from mock import Mock, NonCallableMock
from twisted.internet import defer
import synapse.types
from synapse.replication.resource import ReplicationResource
from synapse.types import UserID
from tests import unittest
from tests.utils import setup_test_homeserver
class ReplicationResourceCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
self.hs = yield setup_test_homeserver(
"red",
http_client=None,
replication_layer=Mock(),
ratelimiter=NonCallableMock(spec_set=[
"send_message",
]),
)
self.user_id = "@seeing:red"
self.user = UserID.from_string(self.user_id)
self.hs.get_ratelimiter().send_message.return_value = (True, 0)
self.resource = ReplicationResource(self.hs)
@defer.inlineCallbacks
def test_streams(self):
# Passing "-1" returns the current stream positions
code, body = yield self.get(streams="-1")
self.assertEquals(code, 200)
self.assertEquals(body["streams"]["field_names"], ["name", "position"])
position = body["streams"]["position"]
# Passing the current position returns an empty response after the
# timeout
get = self.get(streams=str(position), timeout="0")
self.hs.clock.advance_time_msec(1)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body, {})
@defer.inlineCallbacks
def test_events(self):
get = self.get(events="-1", timeout="0")
yield self.hs.get_handlers().room_creation_handler.create_room(
synapse.types.create_requester(self.user), {}
)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body["events"]["field_names"], [
"position", "internal", "json", "state_group"
])
@defer.inlineCallbacks
def test_presence(self):
get = self.get(presence="-1")
yield self.hs.get_presence_handler().set_state(
self.user, {"presence": "online"}
)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body["presence"]["field_names"], [
"position", "user_id", "state", "last_active_ts",
"last_federation_update_ts", "last_user_sync_ts",
"status_msg", "currently_active",
])
@defer.inlineCallbacks
def test_typing(self):
room_id = yield self.create_room()
get = self.get(typing="-1")
yield self.hs.get_typing_handler().started_typing(
self.user, self.user, room_id, timeout=2
)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body["typing"]["field_names"], [
"position", "room_id", "typing"
])
@defer.inlineCallbacks
def test_receipts(self):
room_id = yield self.create_room()
event_id = yield self.send_text_message(room_id, "Hello, World")
get = self.get(receipts="-1")
yield self.hs.get_handlers().receipts_handler.received_client_receipt(
room_id, "m.read", self.user_id, event_id
)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body["receipts"]["field_names"], [
"position", "room_id", "receipt_type", "user_id", "event_id", "data"
])
def _test_timeout(stream):
"""Check that a request for the given stream timesout"""
@defer.inlineCallbacks
def test_timeout(self):
get = self.get(**{stream: "-1", "timeout": "0"})
self.hs.clock.advance_time_msec(1)
code, body = yield get
self.assertEquals(code, 200)
self.assertEquals(body, {})
test_timeout.__name__ = "test_timeout_%s" % (stream)
return test_timeout
test_timeout_events = _test_timeout("events")
test_timeout_presence = _test_timeout("presence")
test_timeout_typing = _test_timeout("typing")
test_timeout_receipts = _test_timeout("receipts")
test_timeout_user_account_data = _test_timeout("user_account_data")
test_timeout_room_account_data = _test_timeout("room_account_data")
test_timeout_tag_account_data = _test_timeout("tag_account_data")
test_timeout_backfill = _test_timeout("backfill")
test_timeout_push_rules = _test_timeout("push_rules")
test_timeout_pushers = _test_timeout("pushers")
test_timeout_state = _test_timeout("state")
@defer.inlineCallbacks
def send_text_message(self, room_id, message):
handler = self.hs.get_handlers().message_handler
event = yield handler.create_and_send_nonmember_event(
synapse.types.create_requester(self.user),
{
"type": "m.room.message",
"content": {"body": "message", "msgtype": "m.text"},
"room_id": room_id,
"sender": self.user.to_string(),
}
)
defer.returnValue(event.event_id)
@defer.inlineCallbacks
def create_room(self):
result = yield self.hs.get_handlers().room_creation_handler.create_room(
synapse.types.create_requester(self.user), {}
)
defer.returnValue(result["room_id"])
@defer.inlineCallbacks
def get(self, **params):
request = NonCallableMock(spec_set=[
"write", "finish", "setResponseCode", "setHeader", "args",
"method", "processing"
])
request.method = "GET"
request.args = {k: [v] for k, v in params.items()}
@contextlib.contextmanager
def processing():
yield
request.processing = processing
yield self.resource._async_render_GET(request)
self.assertTrue(request.finish.called)
if request.setResponseCode.called:
response_code = request.setResponseCode.call_args[0][0]
else:
response_code = 200
response_json = "".join(
call[0][0] for call in request.write.call_args_list
)
response_body = json.loads(response_json)
if response_code == 200:
self.check_response(response_body)
defer.returnValue((response_code, response_body))
def check_response(self, response_body):
for name, stream in response_body.items():
self.assertIn("field_names", stream)
field_names = stream["field_names"]
self.assertIn("rows", stream)
self.assertTrue(stream["rows"])
for row in stream["rows"]:
self.assertEquals(
len(row), len(field_names),
"%s: len(row = %r) == len(field_names = %r)" % (
name, row, field_names
)
)