From 6e8b5ab96a14847756a7cba9ff9a489062c7b608 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Mon, 13 Aug 2018 23:52:39 +1000 Subject: [PATCH 1/7] new unittest base, plus porting some tests --- tests/rest/client/v1/test_typing.py | 44 ++++++++++------------ tests/unittest.py | 58 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py index 677265edf602..e0f0d1985fb4 100644 --- a/tests/rest/client/v1/test_typing.py +++ b/tests/rest/client/v1/test_typing.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2014-2016 OpenMarket Ltd +# Copyright 2018 New Vector # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,41 +18,34 @@ from mock import Mock, NonCallableMock -# twisted imports from twisted.internet import defer -import synapse.rest.client.v1.room +from synapse.rest.client.v1 import room from synapse.types import UserID -from ....utils import MockClock, MockHttpResource, setup_test_homeserver -from .utils import RestTestCase +from tests import unittest +from tests.server import make_request, setup_test_homeserver PATH_PREFIX = "/_matrix/client/api/v1" -class RoomTypingTestCase(RestTestCase): +class RoomTypingTestCase(unittest.HomeserverTestCase): """ Tests /rooms/$room_id/typing/$user_id REST API. """ user_id = "@sid:red" user = UserID.from_string(user_id) + servlets = [room.register_servlets] - @defer.inlineCallbacks - def setUp(self): - self.clock = MockClock() - - self.mock_resource = MockHttpResource(prefix=PATH_PREFIX) - self.auth_user_id = self.user_id + def make_homeserver(self, reactor, clock, hs_args): hs = yield setup_test_homeserver( self.addCleanup, "red", - clock=self.clock, http_client=None, federation_client=Mock(), ratelimiter=NonCallableMock(spec_set=["send_message"]), ) - self.hs = hs self.event_source = hs.get_event_sources().sources["typing"] @@ -100,20 +94,21 @@ def 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 - 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): (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.render(request) + self.assertEquals(200, channel.code) self.assertEquals(self.event_source.get_current_key(), 1) events = yield self.event_source.get_new_events( @@ -130,27 +125,27 @@ def test_set_typing(self): ], ) - @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) + self.render(request) + self.assertEquals(200, channel.code) - @defer.inlineCallbacks def test_typing_timeout(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.render(request) + self.assertEquals(200, channel.code) 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) @@ -159,6 +154,7 @@ def test_typing_timeout(self): "/rooms/%s/typing/%s" % (self.room_id, self.user_id), '{"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) diff --git a/tests/unittest.py b/tests/unittest.py index f448a6dfbd40..4580283ec585 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -15,12 +15,18 @@ import logging +from mock import Mock + import twisted import twisted.logger from twisted.trial import unittest +from synapse.http.server import JsonResource +from synapse.types import UserID, create_requester from synapse.util.logcontext import LoggingContextFilter +from tests.server import get_clock, render + # Set up putting Synapse's logs into Trial's. rootLogger = logging.getLogger() @@ -129,3 +135,55 @@ def DEBUG(target): Can apply to either a TestCase or an individual test method.""" target.loglevel = logging.DEBUG return target + + +class HomeserverTestCase(TestCase): + + servlets = [] + hijack_auth = True + + def setUp(self): + + self.reactor, self.clock = get_clock() + hs_args = {"clock": self.clock, "reactor": self.reactor} + self.hs = self.make_homeserver(self.reactor, self.clock, hs_args) + + if self.hs is None: + raise Exception("No homeserver returned from make_homeserver.") + + # 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 render(self, request): + render(request, self.resource, self.reactor) From ab716912889bb8f6c26fb66e1e49884483cd3409 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Mon, 13 Aug 2018 23:58:36 +1000 Subject: [PATCH 2/7] further cleanups --- tests/rest/client/v1/test_typing.py | 26 +++++++++++--------------- tests/unittest.py | 12 +++++++++--- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py index e0f0d1985fb4..7fb1e9fbeb2f 100644 --- a/tests/rest/client/v1/test_typing.py +++ b/tests/rest/client/v1/test_typing.py @@ -24,7 +24,6 @@ from synapse.types import UserID from tests import unittest -from tests.server import make_request, setup_test_homeserver PATH_PREFIX = "/_matrix/client/api/v1" @@ -37,10 +36,9 @@ class RoomTypingTestCase(unittest.HomeserverTestCase): user = UserID.from_string(user_id) servlets = [room.register_servlets] - def make_homeserver(self, reactor, clock, hs_args): + def make_homeserver(self, reactor, clock): - hs = yield setup_test_homeserver( - self.addCleanup, + hs = yield self.setup_test_homeserver( "red", http_client=None, federation_client=Mock(), @@ -102,18 +100,16 @@ def prepare(self, reactor, clock, hs): self.helper.join(self.room_id, user="@jim:red") def test_set_typing(self): - (code, _) = yield self.mock_resource.trigger( + request, channel = self.make_request( "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id), - '{"typing": true, "timeout": 30000}', + b'{"typing": true, "timeout": 30000}', ) self.render(request) self.assertEquals(200, channel.code) self.assertEquals(self.event_source.get_current_key(), 1) - events = yield self.event_source.get_new_events( - from_key=0, room_ids=[self.room_id] - ) + events = self.event_source.get_new_events(from_key=0, room_ids=[self.room_id]) self.assertEquals( events[0], [ @@ -126,19 +122,19 @@ def test_set_typing(self): ) def test_set_not_typing(self): - (code, _) = yield self.mock_resource.trigger( + request, channel = self.make_request( "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id), - '{"typing": false}', + b'{"typing": false}', ) self.render(request) self.assertEquals(200, channel.code) def test_typing_timeout(self): - (code, _) = yield self.mock_resource.trigger( + request, channel = self.make_request( "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id), - '{"typing": true, "timeout": 30000}', + b'{"typing": true, "timeout": 30000}', ) self.render(request) self.assertEquals(200, channel.code) @@ -149,10 +145,10 @@ def test_typing_timeout(self): self.assertEquals(self.event_source.get_current_key(), 2) - (code, _) = yield self.mock_resource.trigger( + request, channel = self.make_request( "PUT", "/rooms/%s/typing/%s" % (self.room_id, self.user_id), - '{"typing": true, "timeout": 30000}', + b'{"typing": true, "timeout": 30000}', ) self.render(request) self.assertEquals(200, channel.code) diff --git a/tests/unittest.py b/tests/unittest.py index 4580283ec585..805bc903eb00 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -25,7 +25,7 @@ from synapse.types import UserID, create_requester from synapse.util.logcontext import LoggingContextFilter -from tests.server import get_clock, render +from tests.server import get_clock, render, setup_test_homeserver, make_request # Set up putting Synapse's logs into Trial's. rootLogger = logging.getLogger() @@ -145,8 +145,8 @@ class HomeserverTestCase(TestCase): def setUp(self): self.reactor, self.clock = get_clock() - hs_args = {"clock": self.clock, "reactor": self.reactor} - self.hs = self.make_homeserver(self.reactor, self.clock, hs_args) + 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.") @@ -187,3 +187,9 @@ def get_user_by_req(request, allow_guest=False, rights="access"): def render(self, request): render(request, self.resource, self.reactor) + + def setup_test_homeserver(self, *args, **kwargs): + return setup_test_homeserver(self.addCleanup, *args, **kwargs, **self._hs_args) + + def make_request(self, *args, **kwargs): + return make_request(*args, **kwargs) From 8c3678445e697538a51d601de9f8f3080c0e341d Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Tue, 14 Aug 2018 00:24:41 +1000 Subject: [PATCH 3/7] fixes & cleanups --- tests/rest/client/v1/test_typing.py | 2 +- tests/unittest.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py index 7fb1e9fbeb2f..0ad814c5e588 100644 --- a/tests/rest/client/v1/test_typing.py +++ b/tests/rest/client/v1/test_typing.py @@ -38,7 +38,7 @@ class RoomTypingTestCase(unittest.HomeserverTestCase): def make_homeserver(self, reactor, clock): - hs = yield self.setup_test_homeserver( + hs = self.setup_test_homeserver( "red", http_client=None, federation_client=Mock(), diff --git a/tests/unittest.py b/tests/unittest.py index 805bc903eb00..d7c502bb4cd6 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -21,6 +21,7 @@ import twisted.logger from twisted.trial import unittest +from synapse.server import HomeServer from synapse.http.server import JsonResource from synapse.types import UserID, create_requester from synapse.util.logcontext import LoggingContextFilter @@ -151,6 +152,9 @@ def setUp(self): 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) @@ -189,7 +193,9 @@ def render(self, request): render(request, self.resource, self.reactor) def setup_test_homeserver(self, *args, **kwargs): - return setup_test_homeserver(self.addCleanup, *args, **kwargs, **self._hs_args) + kwargs = dict(kwargs) + kwargs.update(self._hs_args) + return setup_test_homeserver(self.addCleanup, *args, **kwargs) def make_request(self, *args, **kwargs): return make_request(*args, **kwargs) From df25bb9eccd789cd9ecec1bc89643b526c1db6f8 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Tue, 14 Aug 2018 00:33:55 +1000 Subject: [PATCH 4/7] changelog --- changelog.d/3684.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/3684.misc diff --git a/changelog.d/3684.misc b/changelog.d/3684.misc new file mode 100644 index 000000000000..4c013263c453 --- /dev/null +++ b/changelog.d/3684.misc @@ -0,0 +1 @@ +Implemented a new testing base class to reduce test boilerplate. From 2e41914e9d82a0e765ca915b4431baf6e8b95c25 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Tue, 14 Aug 2018 00:34:23 +1000 Subject: [PATCH 5/7] whoops, isort --- tests/unittest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unittest.py b/tests/unittest.py index d7c502bb4cd6..02eb025c052b 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -21,12 +21,12 @@ import twisted.logger from twisted.trial import unittest -from synapse.server import HomeServer 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 tests.server import get_clock, render, setup_test_homeserver, make_request +from tests.server import get_clock, make_request, render, setup_test_homeserver # Set up putting Synapse's logs into Trial's. rootLogger = logging.getLogger() From acb811791a118fa85fdab63364f6e9f94d07a2d2 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Tue, 14 Aug 2018 20:14:43 +1000 Subject: [PATCH 6/7] docstrings --- tests/unittest.py | 82 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/tests/unittest.py b/tests/unittest.py index 02eb025c052b..c1c92be882e4 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -16,6 +16,7 @@ import logging from mock import Mock +import abc import twisted import twisted.logger @@ -139,12 +140,24 @@ def DEBUG(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) @@ -189,13 +202,74 @@ def get_user_by_req(request, allow_guest=False, rights="access"): 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) - - def make_request(self, *args, **kwargs): - return make_request(*args, **kwargs) From 1025b498149a46b7e7a5d6f038366b5f00ed16cc Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Tue, 14 Aug 2018 20:20:29 +1000 Subject: [PATCH 7/7] fix --- tests/unittest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittest.py b/tests/unittest.py index c1c92be882e4..e6afe3b96d61 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2014-2016 OpenMarket Ltd +# Copyright 2018 New Vector # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +17,6 @@ import logging from mock import Mock -import abc import twisted import twisted.logger