Implement a new test baseclass to cut down on boilerplate (#3684)

pull/3693/head
Amber Brown 2018-08-14 20:53:43 +10:00 committed by GitHub
parent 8f0c430ca4
commit bdfbd934d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 174 additions and 37 deletions

1
changelog.d/3684.misc Normal file
View File

@ -0,0 +1 @@
Implemented a new testing base class to reduce test boilerplate.

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd # Copyright 2014-2016 OpenMarket Ltd
# Copyright 2018 New Vector
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,41 +18,32 @@
from mock import Mock, NonCallableMock from mock import Mock, NonCallableMock
# twisted imports
from twisted.internet import defer from twisted.internet import defer
import synapse.rest.client.v1.room from synapse.rest.client.v1 import room
from synapse.types import UserID from synapse.types import UserID
from ....utils import MockClock, MockHttpResource, setup_test_homeserver from tests import unittest
from .utils import RestTestCase
PATH_PREFIX = "/_matrix/client/api/v1" PATH_PREFIX = "/_matrix/client/api/v1"
class RoomTypingTestCase(RestTestCase): class RoomTypingTestCase(unittest.HomeserverTestCase):
""" Tests /rooms/$room_id/typing/$user_id REST API. """ """ Tests /rooms/$room_id/typing/$user_id REST API. """
user_id = "@sid:red" user_id = "@sid:red"
user = UserID.from_string(user_id) user = UserID.from_string(user_id)
servlets = [room.register_servlets]
@defer.inlineCallbacks def make_homeserver(self, reactor, clock):
def setUp(self):
self.clock = MockClock()
self.mock_resource = MockHttpResource(prefix=PATH_PREFIX) hs = self.setup_test_homeserver(
self.auth_user_id = self.user_id
hs = yield setup_test_homeserver(
self.addCleanup,
"red", "red",
clock=self.clock,
http_client=None, http_client=None,
federation_client=Mock(), federation_client=Mock(),
ratelimiter=NonCallableMock(spec_set=["send_message"]), ratelimiter=NonCallableMock(spec_set=["send_message"]),
) )
self.hs = hs
self.event_source = hs.get_event_sources().sources["typing"] self.event_source = hs.get_event_sources().sources["typing"]
@ -100,25 +92,24 @@ class RoomTypingTestCase(RestTestCase):
fetch_room_distributions_into fetch_room_distributions_into
) )
synapse.rest.client.v1.room.register_servlets(hs, self.mock_resource) return hs
self.room_id = yield self.create_room_as(self.user_id) def prepare(self, reactor, clock, hs):
self.room_id = self.helper.create_room_as(self.user_id)
# Need another user to make notifications actually work # Need another user to make notifications actually work
yield self.join(self.room_id, user="@jim:red") self.helper.join(self.room_id, user="@jim:red")
@defer.inlineCallbacks
def test_set_typing(self): def test_set_typing(self):
(code, _) = yield self.mock_resource.trigger( request, channel = self.make_request(
"PUT", "PUT",
"/rooms/%s/typing/%s" % (self.room_id, self.user_id), "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
'{"typing": true, "timeout": 30000}', b'{"typing": true, "timeout": 30000}',
) )
self.assertEquals(200, code) self.render(request)
self.assertEquals(200, channel.code)
self.assertEquals(self.event_source.get_current_key(), 1) self.assertEquals(self.event_source.get_current_key(), 1)
events = yield self.event_source.get_new_events( events = self.event_source.get_new_events(from_key=0, room_ids=[self.room_id])
from_key=0, room_ids=[self.room_id]
)
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
@ -130,35 +121,36 @@ class RoomTypingTestCase(RestTestCase):
], ],
) )
@defer.inlineCallbacks
def test_set_not_typing(self): def test_set_not_typing(self):
(code, _) = yield self.mock_resource.trigger( request, channel = self.make_request(
"PUT", "PUT",
"/rooms/%s/typing/%s" % (self.room_id, self.user_id), "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
'{"typing": false}', b'{"typing": false}',
) )
self.assertEquals(200, code) self.render(request)
self.assertEquals(200, channel.code)
@defer.inlineCallbacks
def test_typing_timeout(self): def test_typing_timeout(self):
(code, _) = yield self.mock_resource.trigger( request, channel = self.make_request(
"PUT", "PUT",
"/rooms/%s/typing/%s" % (self.room_id, self.user_id), "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
'{"typing": true, "timeout": 30000}', b'{"typing": true, "timeout": 30000}',
) )
self.assertEquals(200, code) self.render(request)
self.assertEquals(200, channel.code)
self.assertEquals(self.event_source.get_current_key(), 1) self.assertEquals(self.event_source.get_current_key(), 1)
self.clock.advance_time(36) self.reactor.advance(36)
self.assertEquals(self.event_source.get_current_key(), 2) self.assertEquals(self.event_source.get_current_key(), 2)
(code, _) = yield self.mock_resource.trigger( request, channel = self.make_request(
"PUT", "PUT",
"/rooms/%s/typing/%s" % (self.room_id, self.user_id), "/rooms/%s/typing/%s" % (self.room_id, self.user_id),
'{"typing": true, "timeout": 30000}', b'{"typing": true, "timeout": 30000}',
) )
self.assertEquals(200, code) self.render(request)
self.assertEquals(200, channel.code)
self.assertEquals(self.event_source.get_current_key(), 3) self.assertEquals(self.event_source.get_current_key(), 3)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd # Copyright 2014-2016 OpenMarket Ltd
# Copyright 2018 New Vector
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,12 +16,19 @@
import logging import logging
from mock import Mock
import twisted import twisted
import twisted.logger import twisted.logger
from twisted.trial import unittest from twisted.trial import unittest
from synapse.http.server import JsonResource
from synapse.server import HomeServer
from synapse.types import UserID, create_requester
from synapse.util.logcontext import LoggingContextFilter from synapse.util.logcontext import LoggingContextFilter
from tests.server import get_clock, make_request, render, setup_test_homeserver
# Set up putting Synapse's logs into Trial's. # Set up putting Synapse's logs into Trial's.
rootLogger = logging.getLogger() rootLogger = logging.getLogger()
@ -129,3 +137,139 @@ def DEBUG(target):
Can apply to either a TestCase or an individual test method.""" Can apply to either a TestCase or an individual test method."""
target.loglevel = logging.DEBUG target.loglevel = logging.DEBUG
return target return target
class HomeserverTestCase(TestCase):
"""
A base TestCase that reduces boilerplate for HomeServer-using test cases.
Attributes:
servlets (list[function]): List of servlet registration function.
user_id (str): The user ID to assume if auth is hijacked.
hijack_auth (bool): Whether to hijack auth to return the user specified
in user_id.
"""
servlets = []
hijack_auth = True
def setUp(self):
"""
Set up the TestCase by calling the homeserver constructor, optionally
hijacking the authentication system to return a fixed user, and then
calling the prepare function.
"""
self.reactor, self.clock = get_clock()
self._hs_args = {"clock": self.clock, "reactor": self.reactor}
self.hs = self.make_homeserver(self.reactor, self.clock)
if self.hs is None:
raise Exception("No homeserver returned from make_homeserver.")
if not isinstance(self.hs, HomeServer):
raise Exception("A homeserver wasn't returned, but %r" % (self.hs,))
# Register the resources
self.resource = JsonResource(self.hs)
for servlet in self.servlets:
servlet(self.hs, self.resource)
if hasattr(self, "user_id"):
from tests.rest.client.v1.utils import RestHelper
self.helper = RestHelper(self.hs, self.resource, self.user_id)
if self.hijack_auth:
def get_user_by_access_token(token=None, allow_guest=False):
return {
"user": UserID.from_string(self.helper.auth_user_id),
"token_id": 1,
"is_guest": False,
}
def get_user_by_req(request, allow_guest=False, rights="access"):
return create_requester(
UserID.from_string(self.helper.auth_user_id), 1, False, None
)
self.hs.get_auth().get_user_by_req = get_user_by_req
self.hs.get_auth().get_user_by_access_token = get_user_by_access_token
self.hs.get_auth().get_access_token_from_request = Mock(
return_value="1234"
)
if hasattr(self, "prepare"):
self.prepare(self.reactor, self.clock, self.hs)
def make_homeserver(self, reactor, clock):
"""
Make and return a homeserver.
Args:
reactor: A Twisted Reactor, or something that pretends to be one.
clock (synapse.util.Clock): The Clock, associated with the reactor.
Returns:
A homeserver (synapse.server.HomeServer) suitable for testing.
Function to be overridden in subclasses.
"""
raise NotImplementedError()
def prepare(self, reactor, clock, homeserver):
"""
Prepare for the test. This involves things like mocking out parts of
the homeserver, or building test data common across the whole test
suite.
Args:
reactor: A Twisted Reactor, or something that pretends to be one.
clock (synapse.util.Clock): The Clock, associated with the reactor.
homeserver (synapse.server.HomeServer): The HomeServer to test
against.
Function to optionally be overridden in subclasses.
"""
def make_request(self, method, path, content=b""):
"""
Create a SynapseRequest at the path using the method and containing the
given content.
Args:
method (bytes/unicode): The HTTP request method ("verb").
path (bytes/unicode): The HTTP path, suitably URL encoded (e.g.
escaped UTF-8 & spaces and such).
content (bytes): The body of the request.
Returns:
A synapse.http.site.SynapseRequest.
"""
return make_request(method, path, content)
def render(self, request):
"""
Render a request against the resources registered by the test class's
servlets.
Args:
request (synapse.http.site.SynapseRequest): The request to render.
"""
render(request, self.resource, self.reactor)
def setup_test_homeserver(self, *args, **kwargs):
"""
Set up the test homeserver, meant to be called by the overridable
make_homeserver. It automatically passes through the test class's
clock & reactor.
Args:
See tests.utils.setup_test_homeserver.
Returns:
synapse.server.HomeServer
"""
kwargs = dict(kwargs)
kwargs.update(self._hs_args)
return setup_test_homeserver(self.addCleanup, *args, **kwargs)