diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py
index c0ed64f784..1b0a815757 100644
--- a/tests/api/test_auth.py
+++ b/tests/api/test_auth.py
@@ -21,13 +21,11 @@ from synapse.api.constants import UserTypes
from synapse.api.errors import (
AuthError,
Codes,
- InvalidClientCredentialsError,
InvalidClientTokenError,
MissingClientTokenError,
ResourceLimitError,
)
from synapse.storage.databases.main.registration import TokenLookupResult
-from synapse.types import UserID
from tests import unittest
from tests.test_utils import simple_async_mock
@@ -253,67 +251,6 @@ class AuthTestCase(unittest.HomeserverTestCase):
self.assertTrue(user_info.is_guest)
self.store.get_user_by_id.assert_called_with(user_id)
- def test_cannot_use_regular_token_as_guest(self):
- USER_ID = "@percy:matrix.org"
- self.store.add_access_token_to_user = simple_async_mock(None)
- self.store.get_device = simple_async_mock(None)
-
- token = self.get_success(
- self.hs.get_auth_handler().get_access_token_for_user_id(
- USER_ID, "DEVICE", valid_until_ms=None
- )
- )
- self.store.add_access_token_to_user.assert_called_with(
- user_id=USER_ID,
- token=token,
- device_id="DEVICE",
- valid_until_ms=None,
- puppets_user_id=None,
- )
-
- async def get_user(tok):
- if token != tok:
- return None
- return TokenLookupResult(
- user_id=USER_ID,
- is_guest=False,
- token_id=1234,
- device_id="DEVICE",
- )
-
- self.store.get_user_by_access_token = get_user
- self.store.get_user_by_id = simple_async_mock({"is_guest": False})
-
- # check the token works
- request = Mock(args={})
- request.args[b"access_token"] = [token.encode("ascii")]
- request.requestHeaders.getRawHeaders = mock_getRawHeaders()
- requester = self.get_success(
- self.auth.get_user_by_req(request, allow_guest=True)
- )
- self.assertEqual(UserID.from_string(USER_ID), requester.user)
- self.assertFalse(requester.is_guest)
-
- # add an is_guest caveat
- mac = pymacaroons.Macaroon.deserialize(token)
- mac.add_first_party_caveat("guest = true")
- guest_tok = mac.serialize()
-
- # the token should *not* work now
- request = Mock(args={})
- request.args[b"access_token"] = [guest_tok.encode("ascii")]
- request.requestHeaders.getRawHeaders = mock_getRawHeaders()
-
- cm = self.get_failure(
- self.auth.get_user_by_req(request, allow_guest=True),
- InvalidClientCredentialsError,
- )
-
- self.assertEqual(401, cm.value.code)
- self.assertEqual("Guest access token used for regular user", cm.value.msg)
-
- self.store.get_user_by_id.assert_called_with(USER_ID)
-
def test_blocking_mau(self):
self.auth_blocking._limit_usage_by_mau = False
self.auth_blocking._max_mau_value = 50
diff --git a/tests/api/test_ratelimiting.py b/tests/api/test_ratelimiting.py
index fa96ba07a5..dcf0110c16 100644
--- a/tests/api/test_ratelimiting.py
+++ b/tests/api/test_ratelimiting.py
@@ -230,3 +230,60 @@ class TestRatelimiter(unittest.HomeserverTestCase):
# Shouldn't raise
for _ in range(20):
self.get_success_or_raise(limiter.ratelimit(requester, _time_now_s=0))
+
+ def test_multiple_actions(self):
+ limiter = Ratelimiter(
+ store=self.hs.get_datastore(), clock=None, rate_hz=0.1, burst_count=3
+ )
+ # Test that 4 actions aren't allowed with a maximum burst of 3.
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(None, key="test_id", n_actions=4, _time_now_s=0)
+ )
+ self.assertFalse(allowed)
+
+ # Test that 3 actions are allowed with a maximum burst of 3.
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(None, key="test_id", n_actions=3, _time_now_s=0)
+ )
+ self.assertTrue(allowed)
+ self.assertEquals(10.0, time_allowed)
+
+ # Test that, after doing these 3 actions, we can't do any more action without
+ # waiting.
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(None, key="test_id", n_actions=1, _time_now_s=0)
+ )
+ self.assertFalse(allowed)
+ self.assertEquals(10.0, time_allowed)
+
+ # Test that after waiting we can do only 1 action.
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(
+ None,
+ key="test_id",
+ update=False,
+ n_actions=1,
+ _time_now_s=10,
+ )
+ )
+ self.assertTrue(allowed)
+ # The time allowed is the current time because we could still repeat the action
+ # once.
+ self.assertEquals(10.0, time_allowed)
+
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(None, key="test_id", n_actions=2, _time_now_s=10)
+ )
+ self.assertFalse(allowed)
+ # The time allowed doesn't change despite allowed being False because, while we
+ # don't allow 2 actions, we could still do 1.
+ self.assertEquals(10.0, time_allowed)
+
+ # Test that after waiting a bit more we can do 2 actions.
+ allowed, time_allowed = self.get_success_or_raise(
+ limiter.can_do_action(None, key="test_id", n_actions=2, _time_now_s=20)
+ )
+ self.assertTrue(allowed)
+ # The time allowed is the current time because we could still repeat the action
+ # once.
+ self.assertEquals(20.0, time_allowed)
diff --git a/tests/handlers/test_auth.py b/tests/handlers/test_auth.py
index fe7e9484fd..5f3350e490 100644
--- a/tests/handlers/test_auth.py
+++ b/tests/handlers/test_auth.py
@@ -16,12 +16,17 @@ from unittest.mock import Mock
import pymacaroons
from synapse.api.errors import AuthError, ResourceLimitError
+from synapse.rest import admin
from tests import unittest
from tests.test_utils import make_awaitable
class AuthTestCase(unittest.HomeserverTestCase):
+ servlets = [
+ admin.register_servlets,
+ ]
+
def prepare(self, reactor, clock, hs):
self.auth_handler = hs.get_auth_handler()
self.macaroon_generator = hs.get_macaroon_generator()
@@ -35,16 +40,10 @@ class AuthTestCase(unittest.HomeserverTestCase):
self.small_number_of_users = 1
self.large_number_of_users = 100
- def test_token_is_a_macaroon(self):
- token = self.macaroon_generator.generate_access_token("some_user")
- # Check that we can parse the thing with pymacaroons
- macaroon = pymacaroons.Macaroon.deserialize(token)
- # The most basic of sanity checks
- if "some_user" not in macaroon.inspect():
- self.fail("some_user was not in %s" % macaroon.inspect())
+ self.user1 = self.register_user("a_user", "pass")
def test_macaroon_caveats(self):
- token = self.macaroon_generator.generate_access_token("a_user")
+ token = self.macaroon_generator.generate_guest_access_token("a_user")
macaroon = pymacaroons.Macaroon.deserialize(token)
def verify_gen(caveat):
@@ -59,19 +58,23 @@ class AuthTestCase(unittest.HomeserverTestCase):
def verify_nonce(caveat):
return caveat.startswith("nonce =")
+ def verify_guest(caveat):
+ return caveat == "guest = true"
+
v = pymacaroons.Verifier()
v.satisfy_general(verify_gen)
v.satisfy_general(verify_user)
v.satisfy_general(verify_type)
v.satisfy_general(verify_nonce)
+ v.satisfy_general(verify_guest)
v.verify(macaroon, self.hs.config.macaroon_secret_key)
def test_short_term_login_token_gives_user_id(self):
token = self.macaroon_generator.generate_short_term_login_token(
- "a_user", "", 5000
+ self.user1, "", 5000
)
res = self.get_success(self.auth_handler.validate_short_term_login_token(token))
- self.assertEqual("a_user", res.user_id)
+ self.assertEqual(self.user1, res.user_id)
self.assertEqual("", res.auth_provider_id)
# when we advance the clock, the token should be rejected
@@ -83,22 +86,22 @@ class AuthTestCase(unittest.HomeserverTestCase):
def test_short_term_login_token_gives_auth_provider(self):
token = self.macaroon_generator.generate_short_term_login_token(
- "a_user", auth_provider_id="my_idp"
+ self.user1, auth_provider_id="my_idp"
)
res = self.get_success(self.auth_handler.validate_short_term_login_token(token))
- self.assertEqual("a_user", res.user_id)
+ self.assertEqual(self.user1, res.user_id)
self.assertEqual("my_idp", res.auth_provider_id)
def test_short_term_login_token_cannot_replace_user_id(self):
token = self.macaroon_generator.generate_short_term_login_token(
- "a_user", "", 5000
+ self.user1, "", 5000
)
macaroon = pymacaroons.Macaroon.deserialize(token)
res = self.get_success(
self.auth_handler.validate_short_term_login_token(macaroon.serialize())
)
- self.assertEqual("a_user", res.user_id)
+ self.assertEqual(self.user1, res.user_id)
# add another "user_id" caveat, which might allow us to override the
# user_id.
@@ -114,7 +117,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
# Ensure does not throw exception
self.get_success(
self.auth_handler.get_access_token_for_user_id(
- "user_a", device_id=None, valid_until_ms=None
+ self.user1, device_id=None, valid_until_ms=None
)
)
@@ -132,7 +135,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
self.get_failure(
self.auth_handler.get_access_token_for_user_id(
- "user_a", device_id=None, valid_until_ms=None
+ self.user1, device_id=None, valid_until_ms=None
),
ResourceLimitError,
)
@@ -160,7 +163,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
# If not in monthly active cohort
self.get_failure(
self.auth_handler.get_access_token_for_user_id(
- "user_a", device_id=None, valid_until_ms=None
+ self.user1, device_id=None, valid_until_ms=None
),
ResourceLimitError,
)
@@ -177,7 +180,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
)
self.get_success(
self.auth_handler.get_access_token_for_user_id(
- "user_a", device_id=None, valid_until_ms=None
+ self.user1, device_id=None, valid_until_ms=None
)
)
self.get_success(
@@ -195,7 +198,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
# Ensure does not raise exception
self.get_success(
self.auth_handler.get_access_token_for_user_id(
- "user_a", device_id=None, valid_until_ms=None
+ self.user1, device_id=None, valid_until_ms=None
)
)
@@ -210,6 +213,6 @@ class AuthTestCase(unittest.HomeserverTestCase):
def _get_macaroon(self):
token = self.macaroon_generator.generate_short_term_login_token(
- "user_a", "", 5000
+ self.user1, "", 5000
)
return pymacaroons.Macaroon.deserialize(token)
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index ce330e79cc..1ffab709fc 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -729,7 +729,7 @@ class PresenceJoinTestCase(unittest.HomeserverTestCase):
)
self.assertEqual(expected_state.state, PresenceState.ONLINE)
self.federation_sender.send_presence_to_destinations.assert_called_once_with(
- destinations=["server2"], states={expected_state}
+ destinations={"server2"}, states=[expected_state]
)
#
@@ -740,7 +740,7 @@ class PresenceJoinTestCase(unittest.HomeserverTestCase):
self._add_new_user(room_id, "@bob:server3")
self.federation_sender.send_presence_to_destinations.assert_called_once_with(
- destinations=["server3"], states={expected_state}
+ destinations={"server3"}, states=[expected_state]
)
def test_remote_gets_presence_when_local_user_joins(self):
@@ -788,14 +788,8 @@ class PresenceJoinTestCase(unittest.HomeserverTestCase):
self.presence_handler.current_state_for_user("@test2:server")
)
self.assertEqual(expected_state.state, PresenceState.ONLINE)
- self.assertEqual(
- self.federation_sender.send_presence_to_destinations.call_count, 2
- )
- self.federation_sender.send_presence_to_destinations.assert_any_call(
- destinations=["server3"], states={expected_state}
- )
- self.federation_sender.send_presence_to_destinations.assert_any_call(
- destinations=["server2"], states={expected_state}
+ self.federation_sender.send_presence_to_destinations.assert_called_once_with(
+ destinations={"server2", "server3"}, states=[expected_state]
)
def _add_new_user(self, room_id, user_id):
diff --git a/tests/handlers/test_register.py b/tests/handlers/test_register.py
index 2812d7e8f5..aa2999f174 100644
--- a/tests/handlers/test_register.py
+++ b/tests/handlers/test_register.py
@@ -57,10 +57,6 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
self.mock_distributor = Mock()
self.mock_distributor.declare("registered_user")
self.mock_captcha_client = Mock()
- self.macaroon_generator = Mock(
- generate_access_token=Mock(return_value="secret")
- )
- self.hs.get_macaroon_generator = Mock(return_value=self.macaroon_generator)
self.handler = self.hs.get_registration_handler()
self.store = self.hs.get_datastore()
self.lots_of_users = 100
@@ -76,8 +72,8 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
self.get_or_create_user(requester, frank.localpart, "Frankie")
)
self.assertEquals(result_user_id, user_id)
- self.assertTrue(result_token is not None)
- self.assertEquals(result_token, "secret")
+ self.assertIsInstance(result_token, str)
+ self.assertGreater(len(result_token), 20)
def test_if_user_exists(self):
store = self.hs.get_datastore()
@@ -509,7 +505,7 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
user_id = self.get_success(self.handler.register_user(localpart="user"))
# Get an access token.
- token = self.macaroon_generator.generate_access_token(user_id)
+ token = "testtok"
self.get_success(
self.store.add_access_token_to_user(
user_id=user_id, token=token, device_id=None, valid_until_ms=None
@@ -683,7 +679,7 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
user = UserID(localpart, self.hs.hostname)
user_id = user.to_string()
- token = self.macaroon_generator.generate_access_token(user_id)
+ token = self.hs.get_auth_handler().generate_access_token(user)
if need_register:
await self.handler.register_with_store(
diff --git a/tests/handlers/test_space_summary.py b/tests/handlers/test_space_summary.py
new file mode 100644
index 0000000000..2c5e81531b
--- /dev/null
+++ b/tests/handlers/test_space_summary.py
@@ -0,0 +1,81 @@
+# Copyright 2021 The Matrix.org Foundation C.I.C.
+#
+# 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.
+from typing import Any, Optional
+from unittest import mock
+
+from synapse.handlers.space_summary import _child_events_comparison_key
+
+from tests import unittest
+
+
+def _create_event(room_id: str, order: Optional[Any] = None):
+ result = mock.Mock()
+ result.room_id = room_id
+ result.content = {}
+ if order is not None:
+ result.content["order"] = order
+ return result
+
+
+def _order(*events):
+ return sorted(events, key=_child_events_comparison_key)
+
+
+class TestSpaceSummarySort(unittest.TestCase):
+ def test_no_order_last(self):
+ """An event with no ordering is placed behind those with an ordering."""
+ ev1 = _create_event("!abc:test")
+ ev2 = _create_event("!xyz:test", "xyz")
+
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ def test_order(self):
+ """The ordering should be used."""
+ ev1 = _create_event("!abc:test", "xyz")
+ ev2 = _create_event("!xyz:test", "abc")
+
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ def test_order_room_id(self):
+ """Room ID is a tie-breaker for ordering."""
+ ev1 = _create_event("!abc:test", "abc")
+ ev2 = _create_event("!xyz:test", "abc")
+
+ self.assertEqual([ev1, ev2], _order(ev1, ev2))
+
+ def test_invalid_ordering_type(self):
+ """Invalid orderings are considered the same as missing."""
+ ev1 = _create_event("!abc:test", 1)
+ ev2 = _create_event("!xyz:test", "xyz")
+
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ ev1 = _create_event("!abc:test", {})
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ ev1 = _create_event("!abc:test", [])
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ ev1 = _create_event("!abc:test", True)
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ def test_invalid_ordering_value(self):
+ """Invalid orderings are considered the same as missing."""
+ ev1 = _create_event("!abc:test", "foo\n")
+ ev2 = _create_event("!xyz:test", "xyz")
+
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
+
+ ev1 = _create_event("!abc:test", "a" * 51)
+ self.assertEqual([ev2, ev1], _order(ev1, ev2))
diff --git a/tests/rest/admin/test_room.py b/tests/rest/admin/test_room.py
index 6b84188120..ee071c2477 100644
--- a/tests/rest/admin/test_room.py
+++ b/tests/rest/admin/test_room.py
@@ -17,6 +17,8 @@ import urllib.parse
from typing import List, Optional
from unittest.mock import Mock
+from parameterized import parameterized_class
+
import synapse.rest.admin
from synapse.api.constants import EventTypes, Membership
from synapse.api.errors import Codes
@@ -144,6 +146,13 @@ class ShutdownRoomTestCase(unittest.HomeserverTestCase):
)
+@parameterized_class(
+ ("method", "url_template"),
+ [
+ ("POST", "/_synapse/admin/v1/rooms/%s/delete"),
+ ("DELETE", "/_synapse/admin/v1/rooms/%s"),
+ ],
+)
class DeleteRoomTestCase(unittest.HomeserverTestCase):
servlets = [
synapse.rest.admin.register_servlets,
@@ -175,7 +184,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
self.room_id = self.helper.create_room_as(
self.other_user, tok=self.other_user_tok
)
- self.url = "/_synapse/admin/v1/rooms/%s/delete" % self.room_id
+ self.url = self.url_template % self.room_id
def test_requester_is_no_admin(self):
"""
@@ -183,7 +192,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
"""
channel = self.make_request(
- "POST",
+ self.method,
self.url,
json.dumps({}),
access_token=self.other_user_tok,
@@ -196,10 +205,10 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
"""
Check that unknown rooms/server return error 404.
"""
- url = "/_synapse/admin/v1/rooms/!unknown:test/delete"
+ url = self.url_template % "!unknown:test"
channel = self.make_request(
- "POST",
+ self.method,
url,
json.dumps({}),
access_token=self.admin_user_tok,
@@ -212,10 +221,10 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
"""
Check that invalid room names, return an error 400.
"""
- url = "/_synapse/admin/v1/rooms/invalidroom/delete"
+ url = self.url_template % "invalidroom"
channel = self.make_request(
- "POST",
+ self.method,
url,
json.dumps({}),
access_token=self.admin_user_tok,
@@ -234,7 +243,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"new_room_user_id": "@unknown:test"})
channel = self.make_request(
- "POST",
+ self.method,
self.url,
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -253,7 +262,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"new_room_user_id": "@not:exist.bla"})
channel = self.make_request(
- "POST",
+ self.method,
self.url,
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -272,7 +281,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"block": "NotBool"})
channel = self.make_request(
- "POST",
+ self.method,
self.url,
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -288,7 +297,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"purge": "NotBool"})
channel = self.make_request(
- "POST",
+ self.method,
self.url,
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -314,7 +323,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"block": True, "purge": True})
channel = self.make_request(
- "POST",
+ self.method,
self.url.encode("ascii"),
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -347,7 +356,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"block": False, "purge": True})
channel = self.make_request(
- "POST",
+ self.method,
self.url.encode("ascii"),
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -381,7 +390,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
body = json.dumps({"block": False, "purge": False})
channel = self.make_request(
- "POST",
+ self.method,
self.url.encode("ascii"),
content=body.encode(encoding="utf_8"),
access_token=self.admin_user_tok,
@@ -426,10 +435,9 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
self._is_member(room_id=self.room_id, user_id=self.other_user)
# Test that the admin can still send shutdown
- url = "/_synapse/admin/v1/rooms/%s/delete" % self.room_id
channel = self.make_request(
- "POST",
- url.encode("ascii"),
+ self.method,
+ self.url,
json.dumps({"new_room_user_id": self.admin_user}),
access_token=self.admin_user_tok,
)
@@ -473,10 +481,9 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
self._is_member(room_id=self.room_id, user_id=self.other_user)
# Test that the admin can still send shutdown
- url = "/_synapse/admin/v1/rooms/%s/delete" % self.room_id
channel = self.make_request(
- "POST",
- url.encode("ascii"),
+ self.method,
+ self.url,
json.dumps({"new_room_user_id": self.admin_user}),
access_token=self.admin_user_tok,
)
diff --git a/tests/rest/client/v1/test_rooms.py b/tests/rest/client/v1/test_rooms.py
index a3694f3d02..7c4bdcdfdd 100644
--- a/tests/rest/client/v1/test_rooms.py
+++ b/tests/rest/client/v1/test_rooms.py
@@ -463,6 +463,43 @@ class RoomsCreateTestCase(RoomBase):
)
self.assertEquals(400, channel.code)
+ @unittest.override_config({"rc_invites": {"per_room": {"burst_count": 3}}})
+ def test_post_room_invitees_ratelimit(self):
+ """Test that invites sent when creating a room are ratelimited by a RateLimiter,
+ which ratelimits them correctly, including by not limiting when the requester is
+ exempt from ratelimiting.
+ """
+
+ # Build the request's content. We use local MXIDs because invites over federation
+ # are more difficult to mock.
+ content = json.dumps(
+ {
+ "invite": [
+ "@alice1:red",
+ "@alice2:red",
+ "@alice3:red",
+ "@alice4:red",
+ ]
+ }
+ ).encode("utf8")
+
+ # Test that the invites are correctly ratelimited.
+ channel = self.make_request("POST", "/createRoom", content)
+ self.assertEqual(400, channel.code)
+ self.assertEqual(
+ "Cannot invite so many users at once",
+ channel.json_body["error"],
+ )
+
+ # Add the current user to the ratelimit overrides, allowing them no ratelimiting.
+ self.get_success(
+ self.hs.get_datastore().set_ratelimit_for_user(self.user_id, 0, 0)
+ )
+
+ # Test that the invites aren't ratelimited anymore.
+ channel = self.make_request("POST", "/createRoom", content)
+ self.assertEqual(200, channel.code)
+
class RoomTopicTestCase(RoomBase):
""" Tests /rooms/$room_id/topic REST events. """
diff --git a/tests/rest/client/v2_alpha/test_sendtodevice.py b/tests/rest/client/v2_alpha/test_sendtodevice.py
new file mode 100644
index 0000000000..c9c99cc5d7
--- /dev/null
+++ b/tests/rest/client/v2_alpha/test_sendtodevice.py
@@ -0,0 +1,201 @@
+# Copyright 2021 The Matrix.org Foundation C.I.C.
+#
+# 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.
+
+from synapse.rest import admin
+from synapse.rest.client.v1 import login
+from synapse.rest.client.v2_alpha import sendtodevice, sync
+
+from tests.unittest import HomeserverTestCase, override_config
+
+
+class SendToDeviceTestCase(HomeserverTestCase):
+ servlets = [
+ admin.register_servlets,
+ login.register_servlets,
+ sendtodevice.register_servlets,
+ sync.register_servlets,
+ ]
+
+ def test_user_to_user(self):
+ """A to-device message from one user to another should get delivered"""
+
+ user1 = self.register_user("u1", "pass")
+ user1_tok = self.login("u1", "pass", "d1")
+
+ user2 = self.register_user("u2", "pass")
+ user2_tok = self.login("u2", "pass", "d2")
+
+ # send the message
+ test_msg = {"foo": "bar"}
+ chan = self.make_request(
+ "PUT",
+ "/_matrix/client/r0/sendToDevice/m.test/1234",
+ content={"messages": {user2: {"d2": test_msg}}},
+ access_token=user1_tok,
+ )
+ self.assertEqual(chan.code, 200, chan.result)
+
+ # check it appears
+ channel = self.make_request("GET", "/sync", access_token=user2_tok)
+ self.assertEqual(channel.code, 200, channel.result)
+ expected_result = {
+ "events": [
+ {
+ "sender": user1,
+ "type": "m.test",
+ "content": test_msg,
+ }
+ ]
+ }
+ self.assertEqual(channel.json_body["to_device"], expected_result)
+
+ # it should re-appear if we do another sync
+ channel = self.make_request("GET", "/sync", access_token=user2_tok)
+ self.assertEqual(channel.code, 200, channel.result)
+ self.assertEqual(channel.json_body["to_device"], expected_result)
+
+ # it should *not* appear if we do an incremental sync
+ sync_token = channel.json_body["next_batch"]
+ channel = self.make_request(
+ "GET", f"/sync?since={sync_token}", access_token=user2_tok
+ )
+ self.assertEqual(channel.code, 200, channel.result)
+ self.assertEqual(channel.json_body.get("to_device", {}).get("events", []), [])
+
+ @override_config({"rc_key_requests": {"per_second": 10, "burst_count": 2}})
+ def test_local_room_key_request(self):
+ """m.room_key_request has special-casing; test from local user"""
+ user1 = self.register_user("u1", "pass")
+ user1_tok = self.login("u1", "pass", "d1")
+
+ user2 = self.register_user("u2", "pass")
+ user2_tok = self.login("u2", "pass", "d2")
+
+ # send three messages
+ for i in range(3):
+ chan = self.make_request(
+ "PUT",
+ f"/_matrix/client/r0/sendToDevice/m.room_key_request/{i}",
+ content={"messages": {user2: {"d2": {"idx": i}}}},
+ access_token=user1_tok,
+ )
+ self.assertEqual(chan.code, 200, chan.result)
+
+ # now sync: we should get two of the three
+ channel = self.make_request("GET", "/sync", access_token=user2_tok)
+ self.assertEqual(channel.code, 200, channel.result)
+ msgs = channel.json_body["to_device"]["events"]
+ self.assertEqual(len(msgs), 2)
+ for i in range(2):
+ self.assertEqual(
+ msgs[i],
+ {"sender": user1, "type": "m.room_key_request", "content": {"idx": i}},
+ )
+ sync_token = channel.json_body["next_batch"]
+
+ # ... time passes
+ self.reactor.advance(1)
+
+ # and we can send more messages
+ chan = self.make_request(
+ "PUT",
+ "/_matrix/client/r0/sendToDevice/m.room_key_request/3",
+ content={"messages": {user2: {"d2": {"idx": 3}}}},
+ access_token=user1_tok,
+ )
+ self.assertEqual(chan.code, 200, chan.result)
+
+ # ... which should arrive
+ channel = self.make_request(
+ "GET", f"/sync?since={sync_token}", access_token=user2_tok
+ )
+ self.assertEqual(channel.code, 200, channel.result)
+ msgs = channel.json_body["to_device"]["events"]
+ self.assertEqual(len(msgs), 1)
+ self.assertEqual(
+ msgs[0],
+ {"sender": user1, "type": "m.room_key_request", "content": {"idx": 3}},
+ )
+
+ @override_config({"rc_key_requests": {"per_second": 10, "burst_count": 2}})
+ def test_remote_room_key_request(self):
+ """m.room_key_request has special-casing; test from remote user"""
+ user2 = self.register_user("u2", "pass")
+ user2_tok = self.login("u2", "pass", "d2")
+
+ federation_registry = self.hs.get_federation_registry()
+
+ # send three messages
+ for i in range(3):
+ self.get_success(
+ federation_registry.on_edu(
+ "m.direct_to_device",
+ "remote_server",
+ {
+ "sender": "@user:remote_server",
+ "type": "m.room_key_request",
+ "messages": {user2: {"d2": {"idx": i}}},
+ "message_id": f"{i}",
+ },
+ )
+ )
+
+ # now sync: we should get two of the three
+ channel = self.make_request("GET", "/sync", access_token=user2_tok)
+ self.assertEqual(channel.code, 200, channel.result)
+ msgs = channel.json_body["to_device"]["events"]
+ self.assertEqual(len(msgs), 2)
+ for i in range(2):
+ self.assertEqual(
+ msgs[i],
+ {
+ "sender": "@user:remote_server",
+ "type": "m.room_key_request",
+ "content": {"idx": i},
+ },
+ )
+ sync_token = channel.json_body["next_batch"]
+
+ # ... time passes
+ self.reactor.advance(1)
+
+ # and we can send more messages
+ self.get_success(
+ federation_registry.on_edu(
+ "m.direct_to_device",
+ "remote_server",
+ {
+ "sender": "@user:remote_server",
+ "type": "m.room_key_request",
+ "messages": {user2: {"d2": {"idx": 3}}},
+ "message_id": "3",
+ },
+ )
+ )
+
+ # ... which should arrive
+ channel = self.make_request(
+ "GET", f"/sync?since={sync_token}", access_token=user2_tok
+ )
+ self.assertEqual(channel.code, 200, channel.result)
+ msgs = channel.json_body["to_device"]["events"]
+ self.assertEqual(len(msgs), 1)
+ self.assertEqual(
+ msgs[0],
+ {
+ "sender": "@user:remote_server",
+ "type": "m.room_key_request",
+ "content": {"idx": 3},
+ },
+ )
diff --git a/tests/storage/test_cleanup_extrems.py b/tests/storage/test_cleanup_extrems.py
index aa20588bbe..77c4fe721c 100644
--- a/tests/storage/test_cleanup_extrems.py
+++ b/tests/storage/test_cleanup_extrems.py
@@ -47,10 +47,8 @@ class CleanupExtremBackgroundUpdateStoreTestCase(HomeserverTestCase):
)
schema_path = os.path.join(
- prepare_database.dir_path,
- "databases",
+ prepare_database.schema_path,
"main",
- "schema",
"delta",
"54",
"delete_forward_extremities.sql",
diff --git a/tests/util/test_stringutils.py b/tests/util/test_stringutils.py
index f7fecd9cf3..ad4dd7f007 100644
--- a/tests/util/test_stringutils.py
+++ b/tests/util/test_stringutils.py
@@ -13,7 +13,7 @@
# limitations under the License.
from synapse.api.errors import SynapseError
-from synapse.util.stringutils import assert_valid_client_secret
+from synapse.util.stringutils import assert_valid_client_secret, base62_encode
from .. import unittest
@@ -45,3 +45,9 @@ class StringUtilsTestCase(unittest.TestCase):
for client_secret in bad:
with self.assertRaises(SynapseError):
assert_valid_client_secret(client_secret)
+
+ def test_base62_encode(self):
+ self.assertEqual("0", base62_encode(0))
+ self.assertEqual("10", base62_encode(62))
+ self.assertEqual("1c", base62_encode(100))
+ self.assertEqual("001c", base62_encode(100, minwidth=4))
|