mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-05-02 12:46:01 -04:00
Merge branch 'develop' of github.com:matrix-org/synapse into erikj/add_rate_limiting_to_joins
This commit is contained in:
commit
faba873d4b
121 changed files with 1937 additions and 1256 deletions
|
@ -50,13 +50,17 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
def test_regex_user_id_prefix_match(self):
|
||||
self.service.namespaces[ApplicationService.NS_USERS].append(_regex("@irc_.*"))
|
||||
self.event.sender = "@irc_foobar:matrix.org"
|
||||
self.assertTrue((yield self.service.is_interested(self.event)))
|
||||
self.assertTrue(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_user_id_prefix_no_match(self):
|
||||
self.service.namespaces[ApplicationService.NS_USERS].append(_regex("@irc_.*"))
|
||||
self.event.sender = "@someone_else:matrix.org"
|
||||
self.assertFalse((yield self.service.is_interested(self.event)))
|
||||
self.assertFalse(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_room_member_is_checked(self):
|
||||
|
@ -64,7 +68,9 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
self.event.sender = "@someone_else:matrix.org"
|
||||
self.event.type = "m.room.member"
|
||||
self.event.state_key = "@irc_foobar:matrix.org"
|
||||
self.assertTrue((yield self.service.is_interested(self.event)))
|
||||
self.assertTrue(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_room_id_match(self):
|
||||
|
@ -72,7 +78,9 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
_regex("!some_prefix.*some_suffix:matrix.org")
|
||||
)
|
||||
self.event.room_id = "!some_prefixs0m3th1nGsome_suffix:matrix.org"
|
||||
self.assertTrue((yield self.service.is_interested(self.event)))
|
||||
self.assertTrue(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_room_id_no_match(self):
|
||||
|
@ -80,19 +88,26 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
_regex("!some_prefix.*some_suffix:matrix.org")
|
||||
)
|
||||
self.event.room_id = "!XqBunHwQIXUiqCaoxq:matrix.org"
|
||||
self.assertFalse((yield self.service.is_interested(self.event)))
|
||||
self.assertFalse(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_alias_match(self):
|
||||
self.service.namespaces[ApplicationService.NS_ALIASES].append(
|
||||
_regex("#irc_.*:matrix.org")
|
||||
)
|
||||
self.store.get_aliases_for_room.return_value = [
|
||||
"#irc_foobar:matrix.org",
|
||||
"#athing:matrix.org",
|
||||
]
|
||||
self.store.get_users_in_room.return_value = []
|
||||
self.assertTrue((yield self.service.is_interested(self.event, self.store)))
|
||||
self.store.get_aliases_for_room.return_value = defer.succeed(
|
||||
["#irc_foobar:matrix.org", "#athing:matrix.org"]
|
||||
)
|
||||
self.store.get_users_in_room.return_value = defer.succeed([])
|
||||
self.assertTrue(
|
||||
(
|
||||
yield defer.ensureDeferred(
|
||||
self.service.is_interested(self.event, self.store)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def test_non_exclusive_alias(self):
|
||||
self.service.namespaces[ApplicationService.NS_ALIASES].append(
|
||||
|
@ -135,12 +150,17 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
self.service.namespaces[ApplicationService.NS_ALIASES].append(
|
||||
_regex("#irc_.*:matrix.org")
|
||||
)
|
||||
self.store.get_aliases_for_room.return_value = [
|
||||
"#xmpp_foobar:matrix.org",
|
||||
"#athing:matrix.org",
|
||||
]
|
||||
self.store.get_users_in_room.return_value = []
|
||||
self.assertFalse((yield self.service.is_interested(self.event, self.store)))
|
||||
self.store.get_aliases_for_room.return_value = defer.succeed(
|
||||
["#xmpp_foobar:matrix.org", "#athing:matrix.org"]
|
||||
)
|
||||
self.store.get_users_in_room.return_value = defer.succeed([])
|
||||
self.assertFalse(
|
||||
(
|
||||
yield defer.ensureDeferred(
|
||||
self.service.is_interested(self.event, self.store)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_regex_multiple_matches(self):
|
||||
|
@ -149,9 +169,17 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
)
|
||||
self.service.namespaces[ApplicationService.NS_USERS].append(_regex("@irc_.*"))
|
||||
self.event.sender = "@irc_foobar:matrix.org"
|
||||
self.store.get_aliases_for_room.return_value = ["#irc_barfoo:matrix.org"]
|
||||
self.store.get_users_in_room.return_value = []
|
||||
self.assertTrue((yield self.service.is_interested(self.event, self.store)))
|
||||
self.store.get_aliases_for_room.return_value = defer.succeed(
|
||||
["#irc_barfoo:matrix.org"]
|
||||
)
|
||||
self.store.get_users_in_room.return_value = defer.succeed([])
|
||||
self.assertTrue(
|
||||
(
|
||||
yield defer.ensureDeferred(
|
||||
self.service.is_interested(self.event, self.store)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_interested_in_self(self):
|
||||
|
@ -161,19 +189,24 @@ class ApplicationServiceTestCase(unittest.TestCase):
|
|||
self.event.type = "m.room.member"
|
||||
self.event.content = {"membership": "invite"}
|
||||
self.event.state_key = self.service.sender
|
||||
self.assertTrue((yield self.service.is_interested(self.event)))
|
||||
self.assertTrue(
|
||||
(yield defer.ensureDeferred(self.service.is_interested(self.event)))
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_member_list_match(self):
|
||||
self.service.namespaces[ApplicationService.NS_USERS].append(_regex("@irc_.*"))
|
||||
self.store.get_users_in_room.return_value = [
|
||||
"@alice:here",
|
||||
"@irc_fo:here", # AS user
|
||||
"@bob:here",
|
||||
]
|
||||
self.store.get_aliases_for_room.return_value = []
|
||||
# Note that @irc_fo:here is the AS user.
|
||||
self.store.get_users_in_room.return_value = defer.succeed(
|
||||
["@alice:here", "@irc_fo:here", "@bob:here"]
|
||||
)
|
||||
self.store.get_aliases_for_room.return_value = defer.succeed([])
|
||||
|
||||
self.event.sender = "@xmpp_foobar:matrix.org"
|
||||
self.assertTrue(
|
||||
(yield self.service.is_interested(event=self.event, store=self.store))
|
||||
(
|
||||
yield defer.ensureDeferred(
|
||||
self.service.is_interested(event=self.event, store=self.store)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -25,6 +25,7 @@ from synapse.appservice.scheduler import (
|
|||
from synapse.logging.context import make_deferred_yieldable
|
||||
|
||||
from tests import unittest
|
||||
from tests.test_utils import make_awaitable
|
||||
|
||||
from ..utils import MockClock
|
||||
|
||||
|
@ -52,11 +53,11 @@ class ApplicationServiceSchedulerTransactionCtrlTestCase(unittest.TestCase):
|
|||
self.store.get_appservice_state = Mock(
|
||||
return_value=defer.succeed(ApplicationServiceState.UP)
|
||||
)
|
||||
txn.send = Mock(return_value=defer.succeed(True))
|
||||
txn.send = Mock(return_value=make_awaitable(True))
|
||||
self.store.create_appservice_txn = Mock(return_value=defer.succeed(txn))
|
||||
|
||||
# actual call
|
||||
self.txnctrl.send(service, events)
|
||||
self.successResultOf(defer.ensureDeferred(self.txnctrl.send(service, events)))
|
||||
|
||||
self.store.create_appservice_txn.assert_called_once_with(
|
||||
service=service, events=events # txn made and saved
|
||||
|
@ -77,7 +78,7 @@ class ApplicationServiceSchedulerTransactionCtrlTestCase(unittest.TestCase):
|
|||
self.store.create_appservice_txn = Mock(return_value=defer.succeed(txn))
|
||||
|
||||
# actual call
|
||||
self.txnctrl.send(service, events)
|
||||
self.successResultOf(defer.ensureDeferred(self.txnctrl.send(service, events)))
|
||||
|
||||
self.store.create_appservice_txn.assert_called_once_with(
|
||||
service=service, events=events # txn made and saved
|
||||
|
@ -98,11 +99,11 @@ class ApplicationServiceSchedulerTransactionCtrlTestCase(unittest.TestCase):
|
|||
return_value=defer.succeed(ApplicationServiceState.UP)
|
||||
)
|
||||
self.store.set_appservice_state = Mock(return_value=defer.succeed(True))
|
||||
txn.send = Mock(return_value=defer.succeed(False)) # fails to send
|
||||
txn.send = Mock(return_value=make_awaitable(False)) # fails to send
|
||||
self.store.create_appservice_txn = Mock(return_value=defer.succeed(txn))
|
||||
|
||||
# actual call
|
||||
self.txnctrl.send(service, events)
|
||||
self.successResultOf(defer.ensureDeferred(self.txnctrl.send(service, events)))
|
||||
|
||||
self.store.create_appservice_txn.assert_called_once_with(
|
||||
service=service, events=events
|
||||
|
@ -144,7 +145,8 @@ class ApplicationServiceSchedulerRecovererTestCase(unittest.TestCase):
|
|||
self.recoverer.recover()
|
||||
# shouldn't have called anything prior to waiting for exp backoff
|
||||
self.assertEquals(0, self.store.get_oldest_unsent_txn.call_count)
|
||||
txn.send = Mock(return_value=True)
|
||||
txn.send = Mock(return_value=make_awaitable(True))
|
||||
txn.complete.return_value = make_awaitable(None)
|
||||
# wait for exp backoff
|
||||
self.clock.advance_time(2)
|
||||
self.assertEquals(1, txn.send.call_count)
|
||||
|
@ -169,7 +171,8 @@ class ApplicationServiceSchedulerRecovererTestCase(unittest.TestCase):
|
|||
|
||||
self.recoverer.recover()
|
||||
self.assertEquals(0, self.store.get_oldest_unsent_txn.call_count)
|
||||
txn.send = Mock(return_value=False)
|
||||
txn.send = Mock(return_value=make_awaitable(False))
|
||||
txn.complete.return_value = make_awaitable(None)
|
||||
self.clock.advance_time(2)
|
||||
self.assertEquals(1, txn.send.call_count)
|
||||
self.assertEquals(0, txn.complete.call_count)
|
||||
|
@ -182,7 +185,7 @@ class ApplicationServiceSchedulerRecovererTestCase(unittest.TestCase):
|
|||
self.assertEquals(3, txn.send.call_count)
|
||||
self.assertEquals(0, txn.complete.call_count)
|
||||
self.assertEquals(0, self.callback.call_count)
|
||||
txn.send = Mock(return_value=True) # successfully send the txn
|
||||
txn.send = Mock(return_value=make_awaitable(True)) # successfully send the txn
|
||||
pop_txn = True # returns the txn the first time, then no more.
|
||||
self.clock.advance_time(16)
|
||||
self.assertEquals(1, txn.send.call_count) # new mock reset call count
|
||||
|
|
|
@ -102,11 +102,10 @@ class KeyringTestCase(unittest.HomeserverTestCase):
|
|||
}
|
||||
persp_deferred = defer.Deferred()
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def get_perspectives(**kwargs):
|
||||
async def get_perspectives(**kwargs):
|
||||
self.assertEquals(current_context().request, "11")
|
||||
with PreserveLoggingContext():
|
||||
yield persp_deferred
|
||||
await persp_deferred
|
||||
return persp_resp
|
||||
|
||||
self.http_client.post_json.side_effect = get_perspectives
|
||||
|
@ -355,7 +354,7 @@ class ServerKeyFetcherTestCase(unittest.HomeserverTestCase):
|
|||
}
|
||||
signedjson.sign.sign_json(response, SERVER_NAME, testkey)
|
||||
|
||||
def get_json(destination, path, **kwargs):
|
||||
async def get_json(destination, path, **kwargs):
|
||||
self.assertEqual(destination, SERVER_NAME)
|
||||
self.assertEqual(path, "/_matrix/key/v2/server/key1")
|
||||
return response
|
||||
|
@ -444,7 +443,7 @@ class PerspectivesKeyFetcherTestCase(unittest.HomeserverTestCase):
|
|||
Tell the mock http client to expect a perspectives-server key query
|
||||
"""
|
||||
|
||||
def post_json(destination, path, data, **kwargs):
|
||||
async def post_json(destination, path, data, **kwargs):
|
||||
self.assertEqual(destination, self.mock_perspective_server.server_name)
|
||||
self.assertEqual(path, "/_matrix/key/v2/query")
|
||||
|
||||
|
@ -580,14 +579,12 @@ class PerspectivesKeyFetcherTestCase(unittest.HomeserverTestCase):
|
|||
# remove the perspectives server's signature
|
||||
response = build_response()
|
||||
del response["signatures"][self.mock_perspective_server.server_name]
|
||||
self.http_client.post_json.return_value = {"server_keys": [response]}
|
||||
keys = get_key_from_perspectives(response)
|
||||
self.assertEqual(keys, {}, "Expected empty dict with missing persp server sig")
|
||||
|
||||
# remove the origin server's signature
|
||||
response = build_response()
|
||||
del response["signatures"][SERVER_NAME]
|
||||
self.http_client.post_json.return_value = {"server_keys": [response]}
|
||||
keys = get_key_from_perspectives(response)
|
||||
self.assertEqual(keys, {}, "Expected empty dict with missing origin server sig")
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ from synapse.rest.client.v1 import login, room
|
|||
from synapse.types import UserID
|
||||
|
||||
from tests import unittest
|
||||
from tests.test_utils import make_awaitable
|
||||
|
||||
|
||||
class RoomComplexityTests(unittest.FederatingHomeserverTestCase):
|
||||
|
@ -78,9 +79,40 @@ class RoomComplexityTests(unittest.FederatingHomeserverTestCase):
|
|||
fed_transport = self.hs.get_federation_transport_client()
|
||||
|
||||
# Mock out some things, because we don't want to test the whole join
|
||||
fed_transport.client.get_json = Mock(return_value=defer.succeed({"v1": 9999}))
|
||||
fed_transport.client.get_json = Mock(return_value=make_awaitable({"v1": 9999}))
|
||||
handler.federation_handler.do_invite_join = Mock(
|
||||
return_value=defer.succeed(("", 1))
|
||||
return_value=make_awaitable(("", 1))
|
||||
)
|
||||
|
||||
d = handler._remote_join(
|
||||
None,
|
||||
["other.example.com"],
|
||||
"roomid",
|
||||
UserID.from_string(u1),
|
||||
{"membership": "join"},
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
# The request failed with a SynapseError saying the resource limit was
|
||||
# exceeded.
|
||||
f = self.get_failure(d, SynapseError)
|
||||
self.assertEqual(f.value.code, 400, f.value)
|
||||
self.assertEqual(f.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
|
||||
|
||||
def test_join_too_large_admin(self):
|
||||
# Check whether an admin can join if option "admins_can_join" is undefined,
|
||||
# this option defaults to false, so the join should fail.
|
||||
|
||||
u1 = self.register_user("u1", "pass", admin=True)
|
||||
|
||||
handler = self.hs.get_room_member_handler()
|
||||
fed_transport = self.hs.get_federation_transport_client()
|
||||
|
||||
# Mock out some things, because we don't want to test the whole join
|
||||
fed_transport.client.get_json = Mock(return_value=make_awaitable({"v1": 9999}))
|
||||
handler.federation_handler.do_invite_join = Mock(
|
||||
return_value=make_awaitable(("", 1))
|
||||
)
|
||||
|
||||
d = handler._remote_join(
|
||||
|
@ -116,9 +148,9 @@ class RoomComplexityTests(unittest.FederatingHomeserverTestCase):
|
|||
fed_transport = self.hs.get_federation_transport_client()
|
||||
|
||||
# Mock out some things, because we don't want to test the whole join
|
||||
fed_transport.client.get_json = Mock(return_value=defer.succeed(None))
|
||||
fed_transport.client.get_json = Mock(return_value=make_awaitable(None))
|
||||
handler.federation_handler.do_invite_join = Mock(
|
||||
return_value=defer.succeed(("", 1))
|
||||
return_value=make_awaitable(("", 1))
|
||||
)
|
||||
|
||||
# Artificially raise the complexity
|
||||
|
@ -141,3 +173,81 @@ class RoomComplexityTests(unittest.FederatingHomeserverTestCase):
|
|||
f = self.get_failure(d, SynapseError)
|
||||
self.assertEqual(f.value.code, 400)
|
||||
self.assertEqual(f.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
|
||||
|
||||
|
||||
class RoomComplexityAdminTests(unittest.FederatingHomeserverTestCase):
|
||||
# Test the behavior of joining rooms which exceed the complexity if option
|
||||
# limit_remote_rooms.admins_can_join is True.
|
||||
|
||||
servlets = [
|
||||
admin.register_servlets,
|
||||
room.register_servlets,
|
||||
login.register_servlets,
|
||||
]
|
||||
|
||||
def default_config(self):
|
||||
config = super().default_config()
|
||||
config["limit_remote_rooms"] = {
|
||||
"enabled": True,
|
||||
"complexity": 0.05,
|
||||
"admins_can_join": True,
|
||||
}
|
||||
return config
|
||||
|
||||
def test_join_too_large_no_admin(self):
|
||||
# A user which is not an admin should not be able to join a remote room
|
||||
# which is too complex.
|
||||
|
||||
u1 = self.register_user("u1", "pass")
|
||||
|
||||
handler = self.hs.get_room_member_handler()
|
||||
fed_transport = self.hs.get_federation_transport_client()
|
||||
|
||||
# Mock out some things, because we don't want to test the whole join
|
||||
fed_transport.client.get_json = Mock(return_value=make_awaitable({"v1": 9999}))
|
||||
handler.federation_handler.do_invite_join = Mock(
|
||||
return_value=make_awaitable(("", 1))
|
||||
)
|
||||
|
||||
d = handler._remote_join(
|
||||
None,
|
||||
["other.example.com"],
|
||||
"roomid",
|
||||
UserID.from_string(u1),
|
||||
{"membership": "join"},
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
# The request failed with a SynapseError saying the resource limit was
|
||||
# exceeded.
|
||||
f = self.get_failure(d, SynapseError)
|
||||
self.assertEqual(f.value.code, 400, f.value)
|
||||
self.assertEqual(f.value.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
|
||||
|
||||
def test_join_too_large_admin(self):
|
||||
# An admin should be able to join rooms where a complexity check fails.
|
||||
|
||||
u1 = self.register_user("u1", "pass", admin=True)
|
||||
|
||||
handler = self.hs.get_room_member_handler()
|
||||
fed_transport = self.hs.get_federation_transport_client()
|
||||
|
||||
# Mock out some things, because we don't want to test the whole join
|
||||
fed_transport.client.get_json = Mock(return_value=make_awaitable({"v1": 9999}))
|
||||
handler.federation_handler.do_invite_join = Mock(
|
||||
return_value=make_awaitable(("", 1))
|
||||
)
|
||||
|
||||
d = handler._remote_join(
|
||||
None,
|
||||
["other.example.com"],
|
||||
"roomid",
|
||||
UserID.from_string(u1),
|
||||
{"membership": "join"},
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
# The request success since the user is an admin
|
||||
self.get_success(d)
|
||||
|
|
|
@ -47,13 +47,13 @@ class FederationSenderReceiptsTestCases(HomeserverTestCase):
|
|||
mock_send_transaction = (
|
||||
self.hs.get_federation_transport_client().send_transaction
|
||||
)
|
||||
mock_send_transaction.return_value = defer.succeed({})
|
||||
mock_send_transaction.return_value = make_awaitable({})
|
||||
|
||||
sender = self.hs.get_federation_sender()
|
||||
receipt = ReadReceipt(
|
||||
"room_id", "m.read", "user_id", ["event_id"], {"ts": 1234}
|
||||
)
|
||||
self.successResultOf(sender.send_read_receipt(receipt))
|
||||
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -87,13 +87,13 @@ class FederationSenderReceiptsTestCases(HomeserverTestCase):
|
|||
mock_send_transaction = (
|
||||
self.hs.get_federation_transport_client().send_transaction
|
||||
)
|
||||
mock_send_transaction.return_value = defer.succeed({})
|
||||
mock_send_transaction.return_value = make_awaitable({})
|
||||
|
||||
sender = self.hs.get_federation_sender()
|
||||
receipt = ReadReceipt(
|
||||
"room_id", "m.read", "user_id", ["event_id"], {"ts": 1234}
|
||||
)
|
||||
self.successResultOf(sender.send_read_receipt(receipt))
|
||||
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -125,7 +125,7 @@ class FederationSenderReceiptsTestCases(HomeserverTestCase):
|
|||
receipt = ReadReceipt(
|
||||
"room_id", "m.read", "user_id", ["other_id"], {"ts": 1234}
|
||||
)
|
||||
self.successResultOf(sender.send_read_receipt(receipt))
|
||||
self.successResultOf(defer.ensureDeferred(sender.send_read_receipt(receipt)))
|
||||
self.pump()
|
||||
mock_send_transaction.assert_not_called()
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ from twisted.internet import defer
|
|||
|
||||
from synapse.handlers.appservice import ApplicationServicesHandler
|
||||
|
||||
from tests.test_utils import make_awaitable
|
||||
from tests.utils import MockClock
|
||||
|
||||
from .. import unittest
|
||||
|
@ -117,7 +118,7 @@ class AppServiceHandlerTestCase(unittest.TestCase):
|
|||
self._mkservice_alias(is_interested_in_alias=False),
|
||||
]
|
||||
|
||||
self.mock_as_api.query_alias.return_value = defer.succeed(True)
|
||||
self.mock_as_api.query_alias.return_value = make_awaitable(True)
|
||||
self.mock_store.get_app_services.return_value = services
|
||||
self.mock_store.get_association_from_room_alias.return_value = defer.succeed(
|
||||
Mock(room_id=room_id, servers=servers)
|
||||
|
@ -135,7 +136,7 @@ class AppServiceHandlerTestCase(unittest.TestCase):
|
|||
|
||||
def _mkservice(self, is_interested):
|
||||
service = Mock()
|
||||
service.is_interested.return_value = defer.succeed(is_interested)
|
||||
service.is_interested.return_value = make_awaitable(is_interested)
|
||||
service.token = "mock_service_token"
|
||||
service.url = "mock_service_url"
|
||||
return service
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
from mock import Mock
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import synapse
|
||||
import synapse.api.errors
|
||||
from synapse.api.constants import EventTypes
|
||||
|
@ -26,6 +24,7 @@ from synapse.rest.client.v1 import directory, login, room
|
|||
from synapse.types import RoomAlias, create_requester
|
||||
|
||||
from tests import unittest
|
||||
from tests.test_utils import make_awaitable
|
||||
|
||||
|
||||
class DirectoryTestCase(unittest.HomeserverTestCase):
|
||||
|
@ -71,7 +70,7 @@ class DirectoryTestCase(unittest.HomeserverTestCase):
|
|||
self.assertEquals({"room_id": "!8765qwer:test", "servers": ["test"]}, result)
|
||||
|
||||
def test_get_remote_association(self):
|
||||
self.mock_federation.make_query.return_value = defer.succeed(
|
||||
self.mock_federation.make_query.return_value = make_awaitable(
|
||||
{"room_id": "!8765qwer:test", "servers": ["test", "remote"]}
|
||||
)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ from synapse.handlers.profile import MasterProfileHandler
|
|||
from synapse.types import UserID
|
||||
|
||||
from tests import unittest
|
||||
from tests.test_utils import make_awaitable
|
||||
from tests.utils import setup_test_homeserver
|
||||
|
||||
|
||||
|
@ -138,7 +139,7 @@ class ProfileTestCase(unittest.TestCase):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_other_name(self):
|
||||
self.mock_federation.make_query.return_value = defer.succeed(
|
||||
self.mock_federation.make_query.return_value = make_awaitable(
|
||||
{"displayname": "Alice"}
|
||||
)
|
||||
|
||||
|
|
|
@ -58,7 +58,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
@defer.inlineCallbacks
|
||||
def do_request():
|
||||
with LoggingContext("one") as context:
|
||||
fetch_d = self.cl.get_json("testserv:8008", "foo/bar")
|
||||
fetch_d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar")
|
||||
)
|
||||
|
||||
# Nothing happened yet
|
||||
self.assertNoResult(fetch_d)
|
||||
|
@ -120,7 +122,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
"""
|
||||
If the DNS lookup returns an error, it will bubble up.
|
||||
"""
|
||||
d = self.cl.get_json("testserv2:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv2:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
self.pump()
|
||||
|
||||
f = self.failureResultOf(d)
|
||||
|
@ -128,7 +132,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
self.assertIsInstance(f.value.inner_exception, DNSLookupError)
|
||||
|
||||
def test_client_connection_refused(self):
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -154,7 +160,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
If the HTTP request is not connected and is timed out, it'll give a
|
||||
ConnectingCancelledError or TimeoutError.
|
||||
"""
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -184,7 +192,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
If the HTTP request is connected, but gets no response before being
|
||||
timed out, it'll give a ResponseNeverReceived.
|
||||
"""
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -226,7 +236,7 @@ class FederationClientTests(HomeserverTestCase):
|
|||
# Try making a GET request to a blacklisted IPv4 address
|
||||
# ------------------------------------------------------
|
||||
# Make the request
|
||||
d = cl.get_json("internal:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(cl.get_json("internal:8008", "foo/bar", timeout=10000))
|
||||
|
||||
# Nothing happened yet
|
||||
self.assertNoResult(d)
|
||||
|
@ -244,7 +254,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
# Try making a POST request to a blacklisted IPv6 address
|
||||
# -------------------------------------------------------
|
||||
# Make the request
|
||||
d = cl.post_json("internalv6:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
cl.post_json("internalv6:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
|
||||
# Nothing has happened yet
|
||||
self.assertNoResult(d)
|
||||
|
@ -263,7 +275,7 @@ class FederationClientTests(HomeserverTestCase):
|
|||
# Try making a GET request to a non-blacklisted IPv4 address
|
||||
# ----------------------------------------------------------
|
||||
# Make the request
|
||||
d = cl.post_json("fine:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(cl.post_json("fine:8008", "foo/bar", timeout=10000))
|
||||
|
||||
# Nothing has happened yet
|
||||
self.assertNoResult(d)
|
||||
|
@ -286,7 +298,7 @@ class FederationClientTests(HomeserverTestCase):
|
|||
request = MatrixFederationRequest(
|
||||
method="GET", destination="testserv:8008", path="foo/bar"
|
||||
)
|
||||
d = self.cl._send_request(request, timeout=10000)
|
||||
d = defer.ensureDeferred(self.cl._send_request(request, timeout=10000))
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -310,7 +322,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
If the HTTP request is connected, but gets no response before being
|
||||
timed out, it'll give a ResponseNeverReceived.
|
||||
"""
|
||||
d = self.cl.post_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.post_json("testserv:8008", "foo/bar", timeout=10000)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -342,7 +356,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
requiring a trailing slash. We need to retry the request with a
|
||||
trailing slash. Workaround for Synapse <= v0.99.3, explained in #3622.
|
||||
"""
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar", try_trailing_slash_on_400=True)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar", try_trailing_slash_on_400=True)
|
||||
)
|
||||
|
||||
# Send the request
|
||||
self.pump()
|
||||
|
@ -395,7 +411,9 @@ class FederationClientTests(HomeserverTestCase):
|
|||
|
||||
See test_client_requires_trailing_slashes() for context.
|
||||
"""
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar", try_trailing_slash_on_400=True)
|
||||
d = defer.ensureDeferred(
|
||||
self.cl.get_json("testserv:8008", "foo/bar", try_trailing_slash_on_400=True)
|
||||
)
|
||||
|
||||
# Send the request
|
||||
self.pump()
|
||||
|
@ -432,7 +450,11 @@ class FederationClientTests(HomeserverTestCase):
|
|||
self.failureResultOf(d)
|
||||
|
||||
def test_client_sends_body(self):
|
||||
self.cl.post_json("testserv:8008", "foo/bar", timeout=10000, data={"a": "b"})
|
||||
defer.ensureDeferred(
|
||||
self.cl.post_json(
|
||||
"testserv:8008", "foo/bar", timeout=10000, data={"a": "b"}
|
||||
)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
|
||||
|
@ -453,7 +475,7 @@ class FederationClientTests(HomeserverTestCase):
|
|||
|
||||
def test_closes_connection(self):
|
||||
"""Check that the client closes unused HTTP connections"""
|
||||
d = self.cl.get_json("testserv:8008", "foo/bar")
|
||||
d = defer.ensureDeferred(self.cl.get_json("testserv:8008", "foo/bar"))
|
||||
|
||||
self.pump()
|
||||
|
||||
|
|
|
@ -366,7 +366,9 @@ class SlavedEventStoreTestCase(BaseSlavedStoreTestCase):
|
|||
state_handler = self.hs.get_state_handler()
|
||||
context = self.get_success(state_handler.compute_event_context(event))
|
||||
|
||||
self.master_store.add_push_actions_to_staging(
|
||||
event.event_id, {user_id: actions for user_id, actions in push_actions}
|
||||
self.get_success(
|
||||
self.master_store.add_push_actions_to_staging(
|
||||
event.event_id, {user_id: actions for user_id, actions in push_actions}
|
||||
)
|
||||
)
|
||||
return event, context
|
||||
|
|
|
@ -16,8 +16,6 @@ import logging
|
|||
|
||||
from mock import Mock
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.constants import EventTypes, Membership
|
||||
from synapse.events.builder import EventBuilderFactory
|
||||
from synapse.rest.admin import register_servlets_for_client_rest_resource
|
||||
|
@ -25,6 +23,7 @@ from synapse.rest.client.v1 import login, room
|
|||
from synapse.types import UserID
|
||||
|
||||
from tests.replication._base import BaseMultiWorkerStreamTestCase
|
||||
from tests.test_utils import make_awaitable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -46,7 +45,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
|
|||
new event.
|
||||
"""
|
||||
mock_client = Mock(spec=["put_json"])
|
||||
mock_client.put_json.side_effect = lambda *_, **__: defer.succeed({})
|
||||
mock_client.put_json.side_effect = lambda *_, **__: make_awaitable({})
|
||||
|
||||
self.make_worker_hs(
|
||||
"synapse.app.federation_sender",
|
||||
|
@ -74,7 +73,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
|
|||
new events.
|
||||
"""
|
||||
mock_client1 = Mock(spec=["put_json"])
|
||||
mock_client1.put_json.side_effect = lambda *_, **__: defer.succeed({})
|
||||
mock_client1.put_json.side_effect = lambda *_, **__: make_awaitable({})
|
||||
self.make_worker_hs(
|
||||
"synapse.app.federation_sender",
|
||||
{
|
||||
|
@ -86,7 +85,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
|
|||
)
|
||||
|
||||
mock_client2 = Mock(spec=["put_json"])
|
||||
mock_client2.put_json.side_effect = lambda *_, **__: defer.succeed({})
|
||||
mock_client2.put_json.side_effect = lambda *_, **__: make_awaitable({})
|
||||
self.make_worker_hs(
|
||||
"synapse.app.federation_sender",
|
||||
{
|
||||
|
@ -137,7 +136,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
|
|||
new typing EDUs.
|
||||
"""
|
||||
mock_client1 = Mock(spec=["put_json"])
|
||||
mock_client1.put_json.side_effect = lambda *_, **__: defer.succeed({})
|
||||
mock_client1.put_json.side_effect = lambda *_, **__: make_awaitable({})
|
||||
self.make_worker_hs(
|
||||
"synapse.app.federation_sender",
|
||||
{
|
||||
|
@ -149,7 +148,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
|
|||
)
|
||||
|
||||
mock_client2 = Mock(spec=["put_json"])
|
||||
mock_client2.put_json.side_effect = lambda *_, **__: defer.succeed({})
|
||||
mock_client2.put_json.side_effect = lambda *_, **__: make_awaitable({})
|
||||
self.make_worker_hs(
|
||||
"synapse.app.federation_sender",
|
||||
{
|
||||
|
|
|
@ -178,7 +178,7 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
|||
|
||||
self.fetches = []
|
||||
|
||||
def get_file(destination, path, output_stream, args=None, max_size=None):
|
||||
async def get_file(destination, path, output_stream, args=None, max_size=None):
|
||||
"""
|
||||
Returns tuple[int,dict,str,int] of file length, response headers,
|
||||
absolute URI, and response code.
|
||||
|
@ -192,7 +192,7 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
|
|||
d = Deferred()
|
||||
d.addCallback(write_to)
|
||||
self.fetches.append((d, destination, path, args))
|
||||
return make_deferred_yieldable(d)
|
||||
return await make_deferred_yieldable(d)
|
||||
|
||||
client = Mock()
|
||||
client.get_file = get_file
|
||||
|
|
|
@ -283,6 +283,23 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
|||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual(Codes.BAD_JSON, channel.json_body["errcode"])
|
||||
|
||||
def test_purge_is_not_bool(self):
|
||||
"""
|
||||
If parameter `purge` is not boolean, return an error
|
||||
"""
|
||||
body = json.dumps({"purge": "NotBool"})
|
||||
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
self.url,
|
||||
content=body.encode(encoding="utf_8"),
|
||||
access_token=self.admin_user_tok,
|
||||
)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual(Codes.BAD_JSON, channel.json_body["errcode"])
|
||||
|
||||
def test_purge_room_and_block(self):
|
||||
"""Test to purge a room and block it.
|
||||
Members will not be moved to a new room and will not receive a message.
|
||||
|
@ -297,7 +314,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
|||
# Assert one user in room
|
||||
self._is_member(room_id=self.room_id, user_id=self.other_user)
|
||||
|
||||
body = json.dumps({"block": True})
|
||||
body = json.dumps({"block": True, "purge": True})
|
||||
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
|
@ -331,7 +348,7 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
|||
# Assert one user in room
|
||||
self._is_member(room_id=self.room_id, user_id=self.other_user)
|
||||
|
||||
body = json.dumps({"block": False})
|
||||
body = json.dumps({"block": False, "purge": True})
|
||||
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
|
@ -351,6 +368,42 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
|||
self._is_blocked(self.room_id, expect=False)
|
||||
self._has_no_members(self.room_id)
|
||||
|
||||
def test_block_room_and_not_purge(self):
|
||||
"""Test to block a room without purging it.
|
||||
Members will not be moved to a new room and will not receive a message.
|
||||
The room will not be purged.
|
||||
"""
|
||||
# Test that room is not purged
|
||||
with self.assertRaises(AssertionError):
|
||||
self._is_purged(self.room_id)
|
||||
|
||||
# Test that room is not blocked
|
||||
self._is_blocked(self.room_id, expect=False)
|
||||
|
||||
# Assert one user in room
|
||||
self._is_member(room_id=self.room_id, user_id=self.other_user)
|
||||
|
||||
body = json.dumps({"block": False, "purge": False})
|
||||
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
self.url.encode("ascii"),
|
||||
content=body.encode(encoding="utf_8"),
|
||||
access_token=self.admin_user_tok,
|
||||
)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual(None, channel.json_body["new_room_id"])
|
||||
self.assertEqual(self.other_user, channel.json_body["kicked_users"][0])
|
||||
self.assertIn("failed_to_kick_users", channel.json_body)
|
||||
self.assertIn("local_aliases", channel.json_body)
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self._is_purged(self.room_id)
|
||||
self._is_blocked(self.room_id, expect=False)
|
||||
self._has_no_members(self.room_id)
|
||||
|
||||
def test_shutdown_room_consent(self):
|
||||
"""Test that we can shutdown rooms with local users who have not
|
||||
yet accepted the privacy policy. This used to fail when we tried to
|
||||
|
|
|
@ -143,6 +143,26 @@ class RestHelper(object):
|
|||
|
||||
return channel.json_body
|
||||
|
||||
def redact(self, room_id, event_id, txn_id=None, tok=None, expect_code=200):
|
||||
if txn_id is None:
|
||||
txn_id = "m%s" % (str(time.time()))
|
||||
|
||||
path = "/_matrix/client/r0/rooms/%s/redact/%s/%s" % (room_id, event_id, txn_id)
|
||||
if tok:
|
||||
path = path + "?access_token=%s" % tok
|
||||
|
||||
request, channel = make_request(
|
||||
self.hs.get_reactor(), "PUT", path, json.dumps({}).encode("utf8")
|
||||
)
|
||||
render(request, self.resource, self.hs.get_reactor())
|
||||
|
||||
assert int(channel.result["code"]) == expect_code, (
|
||||
"Expected: %d, got: %d, resp: %r"
|
||||
% (expect_code, int(channel.result["code"]), channel.result["body"])
|
||||
)
|
||||
|
||||
return channel.json_body
|
||||
|
||||
def _read_write_state(
|
||||
self,
|
||||
room_id: str,
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
import json
|
||||
|
||||
import synapse.rest.admin
|
||||
from synapse.api.constants import EventContentFields, EventTypes
|
||||
from synapse.api.constants import EventContentFields, EventTypes, RelationTypes
|
||||
from synapse.rest.client.v1 import login, room
|
||||
from synapse.rest.client.v2_alpha import sync
|
||||
from synapse.rest.client.v2_alpha import read_marker, sync
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import TimedOutException
|
||||
|
@ -324,3 +324,156 @@ class SyncTypingTests(unittest.HomeserverTestCase):
|
|||
"GET", sync_url % (access_token, next_batch)
|
||||
)
|
||||
self.assertRaises(TimedOutException, self.render, request)
|
||||
|
||||
|
||||
class UnreadMessagesTestCase(unittest.HomeserverTestCase):
|
||||
servlets = [
|
||||
synapse.rest.admin.register_servlets,
|
||||
login.register_servlets,
|
||||
read_marker.register_servlets,
|
||||
room.register_servlets,
|
||||
sync.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
self.url = "/sync?since=%s"
|
||||
self.next_batch = "s0"
|
||||
|
||||
# Register the first user (used to check the unread counts).
|
||||
self.user_id = self.register_user("kermit", "monkey")
|
||||
self.tok = self.login("kermit", "monkey")
|
||||
|
||||
# Create the room we'll check unread counts for.
|
||||
self.room_id = self.helper.create_room_as(self.user_id, tok=self.tok)
|
||||
|
||||
# Register the second user (used to send events to the room).
|
||||
self.user2 = self.register_user("kermit2", "monkey")
|
||||
self.tok2 = self.login("kermit2", "monkey")
|
||||
|
||||
# Change the power levels of the room so that the second user can send state
|
||||
# events.
|
||||
self.helper.send_state(
|
||||
self.room_id,
|
||||
EventTypes.PowerLevels,
|
||||
{
|
||||
"users": {self.user_id: 100, self.user2: 100},
|
||||
"users_default": 0,
|
||||
"events": {
|
||||
"m.room.name": 50,
|
||||
"m.room.power_levels": 100,
|
||||
"m.room.history_visibility": 100,
|
||||
"m.room.canonical_alias": 50,
|
||||
"m.room.avatar": 50,
|
||||
"m.room.tombstone": 100,
|
||||
"m.room.server_acl": 100,
|
||||
"m.room.encryption": 100,
|
||||
},
|
||||
"events_default": 0,
|
||||
"state_default": 50,
|
||||
"ban": 50,
|
||||
"kick": 50,
|
||||
"redact": 50,
|
||||
"invite": 0,
|
||||
},
|
||||
tok=self.tok,
|
||||
)
|
||||
|
||||
def test_unread_counts(self):
|
||||
"""Tests that /sync returns the right value for the unread count (MSC2654)."""
|
||||
|
||||
# Check that our own messages don't increase the unread count.
|
||||
self.helper.send(self.room_id, "hello", tok=self.tok)
|
||||
self._check_unread_count(0)
|
||||
|
||||
# Join the new user and check that this doesn't increase the unread count.
|
||||
self.helper.join(room=self.room_id, user=self.user2, tok=self.tok2)
|
||||
self._check_unread_count(0)
|
||||
|
||||
# Check that the new user sending a message increases our unread count.
|
||||
res = self.helper.send(self.room_id, "hello", tok=self.tok2)
|
||||
self._check_unread_count(1)
|
||||
|
||||
# Send a read receipt to tell the server we've read the latest event.
|
||||
body = json.dumps({"m.read": res["event_id"]}).encode("utf8")
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
"/rooms/%s/read_markers" % self.room_id,
|
||||
body,
|
||||
access_token=self.tok,
|
||||
)
|
||||
self.render(request)
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
# Check that the unread counter is back to 0.
|
||||
self._check_unread_count(0)
|
||||
|
||||
# Check that room name changes increase the unread counter.
|
||||
self.helper.send_state(
|
||||
self.room_id, "m.room.name", {"name": "my super room"}, tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(1)
|
||||
|
||||
# Check that room topic changes increase the unread counter.
|
||||
self.helper.send_state(
|
||||
self.room_id, "m.room.topic", {"topic": "welcome!!!"}, tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(2)
|
||||
|
||||
# Check that encrypted messages increase the unread counter.
|
||||
self.helper.send_event(self.room_id, EventTypes.Encrypted, {}, tok=self.tok2)
|
||||
self._check_unread_count(3)
|
||||
|
||||
# Check that custom events with a body increase the unread counter.
|
||||
self.helper.send_event(
|
||||
self.room_id, "org.matrix.custom_type", {"body": "hello"}, tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(4)
|
||||
|
||||
# Check that edits don't increase the unread counter.
|
||||
self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={
|
||||
"body": "hello",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {"rel_type": RelationTypes.REPLACE},
|
||||
},
|
||||
tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(4)
|
||||
|
||||
# Check that notices don't increase the unread counter.
|
||||
self.helper.send_event(
|
||||
room_id=self.room_id,
|
||||
type=EventTypes.Message,
|
||||
content={"body": "hello", "msgtype": "m.notice"},
|
||||
tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(4)
|
||||
|
||||
# Check that tombstone events changes increase the unread counter.
|
||||
self.helper.send_state(
|
||||
self.room_id,
|
||||
EventTypes.Tombstone,
|
||||
{"replacement_room": "!someroom:test"},
|
||||
tok=self.tok2,
|
||||
)
|
||||
self._check_unread_count(5)
|
||||
|
||||
def _check_unread_count(self, expected_count: True):
|
||||
"""Syncs and compares the unread count with the expected value."""
|
||||
|
||||
request, channel = self.make_request(
|
||||
"GET", self.url % self.next_batch, access_token=self.tok,
|
||||
)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.code, 200, channel.json_body)
|
||||
|
||||
room_entry = channel.json_body["rooms"]["join"][self.room_id]
|
||||
self.assertEqual(
|
||||
room_entry["org.matrix.msc2654.unread_count"], expected_count, room_entry,
|
||||
)
|
||||
|
||||
# Store the next batch for the next request.
|
||||
self.next_batch = channel.json_body["next_batch"]
|
||||
|
|
|
@ -53,7 +53,7 @@ class BaseRemoteKeyResourceTestCase(unittest.HomeserverTestCase):
|
|||
Tell the mock http client to expect an outgoing GET request for the given key
|
||||
"""
|
||||
|
||||
def get_json(destination, path, ignore_backoff=False, **kwargs):
|
||||
async def get_json(destination, path, ignore_backoff=False, **kwargs):
|
||||
self.assertTrue(ignore_backoff)
|
||||
self.assertEqual(destination, server_name)
|
||||
key_id = "%s:%s" % (signing_key.alg, signing_key.version)
|
||||
|
@ -177,7 +177,7 @@ class EndToEndPerspectivesTests(BaseRemoteKeyResourceTestCase):
|
|||
|
||||
# wire up outbound POST /key/v2/query requests from hs2 so that they
|
||||
# will be forwarded to hs1
|
||||
def post_json(destination, path, data):
|
||||
async def post_json(destination, path, data):
|
||||
self.assertEqual(destination, self.hs.hostname)
|
||||
self.assertEqual(
|
||||
path, "/_matrix/key/v2/query",
|
||||
|
|
|
@ -39,14 +39,18 @@ class EventPushActionsStoreTestCase(tests.unittest.TestCase):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_unread_push_actions_for_user_in_range_for_http(self):
|
||||
yield self.store.get_unread_push_actions_for_user_in_range_for_http(
|
||||
USER_ID, 0, 1000, 20
|
||||
yield defer.ensureDeferred(
|
||||
self.store.get_unread_push_actions_for_user_in_range_for_http(
|
||||
USER_ID, 0, 1000, 20
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_unread_push_actions_for_user_in_range_for_email(self):
|
||||
yield self.store.get_unread_push_actions_for_user_in_range_for_email(
|
||||
USER_ID, 0, 1000, 20
|
||||
yield defer.ensureDeferred(
|
||||
self.store.get_unread_push_actions_for_user_in_range_for_email(
|
||||
USER_ID, 0, 1000, 20
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -72,8 +76,10 @@ class EventPushActionsStoreTestCase(tests.unittest.TestCase):
|
|||
event.internal_metadata.stream_ordering = stream
|
||||
event.depth = stream
|
||||
|
||||
yield self.store.add_push_actions_to_staging(
|
||||
event.event_id, {user_id: action}
|
||||
yield defer.ensureDeferred(
|
||||
self.store.add_push_actions_to_staging(
|
||||
event.event_id, {user_id: action}
|
||||
)
|
||||
)
|
||||
yield self.store.db.runInteraction(
|
||||
"",
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.rest.client.v1 import room
|
||||
|
||||
from tests.unittest import HomeserverTestCase
|
||||
|
@ -49,7 +51,9 @@ class PurgeTests(HomeserverTestCase):
|
|||
event = self.successResultOf(event)
|
||||
|
||||
# Purge everything before this topological token
|
||||
purge = storage.purge_events.purge_history(self.room_id, event, True)
|
||||
purge = defer.ensureDeferred(
|
||||
storage.purge_events.purge_history(self.room_id, event, True)
|
||||
)
|
||||
self.pump()
|
||||
self.assertEqual(self.successResultOf(purge), None)
|
||||
|
||||
|
@ -88,7 +92,7 @@ class PurgeTests(HomeserverTestCase):
|
|||
)
|
||||
|
||||
# Purge everything before this topological token
|
||||
purge = storage.purge_history(self.room_id, event, True)
|
||||
purge = defer.ensureDeferred(storage.purge_history(self.room_id, event, True))
|
||||
self.pump()
|
||||
f = self.failureResultOf(purge)
|
||||
self.assertIn("greater than forward", f.value.args[0])
|
||||
|
|
|
@ -237,7 +237,9 @@ class RedactionTestCase(unittest.HomeserverTestCase):
|
|||
|
||||
@defer.inlineCallbacks
|
||||
def build(self, prev_event_ids):
|
||||
built_event = yield self._base_builder.build(prev_event_ids)
|
||||
built_event = yield defer.ensureDeferred(
|
||||
self._base_builder.build(prev_event_ids)
|
||||
)
|
||||
|
||||
built_event._event_id = self._event_id
|
||||
built_event._dict["event_id"] = self._event_id
|
||||
|
|
|
@ -37,11 +37,13 @@ class RoomStoreTestCase(unittest.TestCase):
|
|||
self.alias = RoomAlias.from_string("#a-room-name:test")
|
||||
self.u_creator = UserID.from_string("@creator:test")
|
||||
|
||||
yield self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id=self.u_creator.to_string(),
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
yield defer.ensureDeferred(
|
||||
self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id=self.u_creator.to_string(),
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -88,17 +90,21 @@ class RoomEventsStoreTestCase(unittest.TestCase):
|
|||
|
||||
self.room = RoomID.from_string("!abcde:test")
|
||||
|
||||
yield self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id="@creator:text",
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
yield defer.ensureDeferred(
|
||||
self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id="@creator:text",
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def inject_room_event(self, **kwargs):
|
||||
yield self.storage.persistence.persist_event(
|
||||
self.event_factory.create_event(room_id=self.room.to_string(), **kwargs)
|
||||
yield defer.ensureDeferred(
|
||||
self.storage.persistence.persist_event(
|
||||
self.event_factory.create_event(room_id=self.room.to_string(), **kwargs)
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
|
@ -44,11 +44,13 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
|
||||
self.room = RoomID.from_string("!abc123:test")
|
||||
|
||||
yield self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id="@creator:text",
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
yield defer.ensureDeferred(
|
||||
self.store.store_room(
|
||||
self.room.to_string(),
|
||||
room_creator_user_id="@creator:text",
|
||||
is_public=True,
|
||||
room_version=RoomVersions.V1,
|
||||
)
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -68,7 +70,9 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
self.event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
|
||||
yield self.storage.persistence.persist_event(event, context)
|
||||
yield defer.ensureDeferred(
|
||||
self.storage.persistence.persist_event(event, context)
|
||||
)
|
||||
|
||||
return event
|
||||
|
||||
|
@ -87,8 +91,8 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
self.room, self.u_alice, EventTypes.Name, "", {"name": "test room"}
|
||||
)
|
||||
|
||||
state_group_map = yield self.storage.state.get_state_groups_ids(
|
||||
self.room, [e2.event_id]
|
||||
state_group_map = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_groups_ids(self.room, [e2.event_id])
|
||||
)
|
||||
self.assertEqual(len(state_group_map), 1)
|
||||
state_map = list(state_group_map.values())[0]
|
||||
|
@ -106,8 +110,8 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
self.room, self.u_alice, EventTypes.Name, "", {"name": "test room"}
|
||||
)
|
||||
|
||||
state_group_map = yield self.storage.state.get_state_groups(
|
||||
self.room, [e2.event_id]
|
||||
state_group_map = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_groups(self.room, [e2.event_id])
|
||||
)
|
||||
self.assertEqual(len(state_group_map), 1)
|
||||
state_list = list(state_group_map.values())[0]
|
||||
|
@ -148,7 +152,9 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
)
|
||||
|
||||
# check we get the full state as of the final event
|
||||
state = yield self.storage.state.get_state_for_event(e5.event_id)
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(e5.event_id)
|
||||
)
|
||||
|
||||
self.assertIsNotNone(e4)
|
||||
|
||||
|
@ -164,22 +170,28 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
)
|
||||
|
||||
# check we can filter to the m.room.name event (with a '' state key)
|
||||
state = yield self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Name, "")])
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Name, "")])
|
||||
)
|
||||
)
|
||||
|
||||
self.assertStateMapEqual({(e2.type, e2.state_key): e2}, state)
|
||||
|
||||
# check we can filter to the m.room.name event (with a wildcard None state key)
|
||||
state = yield self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Name, None)])
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Name, None)])
|
||||
)
|
||||
)
|
||||
|
||||
self.assertStateMapEqual({(e2.type, e2.state_key): e2}, state)
|
||||
|
||||
# check we can grab the m.room.member events (with a wildcard None state key)
|
||||
state = yield self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Member, None)])
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(
|
||||
e5.event_id, StateFilter.from_types([(EventTypes.Member, None)])
|
||||
)
|
||||
)
|
||||
|
||||
self.assertStateMapEqual(
|
||||
|
@ -188,12 +200,14 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
|
||||
# check we can grab a specific room member without filtering out the
|
||||
# other event types
|
||||
state = yield self.storage.state.get_state_for_event(
|
||||
e5.event_id,
|
||||
state_filter=StateFilter(
|
||||
types={EventTypes.Member: {self.u_alice.to_string()}},
|
||||
include_others=True,
|
||||
),
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(
|
||||
e5.event_id,
|
||||
state_filter=StateFilter(
|
||||
types={EventTypes.Member: {self.u_alice.to_string()}},
|
||||
include_others=True,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertStateMapEqual(
|
||||
|
@ -206,11 +220,13 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
)
|
||||
|
||||
# check that we can grab everything except members
|
||||
state = yield self.storage.state.get_state_for_event(
|
||||
e5.event_id,
|
||||
state_filter=StateFilter(
|
||||
types={EventTypes.Member: set()}, include_others=True
|
||||
),
|
||||
state = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_for_event(
|
||||
e5.event_id,
|
||||
state_filter=StateFilter(
|
||||
types={EventTypes.Member: set()}, include_others=True
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertStateMapEqual(
|
||||
|
@ -222,8 +238,8 @@ class StateStoreTestCase(tests.unittest.TestCase):
|
|||
#######################################################
|
||||
|
||||
room_id = self.room.to_string()
|
||||
group_ids = yield self.storage.state.get_state_groups_ids(
|
||||
room_id, [e5.event_id]
|
||||
group_ids = yield defer.ensureDeferred(
|
||||
self.storage.state.get_state_groups_ids(room_id, [e5.event_id])
|
||||
)
|
||||
group = list(group_ids.keys())[0]
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class MessageAcceptTests(unittest.HomeserverTestCase):
|
|||
prev_events that said event references.
|
||||
"""
|
||||
|
||||
def post_json(destination, path, data, headers=None, timeout=0):
|
||||
async def post_json(destination, path, data, headers=None, timeout=0):
|
||||
# If it asks us for new missing events, give them NOTHING
|
||||
if path.startswith("/_matrix/federation/v1/get_missing_events/"):
|
||||
return {"events": []}
|
||||
|
|
|
@ -213,7 +213,7 @@ class StateTestCase(unittest.TestCase):
|
|||
ctx_c = context_store["C"]
|
||||
ctx_d = context_store["D"]
|
||||
|
||||
prev_state_ids = yield ctx_d.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(ctx_d.get_prev_state_ids())
|
||||
self.assertEqual(2, len(prev_state_ids))
|
||||
|
||||
self.assertEqual(ctx_c.state_group, ctx_d.state_group_before_event)
|
||||
|
@ -259,7 +259,7 @@ class StateTestCase(unittest.TestCase):
|
|||
ctx_c = context_store["C"]
|
||||
ctx_d = context_store["D"]
|
||||
|
||||
prev_state_ids = yield ctx_d.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(ctx_d.get_prev_state_ids())
|
||||
self.assertSetEqual({"START", "A", "C"}, set(prev_state_ids.values()))
|
||||
|
||||
self.assertEqual(ctx_c.state_group, ctx_d.state_group_before_event)
|
||||
|
@ -318,7 +318,7 @@ class StateTestCase(unittest.TestCase):
|
|||
ctx_c = context_store["C"]
|
||||
ctx_e = context_store["E"]
|
||||
|
||||
prev_state_ids = yield ctx_e.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(ctx_e.get_prev_state_ids())
|
||||
self.assertSetEqual({"START", "A", "B", "C"}, set(prev_state_ids.values()))
|
||||
self.assertEqual(ctx_c.state_group, ctx_e.state_group_before_event)
|
||||
self.assertEqual(ctx_e.state_group_before_event, ctx_e.state_group)
|
||||
|
@ -393,7 +393,7 @@ class StateTestCase(unittest.TestCase):
|
|||
ctx_b = context_store["B"]
|
||||
ctx_d = context_store["D"]
|
||||
|
||||
prev_state_ids = yield ctx_d.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(ctx_d.get_prev_state_ids())
|
||||
self.assertSetEqual({"A1", "A2", "A3", "A5", "B"}, set(prev_state_ids.values()))
|
||||
|
||||
self.assertEqual(ctx_b.state_group, ctx_d.state_group_before_event)
|
||||
|
@ -425,7 +425,7 @@ class StateTestCase(unittest.TestCase):
|
|||
self.state.compute_event_context(event, old_state=old_state)
|
||||
)
|
||||
|
||||
prev_state_ids = yield context.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(context.get_prev_state_ids())
|
||||
self.assertCountEqual((e.event_id for e in old_state), prev_state_ids.values())
|
||||
|
||||
current_state_ids = yield defer.ensureDeferred(context.get_current_state_ids())
|
||||
|
@ -450,7 +450,7 @@ class StateTestCase(unittest.TestCase):
|
|||
self.state.compute_event_context(event, old_state=old_state)
|
||||
)
|
||||
|
||||
prev_state_ids = yield context.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(context.get_prev_state_ids())
|
||||
self.assertCountEqual((e.event_id for e in old_state), prev_state_ids.values())
|
||||
|
||||
current_state_ids = yield defer.ensureDeferred(context.get_current_state_ids())
|
||||
|
@ -519,7 +519,7 @@ class StateTestCase(unittest.TestCase):
|
|||
|
||||
context = yield defer.ensureDeferred(self.state.compute_event_context(event))
|
||||
|
||||
prev_state_ids = yield context.get_prev_state_ids()
|
||||
prev_state_ids = yield defer.ensureDeferred(context.get_prev_state_ids())
|
||||
|
||||
self.assertEqual({e.event_id for e in old_state}, set(prev_state_ids.values()))
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
self.store = self.hs.get_datastore()
|
||||
self.storage = self.hs.get_storage()
|
||||
|
||||
yield create_room(self.hs, TEST_ROOM_ID, "@someone:ROOM")
|
||||
yield defer.ensureDeferred(create_room(self.hs, TEST_ROOM_ID, "@someone:ROOM"))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_filtering(self):
|
||||
|
@ -64,8 +64,8 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
evt = yield self.inject_room_member(user, extra_content={"a": "b"})
|
||||
events_to_filter.append(evt)
|
||||
|
||||
filtered = yield filter_events_for_server(
|
||||
self.storage, "test_server", events_to_filter
|
||||
filtered = yield defer.ensureDeferred(
|
||||
filter_events_for_server(self.storage, "test_server", events_to_filter)
|
||||
)
|
||||
|
||||
# the result should be 5 redacted events, and 5 unredacted events.
|
||||
|
@ -102,8 +102,8 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
yield self.hs.get_datastore().mark_user_erased("@erased:local_hs")
|
||||
|
||||
# ... and the filtering happens.
|
||||
filtered = yield filter_events_for_server(
|
||||
self.storage, "test_server", events_to_filter
|
||||
filtered = yield defer.ensureDeferred(
|
||||
filter_events_for_server(self.storage, "test_server", events_to_filter)
|
||||
)
|
||||
|
||||
for i in range(0, len(events_to_filter)):
|
||||
|
@ -140,7 +140,9 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
event, context = yield defer.ensureDeferred(
|
||||
self.event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
yield self.storage.persistence.persist_event(event, context)
|
||||
yield defer.ensureDeferred(
|
||||
self.storage.persistence.persist_event(event, context)
|
||||
)
|
||||
return event
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -162,7 +164,9 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
self.event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
|
||||
yield self.storage.persistence.persist_event(event, context)
|
||||
yield defer.ensureDeferred(
|
||||
self.storage.persistence.persist_event(event, context)
|
||||
)
|
||||
return event
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -183,7 +187,9 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
self.event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
|
||||
yield self.storage.persistence.persist_event(event, context)
|
||||
yield defer.ensureDeferred(
|
||||
self.storage.persistence.persist_event(event, context)
|
||||
)
|
||||
return event
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -265,8 +271,8 @@ class FilterEventsForServerTestCase(tests.unittest.TestCase):
|
|||
storage.main = test_store
|
||||
storage.state = test_store
|
||||
|
||||
filtered = yield filter_events_for_server(
|
||||
test_store, "test_server", events_to_filter
|
||||
filtered = yield defer.ensureDeferred(
|
||||
filter_events_for_server(test_store, "test_server", events_to_filter)
|
||||
)
|
||||
logger.info("Filtering took %f seconds", time.time() - start)
|
||||
|
||||
|
|
|
@ -642,14 +642,8 @@ class DeferredMockCallable(object):
|
|||
)
|
||||
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def create_room(hs, room_id, creator_id):
|
||||
async def create_room(hs, room_id: str, creator_id: str):
|
||||
"""Creates and persist a creation event for the given room
|
||||
|
||||
Args:
|
||||
hs
|
||||
room_id (str)
|
||||
creator_id (str)
|
||||
"""
|
||||
|
||||
persistence_store = hs.get_storage().persistence
|
||||
|
@ -657,7 +651,7 @@ def create_room(hs, room_id, creator_id):
|
|||
event_builder_factory = hs.get_event_builder_factory()
|
||||
event_creation_handler = hs.get_event_creation_handler()
|
||||
|
||||
yield store.store_room(
|
||||
await store.store_room(
|
||||
room_id=room_id,
|
||||
room_creator_user_id=creator_id,
|
||||
is_public=False,
|
||||
|
@ -675,8 +669,6 @@ def create_room(hs, room_id, creator_id):
|
|||
},
|
||||
)
|
||||
|
||||
event, context = yield defer.ensureDeferred(
|
||||
event_creation_handler.create_new_client_event(builder)
|
||||
)
|
||||
event, context = await event_creation_handler.create_new_client_event(builder)
|
||||
|
||||
yield persistence_store.persist_event(event, context)
|
||||
await persistence_store.persist_event(event, context)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue