Update test_typing to use HomeserverTestCase. (#4771)

This commit is contained in:
Richard van der Hoff 2019-03-04 10:05:39 +00:00 committed by GitHub
parent 3064952939
commit 2c3548d9d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 176 additions and 179 deletions

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

@ -0,0 +1 @@
Update test_typing to use HomeserverTestCase.

View File

@ -24,13 +24,17 @@ from synapse.api.errors import AuthError
from synapse.types import UserID from synapse.types import UserID
from tests import unittest from tests import unittest
from tests.utils import register_federation_servlets
from ..utils import ( # Some local users to test with
DeferredMockCallable, U_APPLE = UserID.from_string("@apple:test")
MockClock, U_BANANA = UserID.from_string("@banana:test")
MockHttpResource,
setup_test_homeserver, # Remote user
) U_ONION = UserID.from_string("@onion:farm")
# Test room id
ROOM_ID = "a-room"
def _expect_edu_transaction(edu_type, content, origin="test"): def _expect_edu_transaction(edu_type, content, origin="test"):
@ -46,30 +50,21 @@ def _make_edu_transaction_json(edu_type, content):
return json.dumps(_expect_edu_transaction(edu_type, content)).encode('utf8') return json.dumps(_expect_edu_transaction(edu_type, content)).encode('utf8')
class TypingNotificationsTestCase(unittest.TestCase): class TypingNotificationsTestCase(unittest.HomeserverTestCase):
"""Tests typing notifications to rooms.""" servlets = [register_federation_servlets]
@defer.inlineCallbacks def make_homeserver(self, reactor, clock):
def setUp(self): # we mock out the keyring so as to skip the authentication check on the
self.clock = MockClock() # federation API call.
mock_keyring = Mock(spec=["verify_json_for_server"])
mock_keyring.verify_json_for_server.return_value = defer.succeed(True)
self.mock_http_client = Mock(spec=[]) # we mock out the federation client too
self.mock_http_client.put_json = DeferredMockCallable() mock_federation_client = Mock(spec=["put_json"])
mock_federation_client.put_json.return_value = defer.succeed((200, "OK"))
self.mock_federation_resource = MockHttpResource() hs = self.setup_test_homeserver(
datastore=(Mock(
mock_notifier = Mock()
self.on_new_event = mock_notifier.on_new_event
self.auth = Mock(spec=[])
self.state_handler = Mock()
hs = yield setup_test_homeserver(
self.addCleanup,
"test",
auth=self.auth,
clock=self.clock,
datastore=Mock(
spec=[ spec=[
# Bits that Federation needs # Bits that Federation needs
"prep_send_transaction", "prep_send_transaction",
@ -82,16 +77,21 @@ class TypingNotificationsTestCase(unittest.TestCase):
"get_user_directory_stream_pos", "get_user_directory_stream_pos",
"get_current_state_deltas", "get_current_state_deltas",
] ]
), )),
state_handler=self.state_handler, notifier=Mock(),
handlers=Mock(), http_client=mock_federation_client,
notifier=mock_notifier, keyring=mock_keyring,
resource_for_client=Mock(),
resource_for_federation=self.mock_federation_resource,
http_client=self.mock_http_client,
keyring=Mock(),
) )
return hs
def prepare(self, reactor, clock, hs):
# the tests assume that we are starting at unix time 1000
reactor.pump((1000, ))
mock_notifier = hs.get_notifier()
self.on_new_event = mock_notifier.on_new_event
self.handler = hs.get_typing_handler() self.handler = hs.get_typing_handler()
self.event_source = hs.get_event_sources().sources["typing"] self.event_source = hs.get_event_sources().sources["typing"]
@ -109,13 +109,12 @@ class TypingNotificationsTestCase(unittest.TestCase):
self.datastore.get_received_txn_response = get_received_txn_response self.datastore.get_received_txn_response = get_received_txn_response
self.room_id = "a-room"
self.room_members = [] self.room_members = []
def check_joined_room(room_id, user_id): def check_joined_room(room_id, user_id):
if user_id not in [u.to_string() for u in self.room_members]: if user_id not in [u.to_string() for u in self.room_members]:
raise AuthError(401, "User is not in the room") raise AuthError(401, "User is not in the room")
hs.get_auth().check_joined_room = check_joined_room
def get_joined_hosts_for_room(room_id): def get_joined_hosts_for_room(room_id):
return set(member.domain for member in self.room_members) return set(member.domain for member in self.room_members)
@ -124,8 +123,7 @@ class TypingNotificationsTestCase(unittest.TestCase):
def get_current_user_in_room(room_id): def get_current_user_in_room(room_id):
return set(str(u) for u in self.room_members) return set(str(u) for u in self.room_members)
hs.get_state_handler().get_current_user_in_room = get_current_user_in_room
self.state_handler.get_current_user_in_room = get_current_user_in_room
self.datastore.get_user_directory_stream_pos.return_value = ( self.datastore.get_user_directory_stream_pos.return_value = (
# we deliberately return a non-None stream pos to avoid doing an initial_spam # we deliberately return a non-None stream pos to avoid doing an initial_spam
@ -134,230 +132,208 @@ class TypingNotificationsTestCase(unittest.TestCase):
self.datastore.get_current_state_deltas.return_value = None self.datastore.get_current_state_deltas.return_value = None
self.auth.check_joined_room = check_joined_room
self.datastore.get_to_device_stream_token = lambda: 0 self.datastore.get_to_device_stream_token = lambda: 0
self.datastore.get_new_device_msgs_for_remote = lambda *args, **kargs: ([], 0) self.datastore.get_new_device_msgs_for_remote = lambda *args, **kargs: ([], 0)
self.datastore.delete_device_msgs_for_remote = lambda *args, **kargs: None self.datastore.delete_device_msgs_for_remote = lambda *args, **kargs: None
# Some local users to test with
self.u_apple = UserID.from_string("@apple:test")
self.u_banana = UserID.from_string("@banana:test")
# Remote user
self.u_onion = UserID.from_string("@onion:farm")
@defer.inlineCallbacks
def test_started_typing_local(self): def test_started_typing_local(self):
self.room_members = [self.u_apple, self.u_banana] self.room_members = [U_APPLE, U_BANANA]
self.assertEquals(self.event_source.get_current_key(), 0) self.assertEquals(self.event_source.get_current_key(), 0)
yield self.handler.started_typing( self.successResultOf(self.handler.started_typing(
target_user=self.u_apple, target_user=U_APPLE,
auth_user=self.u_apple, auth_user=U_APPLE,
room_id=self.room_id, room_id=ROOM_ID,
timeout=20000, timeout=20000,
) ))
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 1, rooms=[self.room_id])] [call('typing_key', 1, rooms=[ROOM_ID])]
) )
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(
room_ids=[self.room_id], from_key=0 room_ids=[ROOM_ID], from_key=0
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": [self.u_apple.to_string()]}, "content": {"user_ids": [U_APPLE.to_string()]},
} }
], ],
) )
@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 = [U_APPLE, U_ONION]
put_json = self.mock_http_client.put_json self.successResultOf(self.handler.started_typing(
put_json.expect_call_and_return( target_user=U_APPLE,
call( auth_user=U_APPLE,
"farm", room_id=ROOM_ID,
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu_transaction(
"m.typing",
content={
"room_id": self.room_id,
"user_id": self.u_apple.to_string(),
"typing": True,
},
),
json_data_callback=ANY,
long_retries=True,
backoff_on_404=True,
),
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, timeout=20000,
))
put_json = self.hs.get_http_client().put_json
put_json.assert_called_once_with(
"farm",
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu_transaction(
"m.typing",
content={
"room_id": ROOM_ID,
"user_id": U_APPLE.to_string(),
"typing": True,
},
),
json_data_callback=ANY,
long_retries=True,
backoff_on_404=True,
) )
yield put_json.await_calls()
@defer.inlineCallbacks
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 = [U_APPLE, U_ONION]
self.assertEquals(self.event_source.get_current_key(), 0) self.assertEquals(self.event_source.get_current_key(), 0)
(code, response) = yield self.mock_federation_resource.trigger( (request, channel) = self.make_request(
"PUT", "PUT",
"/_matrix/federation/v1/send/1000000/", "/_matrix/federation/v1/send/1000000/",
_make_edu_transaction_json( _make_edu_transaction_json(
"m.typing", "m.typing",
content={ content={
"room_id": self.room_id, "room_id": ROOM_ID,
"user_id": self.u_onion.to_string(), "user_id": U_ONION.to_string(),
"typing": True, "typing": True,
}, },
), ),
federation_auth_origin=b'farm', federation_auth_origin=b'farm',
) )
self.render(request)
self.assertEqual(channel.code, 200)
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 1, rooms=[self.room_id])] [call('typing_key', 1, rooms=[ROOM_ID])]
) )
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(
room_ids=[self.room_id], from_key=0 room_ids=[ROOM_ID], from_key=0
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": [self.u_onion.to_string()]}, "content": {"user_ids": [U_ONION.to_string()]},
} }
], ],
) )
@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 = [U_APPLE, U_BANANA, U_ONION]
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call(
"farm",
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu_transaction(
"m.typing",
content={
"room_id": self.room_id,
"user_id": self.u_apple.to_string(),
"typing": False,
},
),
json_data_callback=ANY,
long_retries=True,
backoff_on_404=True,
),
defer.succeed((200, "OK")),
)
# Gut-wrenching # Gut-wrenching
from synapse.handlers.typing import RoomMember from synapse.handlers.typing import RoomMember
member = RoomMember(self.room_id, self.u_apple.to_string()) member = RoomMember(ROOM_ID, U_APPLE.to_string())
self.handler._member_typing_until[member] = 1002000 self.handler._member_typing_until[member] = 1002000
self.handler._room_typing[self.room_id] = set([self.u_apple.to_string()]) self.handler._room_typing[ROOM_ID] = set([U_APPLE.to_string()])
self.assertEquals(self.event_source.get_current_key(), 0) self.assertEquals(self.event_source.get_current_key(), 0)
yield self.handler.stopped_typing( self.successResultOf(self.handler.stopped_typing(
target_user=self.u_apple, auth_user=self.u_apple, room_id=self.room_id target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID
) ))
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 1, rooms=[self.room_id])] [call('typing_key', 1, rooms=[ROOM_ID])]
) )
yield put_json.await_calls() put_json = self.hs.get_http_client().put_json
put_json.assert_called_once_with(
"farm",
path="/_matrix/federation/v1/send/1000000/",
data=_expect_edu_transaction(
"m.typing",
content={
"room_id": ROOM_ID,
"user_id": U_APPLE.to_string(),
"typing": False,
},
),
json_data_callback=ANY,
long_retries=True,
backoff_on_404=True,
)
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(
room_ids=[self.room_id], from_key=0 room_ids=[ROOM_ID], from_key=0
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": []}, "content": {"user_ids": []},
} }
], ],
) )
@defer.inlineCallbacks
def test_typing_timeout(self): def test_typing_timeout(self):
self.room_members = [self.u_apple, self.u_banana] self.room_members = [U_APPLE, U_BANANA]
self.assertEquals(self.event_source.get_current_key(), 0) self.assertEquals(self.event_source.get_current_key(), 0)
yield self.handler.started_typing( self.successResultOf(self.handler.started_typing(
target_user=self.u_apple, target_user=U_APPLE,
auth_user=self.u_apple, auth_user=U_APPLE,
room_id=self.room_id, room_id=ROOM_ID,
timeout=10000, timeout=10000,
) ))
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 1, rooms=[self.room_id])] [call('typing_key', 1, rooms=[ROOM_ID])]
) )
self.on_new_event.reset_mock() self.on_new_event.reset_mock()
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(
room_ids=[self.room_id], from_key=0 room_ids=[ROOM_ID], from_key=0
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": [self.u_apple.to_string()]}, "content": {"user_ids": [U_APPLE.to_string()]},
} }
], ],
) )
self.clock.advance_time(16) self.reactor.pump([16, ])
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 2, rooms=[self.room_id])] [call('typing_key', 2, rooms=[ROOM_ID])]
) )
self.assertEquals(self.event_source.get_current_key(), 2) self.assertEquals(self.event_source.get_current_key(), 2)
events = yield self.event_source.get_new_events( events = self.event_source.get_new_events(
room_ids=[self.room_id], from_key=1 room_ids=[ROOM_ID], from_key=1
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": []}, "content": {"user_ids": []},
} }
], ],
@ -365,29 +341,29 @@ class TypingNotificationsTestCase(unittest.TestCase):
# SYN-230 - see if we can still set after timeout # SYN-230 - see if we can still set after timeout
yield self.handler.started_typing( self.successResultOf(self.handler.started_typing(
target_user=self.u_apple, target_user=U_APPLE,
auth_user=self.u_apple, auth_user=U_APPLE,
room_id=self.room_id, room_id=ROOM_ID,
timeout=10000, timeout=10000,
) ))
self.on_new_event.assert_has_calls( self.on_new_event.assert_has_calls(
[call('typing_key', 3, rooms=[self.room_id])] [call('typing_key', 3, rooms=[ROOM_ID])]
) )
self.on_new_event.reset_mock() self.on_new_event.reset_mock()
self.assertEquals(self.event_source.get_current_key(), 3) self.assertEquals(self.event_source.get_current_key(), 3)
events = yield self.event_source.get_new_events( events = self.event_source.get_new_events(
room_ids=[self.room_id], from_key=0 room_ids=[ROOM_ID], from_key=0
) )
self.assertEquals( self.assertEquals(
events[0], events[0],
[ [
{ {
"type": "m.typing", "type": "m.typing",
"room_id": self.room_id, "room_id": ROOM_ID,
"content": {"user_ids": [self.u_apple.to_string()]}, "content": {"user_ids": [U_APPLE.to_string()]},
} }
], ],
) )

View File

@ -137,6 +137,7 @@ def make_request(
access_token=None, access_token=None,
request=SynapseRequest, request=SynapseRequest,
shorthand=True, shorthand=True,
federation_auth_origin=None,
): ):
""" """
Make a web request using the given method and path, feed it the Make a web request using the given method and path, feed it the
@ -150,9 +151,11 @@ def make_request(
a dict. a dict.
shorthand: Whether to try and be helpful and prefix the given URL shorthand: Whether to try and be helpful and prefix the given URL
with the usual REST API path, if it doesn't contain it. with the usual REST API path, if it doesn't contain it.
federation_auth_origin (bytes|None): if set to not-None, we will add a fake
Authorization header pretenting to be the given server name.
Returns: Returns:
A synapse.http.site.SynapseRequest. Tuple[synapse.http.site.SynapseRequest, channel]
""" """
if not isinstance(method, bytes): if not isinstance(method, bytes):
method = method.encode('ascii') method = method.encode('ascii')
@ -184,6 +187,11 @@ def make_request(
b"Authorization", b"Bearer " + access_token.encode('ascii') b"Authorization", b"Bearer " + access_token.encode('ascii')
) )
if federation_auth_origin is not None:
req.requestHeaders.addRawHeader(
b"Authorization", b"X-Matrix origin=%s,key=,sig=" % (federation_auth_origin,)
)
if content: if content:
req.requestHeaders.addRawHeader(b"Content-Type", b"application/json") req.requestHeaders.addRawHeader(b"Content-Type", b"application/json")
@ -288,9 +296,6 @@ def setup_test_homeserver(cleanup_func, *args, **kwargs):
**kwargs **kwargs
) )
pool.runWithConnection = runWithConnection
pool.runInteraction = runInteraction
class ThreadPool: class ThreadPool:
""" """
Threadless thread pool. Threadless thread pool.
@ -316,8 +321,12 @@ def setup_test_homeserver(cleanup_func, *args, **kwargs):
return d return d
clock.threadpool = ThreadPool() clock.threadpool = ThreadPool()
pool.threadpool = ThreadPool()
pool.running = True if pool:
pool.runWithConnection = runWithConnection
pool.runInteraction = runInteraction
pool.threadpool = ThreadPool()
pool.running = True
return d return d

View File

@ -262,6 +262,7 @@ class HomeserverTestCase(TestCase):
access_token=None, access_token=None,
request=SynapseRequest, request=SynapseRequest,
shorthand=True, shorthand=True,
federation_auth_origin=None,
): ):
""" """
Create a SynapseRequest at the path using the method and containing the Create a SynapseRequest at the path using the method and containing the
@ -275,15 +276,18 @@ class HomeserverTestCase(TestCase):
a dict. a dict.
shorthand: Whether to try and be helpful and prefix the given URL shorthand: Whether to try and be helpful and prefix the given URL
with the usual REST API path, if it doesn't contain it. with the usual REST API path, if it doesn't contain it.
federation_auth_origin (bytes|None): if set to not-None, we will add a fake
Authorization header pretenting to be the given server name.
Returns: Returns:
A synapse.http.site.SynapseRequest. Tuple[synapse.http.site.SynapseRequest, channel]
""" """
if isinstance(content, dict): if isinstance(content, dict):
content = json.dumps(content).encode('utf8') content = json.dumps(content).encode('utf8')
return make_request( return make_request(
self.reactor, method, path, content, access_token, request, shorthand self.reactor, method, path, content, access_token, request, shorthand,
federation_auth_origin,
) )
def render(self, request): def render(self, request):

View File

@ -29,7 +29,7 @@ from twisted.internet import defer, reactor
from synapse.api.constants import EventTypes, RoomVersions from synapse.api.constants import EventTypes, RoomVersions
from synapse.api.errors import CodeMessageException, cs_error from synapse.api.errors import CodeMessageException, cs_error
from synapse.config.server import ServerConfig from synapse.config.server import ServerConfig
from synapse.federation.transport import server from synapse.federation.transport import server as federation_server
from synapse.http.server import HttpServer from synapse.http.server import HttpServer
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.storage import DataStore from synapse.storage import DataStore
@ -200,6 +200,9 @@ def setup_test_homeserver(
Args: Args:
cleanup_func : The function used to register a cleanup routine for cleanup_func : The function used to register a cleanup routine for
after the test. after the test.
Calling this method directly is deprecated: you should instead derive from
HomeserverTestCase.
""" """
if reactor is None: if reactor is None:
from twisted.internet import reactor from twisted.internet import reactor
@ -351,23 +354,27 @@ def setup_test_homeserver(
fed = kargs.get("resource_for_federation", None) fed = kargs.get("resource_for_federation", None)
if fed: if fed:
server.register_servlets( register_federation_servlets(hs, fed)
hs,
resource=fed,
authenticator=server.Authenticator(hs),
ratelimiter=FederationRateLimiter(
hs.get_clock(),
window_size=hs.config.federation_rc_window_size,
sleep_limit=hs.config.federation_rc_sleep_limit,
sleep_msec=hs.config.federation_rc_sleep_delay,
reject_limit=hs.config.federation_rc_reject_limit,
concurrent_requests=hs.config.federation_rc_concurrent,
),
)
defer.returnValue(hs) defer.returnValue(hs)
def register_federation_servlets(hs, resource):
federation_server.register_servlets(
hs,
resource=resource,
authenticator=federation_server.Authenticator(hs),
ratelimiter=FederationRateLimiter(
hs.get_clock(),
window_size=hs.config.federation_rc_window_size,
sleep_limit=hs.config.federation_rc_sleep_limit,
sleep_msec=hs.config.federation_rc_sleep_delay,
reject_limit=hs.config.federation_rc_reject_limit,
concurrent_requests=hs.config.federation_rc_concurrent,
),
)
def get_mock_call_args(pattern_func, mock_func): def get_mock_call_args(pattern_func, mock_func):
""" Return the arguments the mock function was called with interpreted """ Return the arguments the mock function was called with interpreted
by the pattern functions argument list. by the pattern functions argument list.