From 463d95fc869ef5abcf673eb4f573886bbd6038eb Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 15:25:10 +0200 Subject: [PATCH 01/28] Add test for room version 1 and 2 --- tests/test_manage_last_admin-room-v1-v2.py | 206 +++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 tests/test_manage_last_admin-room-v1-v2.py diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py new file mode 100644 index 0000000..28ed616 --- /dev/null +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +# 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. + +import copy + +# From Python 3.8 onwards, aiounittest.AsyncTestCase can be replaced by +# unittest.IsolatedAsyncioTestCase, so we'll be able to get rid of this dependency when +# we stop supporting Python < 3.8 in Synapse. +import aiounittest +from synapse.api.room_versions import RoomVersions +from synapse.events import FrozenEvent + +from manage_last_admin import EventTypes, Membership, ManageLastAdmin +from tests import create_module + + +class ManageLastAdminTest(aiounittest.AsyncTestCase): + def setUp(self): + self.user_id = "@alice:example.com" + self.left_user_id = "@nothere:example.com" + self.mod_user_id = "@mod:example.com" + self.room_id = "!someroom:example.com" + self.state = { + (EventTypes.PowerLevels, ""): FrozenEvent( + { + "sender": self.user_id, + "type": EventTypes.PowerLevels, + "state_key": "", + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.encryption": 100, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100, + "m.room.server_acl": 100, + "m.room.tombstone": 100, + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + self.user_id: 100, + self.left_user_id: 75, + self.mod_user_id: 50, + }, + "users_default": 0 + }, + "room_id": self.room_id, + }, + RoomVersions.V1, + ), + (EventTypes.JoinRules, ""): FrozenEvent( + { + "sender": self.user_id, + "type": EventTypes.JoinRules, + "state_key": "", + "content": {"join_rule": "public"}, + "room_id": self.room_id, + }, + RoomVersions.V1, + ), + (EventTypes.Member, self.mod_user_id): FrozenEvent( + { + "sender": self.mod_user_id, + "type": EventTypes.Member, + "state_key": self.mod_user_id, + "content": {"membership": Membership.JOIN}, + "room_id": self.room_id, + }, + RoomVersions.V1, + ), + (EventTypes.Member, self.left_user_id): FrozenEvent( + { + "sender": self.left_user_id, + "type": EventTypes.Member, + "state_key": self.left_user_id, + "content": {"membership": Membership.LEAVE}, + "room_id": self.room_id, + }, + RoomVersions.V1, + ), + } + + async def test_power_levels_sent_when_last_admin_leaves(self): + """Tests that the module sends the right power levels update when it sees its last admin leave.""" + module = create_module() + + leave_event = FrozenEvent( + { + "sender": self.user_id, + "type": EventTypes.Member, + "content": {"membership": Membership.LEAVE}, + "room_id": self.room_id, + "state_key": self.user_id, + }, + RoomVersions.V1, + ) + + allowed, replacement = await module.check_event_allowed(leave_event, self.state) + self.assertTrue(allowed) + self.assertEqual(replacement, None) + + # Test that the leave triggered a freeze of the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) + + pl_event_dict = args[0] + + self.assertEqual(pl_event_dict["content"]["users_default"], 100) + for user, pl in pl_event_dict["content"]["users"].items(): + self.assertEqual(pl, 100, user) + + async def test_promote_when_last_admin_leaves(self): + """Tests that the module promotes whoever has the highest non-default PL to admin + when the last admin leaves, if the config allows it. + """ + # Set the config flag to allow promoting custom PLs before freezing the room. + module = create_module(config_override={"promote_moderators": True}) + + # Make the last admin leave. + leave_event = FrozenEvent( + { + "sender": self.user_id, + "type": EventTypes.Member, + "content": {"membership": Membership.LEAVE}, + "room_id": self.room_id, + "state_key": self.user_id, + }, + RoomVersions.V1, + ) + + # Check that we get the right result back from the callback. + allowed, replacement = await module.check_event_allowed(leave_event, self.state) + self.assertTrue(allowed) + self.assertEqual(replacement, None) + + # Test that a new event was sent into the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) + + # Test that: + # * the event is a power levels update + # * the user who is PL 75 but left the room didn't get promoted + # * the user who was PL 50 and is still in the room got promoted + evt_dict: dict = args[0] + self.assertEqual(evt_dict["type"], EventTypes.PowerLevels, evt_dict) + self.assertIsNotNone(evt_dict.get("state_key")) + self.assertEqual(evt_dict["content"]["users"][self.left_user_id], 75, evt_dict) + self.assertEqual(evt_dict["content"]["users"][self.mod_user_id], 100, evt_dict) + + # Now we push both the leave event and the power levels update into the state of + # the room. + self.state[(EventTypes.Member, self.user_id)] = leave_event + self.state[(EventTypes.PowerLevels, "")] = FrozenEvent( + evt_dict, RoomVersions.V1, + ) + + # Make the mod (newly admin) leave the room. + new_leave_event = FrozenEvent( + { + "sender": self.mod_user_id, + "type": EventTypes.Member, + "content": {"membership": Membership.LEAVE}, + "room_id": self.room_id, + "state_key": self.mod_user_id, + }, + RoomVersions.V1, + ) + + # Check that we get the right result back from the callback. + allowed, replacement = await module.check_event_allowed( + new_leave_event, self.state, + ) + self.assertTrue(allowed) + self.assertEqual(replacement, None) + + # Test that a new event was sent into the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) + + ## Test that now that there's no user to promote anymore, the room default user level is 100. + pl_event_dict = args[0] + + self.assertEqual(pl_event_dict["content"]["users_default"], 100) + for user, pl in pl_event_dict["content"]["users"].items(): + self.assertEqual(pl, 100, user) From 0f4f404a1959c05bcc7cb56f2d1e439c8a80ddf5 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 15:37:56 +0200 Subject: [PATCH 02/28] Modify class --- tests/test_manage_last_admin-room-v1-v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 28ed616..2ff6ae7 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -26,7 +26,7 @@ from tests import create_module -class ManageLastAdminTest(aiounittest.AsyncTestCase): +class ManageLastAdminTestRoomV1(aiounittest.AsyncTestCase): def setUp(self): self.user_id = "@alice:example.com" self.left_user_id = "@nothere:example.com" From 590c2162f6fb604dec4c6909eaf9af8756806b9b Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 16:07:04 +0200 Subject: [PATCH 03/28] test --- tests/test_manage_last_admin-room-v1-v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 2ff6ae7..4fb383e 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -76,7 +76,7 @@ def setUp(self): }, RoomVersions.V1, ), - (EventTypes.Member, self.mod_user_id): FrozenEvent( + (EventTypes.Member, self.mod_user_id): FrozenEventV3( { "sender": self.mod_user_id, "type": EventTypes.Member, From a8c15d9e07c681ae2e8ceaa766a58cc084280757 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 16:09:35 +0200 Subject: [PATCH 04/28] test --- tests/test_manage_last_admin-room-v1-v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 4fb383e..2ff6ae7 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -76,7 +76,7 @@ def setUp(self): }, RoomVersions.V1, ), - (EventTypes.Member, self.mod_user_id): FrozenEventV3( + (EventTypes.Member, self.mod_user_id): FrozenEvent( { "sender": self.mod_user_id, "type": EventTypes.Member, From 588b917af1e3dfa010593d252ce878c9444e66c3 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 16:26:41 +0200 Subject: [PATCH 05/28] test --- tests/test_manage_last_admin-room-v1-v2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 2ff6ae7..b2ff91f 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -98,7 +98,7 @@ def setUp(self): ), } - async def test_power_levels_sent_when_last_admin_leaves(self): + async def test_power_levels_sent_when_last_admin_leaves_roomv1(self): """Tests that the module sends the right power levels update when it sees its last admin leave.""" module = create_module() @@ -128,7 +128,7 @@ async def test_power_levels_sent_when_last_admin_leaves(self): for user, pl in pl_event_dict["content"]["users"].items(): self.assertEqual(pl, 100, user) - async def test_promote_when_last_admin_leaves(self): + async def test_promote_when_last_admin_leaves_roomv1(self): """Tests that the module promotes whoever has the highest non-default PL to admin when the last admin leaves, if the config allows it. """ From 079440a5ca4f5e203877754dc030ed30d46a74b1 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 16:39:43 +0200 Subject: [PATCH 06/28] verbose --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b50d52b..b1f86c2 100644 --- a/tox.ini +++ b/tox.ini @@ -7,4 +7,4 @@ deps = aiounittest>=1.4.0 commands = - python -m unittest discover + python -v -m unittest discover From 716992d5eb4606a6433a532f4f64fc54b6c04231 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Mon, 26 Jun 2023 17:08:20 +0200 Subject: [PATCH 07/28] test specify path --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index b1f86c2..c811617 100644 --- a/tox.ini +++ b/tox.ini @@ -8,3 +8,4 @@ deps = commands = python -v -m unittest discover + python -m unittest tests/test_manage_last_admin-room-v1-v2.py From 171bdb6daa86e69ffa4488b1b5e05e6d55451fb5 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Tue, 27 Jun 2023 08:40:20 +0200 Subject: [PATCH 08/28] test specify path --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c811617..4603d50 100644 --- a/tox.ini +++ b/tox.ini @@ -7,5 +7,5 @@ deps = aiounittest>=1.4.0 commands = - python -v -m unittest discover + python -m unittest tests/test_manage_last_admin-room.py python -m unittest tests/test_manage_last_admin-room-v1-v2.py From 7f01d6eb519afafd40f644f90e990914eadfda21 Mon Sep 17 00:00:00 2001 From: Nicolas FRIES Date: Tue, 27 Jun 2023 08:42:02 +0200 Subject: [PATCH 09/28] typo --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4603d50..6139a58 100644 --- a/tox.ini +++ b/tox.ini @@ -7,5 +7,5 @@ deps = aiounittest>=1.4.0 commands = - python -m unittest tests/test_manage_last_admin-room.py + python -m unittest tests/test_manage_last_admin.py python -m unittest tests/test_manage_last_admin-room-v1-v2.py From 4200c7941aac9014ec7c2ee413d62735b8471c71 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:16:12 +0200 Subject: [PATCH 10/28] Add event_id --- manage_last_admin/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index ea4d4ee..ff95ed2 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -154,6 +154,7 @@ async def _on_room_leave( "type": EventTypes.PowerLevels, "content": power_levels_content, "state_key": "", + _maybe_get_event_id_dict_for_room_version(event.room_version), } ) @@ -188,9 +189,18 @@ async def _promote_to_admins( "type": EventTypes.PowerLevels, "content": new_pl_content, "state_key": "", + _maybe_get_event_id_dict_for_room_version(event.room_version), } ) + def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion) -> dict: + """If this room version needs it, generate an event id""" + if room_version.event_format != EventFormatVersions.ROOM_V1_V2: + return {} + + randomString = random_string(43) + return {"event_id": "!%i:example.com" % (randomString,)} # TODO : replace example.com with homeserver + def _is_local_user(self, user_id: str) -> bool: """Checks whether a given user ID belongs to this homeserver, or a remote From 1e7b6ab643932dc37c1f60f6e187761a74262b07 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:20:24 +0200 Subject: [PATCH 11/28] Fix pydantic version --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 6139a58..a094a9f 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = tests [testenv:tests] deps = + pydantic>=1.7.4 matrix-synapse>= 1.39.0, < 1.81.0 aiounittest>=1.4.0 From bc0225917028c6d50bc0050e5099ce53c5509054 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:23:23 +0200 Subject: [PATCH 12/28] Fix pydantic --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a094a9f..4d2e2d8 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = tests [testenv:tests] deps = - pydantic>=1.7.4 + pydantic>=1.7.4, <2.0 matrix-synapse>= 1.39.0, < 1.81.0 aiounittest>=1.4.0 From b8dea294518d2b39b99bd122507a073c3565bf45 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:25:02 +0200 Subject: [PATCH 13/28] Fix syntax --- manage_last_admin/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index ff95ed2..ce0777b 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -154,7 +154,7 @@ async def _on_room_leave( "type": EventTypes.PowerLevels, "content": power_levels_content, "state_key": "", - _maybe_get_event_id_dict_for_room_version(event.room_version), + **_maybe_get_event_id_dict_for_room_version(event.room_version), } ) @@ -189,7 +189,7 @@ async def _promote_to_admins( "type": EventTypes.PowerLevels, "content": new_pl_content, "state_key": "", - _maybe_get_event_id_dict_for_room_version(event.room_version), + **_maybe_get_event_id_dict_for_room_version(event.room_version), } ) From 916321c18545c90c77eb5af8e0f4c1374bcf6035 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:27:11 +0200 Subject: [PATCH 14/28] Add import --- manage_last_admin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index ce0777b..aac0e6f 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -21,6 +21,8 @@ from synapse.events import EventBase from synapse.module_api import ModuleApi, UserID from synapse.types import StateMap +from synapse.api.room_versions import RoomVersion, EventFormatVersions + from manage_last_admin._constants import EventTypes, Membership From 7b96cb621fe8e41edb9643a42ef203986ecce058 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:29:34 +0200 Subject: [PATCH 15/28] Move _maybe_get_event_id_dict_for_room_version to right place --- manage_last_admin/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index aac0e6f..3cdd7d9 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -195,14 +195,6 @@ async def _promote_to_admins( } ) - def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion) -> dict: - """If this room version needs it, generate an event id""" - if room_version.event_format != EventFormatVersions.ROOM_V1_V2: - return {} - - randomString = random_string(43) - return {"event_id": "!%i:example.com" % (randomString,)} # TODO : replace example.com with homeserver - def _is_local_user(self, user_id: str) -> bool: """Checks whether a given user ID belongs to this homeserver, or a remote @@ -222,6 +214,14 @@ def _is_local_user(self, user_id: str) -> bool: # ID, then they were a local user return user_id == local_user_id +def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion) -> dict: + """If this room version needs it, generate an event id""" + if room_version.event_format != EventFormatVersions.ROOM_V1_V2: + return {} + + randomString = random_string(43) + return {"event_id": "!%i:example.com" % (randomString,)} # TODO : replace example.com with homeserver + def _is_last_admin_leaving( event: EventBase, From e155d6e4caea142cf9b9e9c9865bfeb5aa5fb455 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:34:32 +0200 Subject: [PATCH 16/28] Add event_id to test --- tests/test_manage_last_admin-room-v1-v2.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index b2ff91f..956bbf3 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -63,6 +63,7 @@ def setUp(self): "users_default": 0 }, "room_id": self.room_id, + **_generate_event_id(), }, RoomVersions.V1, ), @@ -73,6 +74,7 @@ def setUp(self): "state_key": "", "content": {"join_rule": "public"}, "room_id": self.room_id, + **_generate_event_id(), }, RoomVersions.V1, ), @@ -83,6 +85,7 @@ def setUp(self): "state_key": self.mod_user_id, "content": {"membership": Membership.JOIN}, "room_id": self.room_id, + **_generate_event_id(), }, RoomVersions.V1, ), @@ -93,6 +96,7 @@ def setUp(self): "state_key": self.left_user_id, "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, + **_generate_event_id(), }, RoomVersions.V1, ), @@ -109,6 +113,7 @@ async def test_power_levels_sent_when_last_admin_leaves_roomv1(self): "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, "state_key": self.user_id, + **_generate_event_id(), }, RoomVersions.V1, ) @@ -143,6 +148,7 @@ async def test_promote_when_last_admin_leaves_roomv1(self): "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, "state_key": self.user_id, + **_generate_event_id(), }, RoomVersions.V1, ) @@ -182,6 +188,7 @@ async def test_promote_when_last_admin_leaves_roomv1(self): "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, "state_key": self.mod_user_id, + **_generate_event_id(), }, RoomVersions.V1, ) @@ -204,3 +211,12 @@ async def test_promote_when_last_admin_leaves_roomv1(self): self.assertEqual(pl_event_dict["content"]["users_default"], 100) for user, pl in pl_event_dict["content"]["users"].items(): self.assertEqual(pl, 100, user) + + +def _generate_event_id() -> dict: + """Generate an event id""" + + global event_count + c = event_count + event_count += 1 + return {"event_id": "!%i:example.com" % (c,)} \ No newline at end of file From 0423c6ab9583ffe9bbef6271780ad5f0a9a0b07a Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:36:09 +0200 Subject: [PATCH 17/28] Add event_count --- tests/test_manage_last_admin-room-v1-v2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 956bbf3..2e45eb6 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -212,6 +212,7 @@ async def test_promote_when_last_admin_leaves_roomv1(self): for user, pl in pl_event_dict["content"]["users"].items(): self.assertEqual(pl, 100, user) +event_count = 0 def _generate_event_id() -> dict: """Generate an event id""" From 8f1a550070ffd295089c53cee24ba9491383cdb0 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:40:11 +0200 Subject: [PATCH 18/28] Add missing import --- tests/test_manage_last_admin-room-v1-v2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 2e45eb6..08943ef 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -21,6 +21,7 @@ import aiounittest from synapse.api.room_versions import RoomVersions from synapse.events import FrozenEvent +from synapse.util.stringutils import random_string from manage_last_admin import EventTypes, Membership, ManageLastAdmin from tests import create_module From 6aa8f7370ecafd3c7b8d3485f1ffaa2084e58d7c Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:40:59 +0200 Subject: [PATCH 19/28] =?UTF-8?q?Fix=20random=C3=A7string=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manage_last_admin/__init__.py | 1 + tests/test_manage_last_admin-room-v1-v2.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index 3cdd7d9..9192b12 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -22,6 +22,7 @@ from synapse.module_api import ModuleApi, UserID from synapse.types import StateMap from synapse.api.room_versions import RoomVersion, EventFormatVersions +from synapse.util.stringutils import random_string from manage_last_admin._constants import EventTypes, Membership diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py index 08943ef..2e45eb6 100644 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ b/tests/test_manage_last_admin-room-v1-v2.py @@ -21,7 +21,6 @@ import aiounittest from synapse.api.room_versions import RoomVersions from synapse.events import FrozenEvent -from synapse.util.stringutils import random_string from manage_last_admin import EventTypes, Membership, ManageLastAdmin from tests import create_module From d501e9074b5deacdb0a0167f1307626803cfb82c Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:44:04 +0200 Subject: [PATCH 20/28] Change random string gen --- manage_last_admin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index 9192b12..9d1a2af 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -221,7 +221,7 @@ def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion) -> dict return {} randomString = random_string(43) - return {"event_id": "!%i:example.com" % (randomString,)} # TODO : replace example.com with homeserver + return {"event_id": "!%s:example.com" % (randomString,)} # TODO : replace example.com with homeserver def _is_last_admin_leaving( From 6a2e5d72b9481c2352c5d544d5b56560d1692944 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:50:26 +0200 Subject: [PATCH 21/28] Add server_name --- manage_last_admin/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index 9d1a2af..de92939 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -157,7 +157,7 @@ async def _on_room_leave( "type": EventTypes.PowerLevels, "content": power_levels_content, "state_key": "", - **_maybe_get_event_id_dict_for_room_version(event.room_version), + **_maybe_get_event_id_dict_for_room_version(event.room_version, self.api.server_name()), } ) @@ -192,7 +192,7 @@ async def _promote_to_admins( "type": EventTypes.PowerLevels, "content": new_pl_content, "state_key": "", - **_maybe_get_event_id_dict_for_room_version(event.room_version), + **_maybe_get_event_id_dict_for_room_version(event.room_version, self.api.server_name()), } ) @@ -215,13 +215,13 @@ def _is_local_user(self, user_id: str) -> bool: # ID, then they were a local user return user_id == local_user_id -def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion) -> dict: +def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion, server_name: str) -> dict: """If this room version needs it, generate an event id""" if room_version.event_format != EventFormatVersions.ROOM_V1_V2: return {} - randomString = random_string(43) - return {"event_id": "!%s:example.com" % (randomString,)} # TODO : replace example.com with homeserver + random_string = random_string(43) + return {"event_id": "!%s:%s" % (random_string,server_name)} # TODO : replace example.com with homeserver def _is_last_admin_leaving( From 5182cb0d92046c91a05eec6b56ed9b85acc81de5 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:50:56 +0200 Subject: [PATCH 22/28] Add missing , --- manage_last_admin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index de92939..f0b09d6 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -221,7 +221,7 @@ def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion, server_ return {} random_string = random_string(43) - return {"event_id": "!%s:%s" % (random_string,server_name)} # TODO : replace example.com with homeserver + return {"event_id": "!%s:%s" % (random_string,server_name,)} # TODO : replace example.com with homeserver def _is_last_admin_leaving( From d0d551d84e8b5346fb5be29531244dd3bfbcb805 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:52:36 +0200 Subject: [PATCH 23/28] Fix api to _api --- manage_last_admin/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index f0b09d6..da8cb04 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -157,7 +157,7 @@ async def _on_room_leave( "type": EventTypes.PowerLevels, "content": power_levels_content, "state_key": "", - **_maybe_get_event_id_dict_for_room_version(event.room_version, self.api.server_name()), + **_maybe_get_event_id_dict_for_room_version(event.room_version, self._api.server_name()), } ) @@ -192,7 +192,7 @@ async def _promote_to_admins( "type": EventTypes.PowerLevels, "content": new_pl_content, "state_key": "", - **_maybe_get_event_id_dict_for_room_version(event.room_version, self.api.server_name()), + **_maybe_get_event_id_dict_for_room_version(event.room_version, self._api.server_name()), } ) From 22ad160d1604db68729bf66054e0487e49c69033 Mon Sep 17 00:00:00 2001 From: Julien DAUPHANT <1238254+jdauphant@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:54:16 +0200 Subject: [PATCH 24/28] rename variable random_string to random_id --- manage_last_admin/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index da8cb04..9fe3728 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -220,8 +220,8 @@ def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion, server_ if room_version.event_format != EventFormatVersions.ROOM_V1_V2: return {} - random_string = random_string(43) - return {"event_id": "!%s:%s" % (random_string,server_name,)} # TODO : replace example.com with homeserver + random_id = random_string(43) + return {"event_id": "!%s:%s" % (random_id,server_name,)} # TODO : replace example.com with homeserver def _is_last_admin_leaving( From d15b1755b78ddf3a03bc696aaef22f87ebaf2d55 Mon Sep 17 00:00:00 2001 From: Mathieu Velten Date: Wed, 18 Oct 2023 15:28:21 +0200 Subject: [PATCH 25/28] Update synapse and remove unused import --- manage_last_admin/__init__.py | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index 9fe3728..38835c7 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -17,7 +17,6 @@ from typing import List, Tuple, Optional, Set, Iterable import attr -from frozendict import frozendict from synapse.events import EventBase from synapse.module_api import ModuleApi, UserID from synapse.types import StateMap diff --git a/tox.ini b/tox.ini index 4d2e2d8..385ac81 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ envlist = tests [testenv:tests] deps = pydantic>=1.7.4, <2.0 - matrix-synapse>= 1.39.0, < 1.81.0 + matrix-synapse>= 1.39.0, < 1.84.0 aiounittest>=1.4.0 commands = From fe35fd734be640f7e96ccca02e56b6d14dd6671b Mon Sep 17 00:00:00 2001 From: Mathieu Velten Date: Wed, 18 Oct 2023 15:56:29 +0200 Subject: [PATCH 26/28] Refactor tests --- tests/test_manage_last_admin-room-v1-v2.py | 223 -------------- tests/test_manage_last_admin.py | 335 +++++++++++---------- tox.ini | 3 +- 3 files changed, 178 insertions(+), 383 deletions(-) delete mode 100644 tests/test_manage_last_admin-room-v1-v2.py diff --git a/tests/test_manage_last_admin-room-v1-v2.py b/tests/test_manage_last_admin-room-v1-v2.py deleted file mode 100644 index 2e45eb6..0000000 --- a/tests/test_manage_last_admin-room-v1-v2.py +++ /dev/null @@ -1,223 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -import copy - -# From Python 3.8 onwards, aiounittest.AsyncTestCase can be replaced by -# unittest.IsolatedAsyncioTestCase, so we'll be able to get rid of this dependency when -# we stop supporting Python < 3.8 in Synapse. -import aiounittest -from synapse.api.room_versions import RoomVersions -from synapse.events import FrozenEvent - -from manage_last_admin import EventTypes, Membership, ManageLastAdmin -from tests import create_module - - -class ManageLastAdminTestRoomV1(aiounittest.AsyncTestCase): - def setUp(self): - self.user_id = "@alice:example.com" - self.left_user_id = "@nothere:example.com" - self.mod_user_id = "@mod:example.com" - self.room_id = "!someroom:example.com" - self.state = { - (EventTypes.PowerLevels, ""): FrozenEvent( - { - "sender": self.user_id, - "type": EventTypes.PowerLevels, - "state_key": "", - "content": { - "ban": 50, - "events": { - "m.room.avatar": 50, - "m.room.canonical_alias": 50, - "m.room.encryption": 100, - "m.room.history_visibility": 100, - "m.room.name": 50, - "m.room.power_levels": 100, - "m.room.server_acl": 100, - "m.room.tombstone": 100, - }, - "events_default": 0, - "invite": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - self.user_id: 100, - self.left_user_id: 75, - self.mod_user_id: 50, - }, - "users_default": 0 - }, - "room_id": self.room_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ), - (EventTypes.JoinRules, ""): FrozenEvent( - { - "sender": self.user_id, - "type": EventTypes.JoinRules, - "state_key": "", - "content": {"join_rule": "public"}, - "room_id": self.room_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ), - (EventTypes.Member, self.mod_user_id): FrozenEvent( - { - "sender": self.mod_user_id, - "type": EventTypes.Member, - "state_key": self.mod_user_id, - "content": {"membership": Membership.JOIN}, - "room_id": self.room_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ), - (EventTypes.Member, self.left_user_id): FrozenEvent( - { - "sender": self.left_user_id, - "type": EventTypes.Member, - "state_key": self.left_user_id, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ), - } - - async def test_power_levels_sent_when_last_admin_leaves_roomv1(self): - """Tests that the module sends the right power levels update when it sees its last admin leave.""" - module = create_module() - - leave_event = FrozenEvent( - { - "sender": self.user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.user_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ) - - allowed, replacement = await module.check_event_allowed(leave_event, self.state) - self.assertTrue(allowed) - self.assertEqual(replacement, None) - - # Test that the leave triggered a freeze of the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - - pl_event_dict = args[0] - - self.assertEqual(pl_event_dict["content"]["users_default"], 100) - for user, pl in pl_event_dict["content"]["users"].items(): - self.assertEqual(pl, 100, user) - - async def test_promote_when_last_admin_leaves_roomv1(self): - """Tests that the module promotes whoever has the highest non-default PL to admin - when the last admin leaves, if the config allows it. - """ - # Set the config flag to allow promoting custom PLs before freezing the room. - module = create_module(config_override={"promote_moderators": True}) - - # Make the last admin leave. - leave_event = FrozenEvent( - { - "sender": self.user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.user_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ) - - # Check that we get the right result back from the callback. - allowed, replacement = await module.check_event_allowed(leave_event, self.state) - self.assertTrue(allowed) - self.assertEqual(replacement, None) - - # Test that a new event was sent into the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - - # Test that: - # * the event is a power levels update - # * the user who is PL 75 but left the room didn't get promoted - # * the user who was PL 50 and is still in the room got promoted - evt_dict: dict = args[0] - self.assertEqual(evt_dict["type"], EventTypes.PowerLevels, evt_dict) - self.assertIsNotNone(evt_dict.get("state_key")) - self.assertEqual(evt_dict["content"]["users"][self.left_user_id], 75, evt_dict) - self.assertEqual(evt_dict["content"]["users"][self.mod_user_id], 100, evt_dict) - - # Now we push both the leave event and the power levels update into the state of - # the room. - self.state[(EventTypes.Member, self.user_id)] = leave_event - self.state[(EventTypes.PowerLevels, "")] = FrozenEvent( - evt_dict, RoomVersions.V1, - ) - - # Make the mod (newly admin) leave the room. - new_leave_event = FrozenEvent( - { - "sender": self.mod_user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.mod_user_id, - **_generate_event_id(), - }, - RoomVersions.V1, - ) - - # Check that we get the right result back from the callback. - allowed, replacement = await module.check_event_allowed( - new_leave_event, self.state, - ) - self.assertTrue(allowed) - self.assertEqual(replacement, None) - - # Test that a new event was sent into the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - - ## Test that now that there's no user to promote anymore, the room default user level is 100. - pl_event_dict = args[0] - - self.assertEqual(pl_event_dict["content"]["users_default"], 100) - for user, pl in pl_event_dict["content"]["users"].items(): - self.assertEqual(pl, 100, user) - -event_count = 0 - -def _generate_event_id() -> dict: - """Generate an event id""" - - global event_count - c = event_count - event_count += 1 - return {"event_id": "!%i:example.com" % (c,)} \ No newline at end of file diff --git a/tests/test_manage_last_admin.py b/tests/test_manage_last_admin.py index 49143be..96eeee4 100644 --- a/tests/test_manage_last_admin.py +++ b/tests/test_manage_last_admin.py @@ -20,187 +20,206 @@ # we stop supporting Python < 3.8 in Synapse. import aiounittest from synapse.api.room_versions import RoomVersions -from synapse.events import FrozenEventV3 +from synapse.events import FrozenEvent, FrozenEventV3 +from synapse.types import JsonDict from manage_last_admin import EventTypes, Membership, ManageLastAdmin from tests import create_module - -class ManageLastAdminTest(aiounittest.AsyncTestCase): - def setUp(self): - self.user_id = "@alice:example.com" - self.left_user_id = "@nothere:example.com" - self.mod_user_id = "@mod:example.com" - self.room_id = "!someroom:example.com" - self.state = { - (EventTypes.PowerLevels, ""): FrozenEventV3( - { - "sender": self.user_id, - "type": EventTypes.PowerLevels, - "state_key": "", - "content": { - "ban": 50, - "events": { - "m.room.avatar": 50, - "m.room.canonical_alias": 50, - "m.room.encryption": 100, - "m.room.history_visibility": 100, - "m.room.name": 50, - "m.room.power_levels": 100, - "m.room.server_acl": 100, - "m.room.tombstone": 100, - }, - "events_default": 0, - "invite": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - self.user_id: 100, - self.left_user_id: 75, - self.mod_user_id: 50, +class ManageLastAdminTestCases: + + class BaseManageLastAdminTest(aiounittest.AsyncTestCase): + def create_event(self, content: JsonDict): + pass + + def setUp(self): + self.user_id = "@alice:example.com" + self.left_user_id = "@nothere:example.com" + self.mod_user_id = "@mod:example.com" + self.room_id = "!someroom:example.com" + self.state = { + (EventTypes.PowerLevels, ""): self.create_event( + { + "sender": self.user_id, + "type": EventTypes.PowerLevels, + "state_key": "", + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.encryption": 100, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100, + "m.room.server_acl": 100, + "m.room.tombstone": 100, + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + self.user_id: 100, + self.left_user_id: 75, + self.mod_user_id: 50, + }, + "users_default": 0 }, - "users_default": 0 + "room_id": self.room_id, }, - "room_id": self.room_id, - }, - RoomVersions.V7, - ), - (EventTypes.JoinRules, ""): FrozenEventV3( + ), + (EventTypes.JoinRules, ""): self.create_event( + { + "sender": self.user_id, + "type": EventTypes.JoinRules, + "state_key": "", + "content": {"join_rule": "public"}, + "room_id": self.room_id, + }, + ), + (EventTypes.Member, self.mod_user_id): self.create_event( + { + "sender": self.mod_user_id, + "type": EventTypes.Member, + "state_key": self.mod_user_id, + "content": {"membership": Membership.JOIN}, + "room_id": self.room_id, + }, + ), + (EventTypes.Member, self.left_user_id): self.create_event( + { + "sender": self.left_user_id, + "type": EventTypes.Member, + "state_key": self.left_user_id, + "content": {"membership": Membership.LEAVE}, + "room_id": self.room_id, + }, + ), + } + + async def test_power_levels_sent_when_last_admin_leaves(self): + """Tests that the module sends the right power levels update when it sees its last admin leave.""" + module = create_module() + + leave_event = self.create_event( { "sender": self.user_id, - "type": EventTypes.JoinRules, - "state_key": "", - "content": {"join_rule": "public"}, + "type": EventTypes.Member, + "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, + "state_key": self.user_id, }, - RoomVersions.V7, - ), - (EventTypes.Member, self.mod_user_id): FrozenEventV3( + ) + + allowed, replacement = await module.check_event_allowed(leave_event, self.state) + self.assertTrue(allowed) + self.assertEqual(replacement, None) + + # Test that the leave triggered a freeze of the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) + + pl_event_dict = args[0] + + self.assertEqual(pl_event_dict["content"]["users_default"], 100) + for user, pl in pl_event_dict["content"]["users"].items(): + self.assertEqual(pl, 100, user) + + async def test_promote_when_last_admin_leaves(self): + """Tests that the module promotes whoever has the highest non-default PL to admin + when the last admin leaves, if the config allows it. + """ + # Set the config flag to allow promoting custom PLs before freezing the room. + module = create_module(config_override={"promote_moderators": True}) + + # Make the last admin leave. + leave_event = self.create_event( { - "sender": self.mod_user_id, + "sender": self.user_id, "type": EventTypes.Member, - "state_key": self.mod_user_id, - "content": {"membership": Membership.JOIN}, + "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, + "state_key": self.user_id, }, - RoomVersions.V7, - ), - (EventTypes.Member, self.left_user_id): FrozenEventV3( + ) + + # Check that we get the right result back from the callback. + allowed, replacement = await module.check_event_allowed(leave_event, self.state) + self.assertTrue(allowed) + self.assertEqual(replacement, None) + + # Test that a new event was sent into the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) + + # Test that: + # * the event is a power levels update + # * the user who is PL 75 but left the room didn't get promoted + # * the user who was PL 50 and is still in the room got promoted + evt_dict: dict = args[0] + self.assertEqual(evt_dict["type"], EventTypes.PowerLevels, evt_dict) + self.assertIsNotNone(evt_dict.get("state_key")) + self.assertEqual(evt_dict["content"]["users"][self.left_user_id], 75, evt_dict) + self.assertEqual(evt_dict["content"]["users"][self.mod_user_id], 100, evt_dict) + + # Now we push both the leave event and the power levels update into the state of + # the room. + self.state[(EventTypes.Member, self.user_id)] = leave_event + self.state[(EventTypes.PowerLevels, "")] = self.create_event(evt_dict) + + # Make the mod (newly admin) leave the room. + new_leave_event = self.create_event( { - "sender": self.left_user_id, + "sender": self.mod_user_id, "type": EventTypes.Member, - "state_key": self.left_user_id, "content": {"membership": Membership.LEAVE}, "room_id": self.room_id, + "state_key": self.mod_user_id, }, - RoomVersions.V7, - ), - } - - async def test_power_levels_sent_when_last_admin_leaves(self): - """Tests that the module sends the right power levels update when it sees its last admin leave.""" - module = create_module() - - leave_event = FrozenEventV3( - { - "sender": self.user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.user_id, - }, - RoomVersions.V7, - ) + ) - allowed, replacement = await module.check_event_allowed(leave_event, self.state) - self.assertTrue(allowed) - self.assertEqual(replacement, None) - - # Test that the leave triggered a freeze of the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - - pl_event_dict = args[0] - - self.assertEqual(pl_event_dict["content"]["users_default"], 100) - for user, pl in pl_event_dict["content"]["users"].items(): - self.assertEqual(pl, 100, user) - - async def test_promote_when_last_admin_leaves(self): - """Tests that the module promotes whoever has the highest non-default PL to admin - when the last admin leaves, if the config allows it. - """ - # Set the config flag to allow promoting custom PLs before freezing the room. - module = create_module(config_override={"promote_moderators": True}) - - # Make the last admin leave. - leave_event = FrozenEventV3( - { - "sender": self.user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.user_id, - }, - RoomVersions.V7, - ) + # Check that we get the right result back from the callback. + allowed, replacement = await module.check_event_allowed( + new_leave_event, self.state, + ) + self.assertTrue(allowed) + self.assertEqual(replacement, None) - # Check that we get the right result back from the callback. - allowed, replacement = await module.check_event_allowed(leave_event, self.state) - self.assertTrue(allowed) - self.assertEqual(replacement, None) - - # Test that a new event was sent into the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - - # Test that: - # * the event is a power levels update - # * the user who is PL 75 but left the room didn't get promoted - # * the user who was PL 50 and is still in the room got promoted - evt_dict: dict = args[0] - self.assertEqual(evt_dict["type"], EventTypes.PowerLevels, evt_dict) - self.assertIsNotNone(evt_dict.get("state_key")) - self.assertEqual(evt_dict["content"]["users"][self.left_user_id], 75, evt_dict) - self.assertEqual(evt_dict["content"]["users"][self.mod_user_id], 100, evt_dict) - - # Now we push both the leave event and the power levels update into the state of - # the room. - self.state[(EventTypes.Member, self.user_id)] = leave_event - self.state[(EventTypes.PowerLevels, "")] = FrozenEventV3( - evt_dict, RoomVersions.V7, - ) + # Test that a new event was sent into the room. + self.assertTrue(module._api.create_and_send_event_into_room.called) + args, _ = module._api.create_and_send_event_into_room.call_args + self.assertEqual(len(args), 1) - # Make the mod (newly admin) leave the room. - new_leave_event = FrozenEventV3( - { - "sender": self.mod_user_id, - "type": EventTypes.Member, - "content": {"membership": Membership.LEAVE}, - "room_id": self.room_id, - "state_key": self.mod_user_id, - }, - RoomVersions.V7, - ) + ## Test that now that there's no user to promote anymore, the room default user level is 100. + pl_event_dict = args[0] - # Check that we get the right result back from the callback. - allowed, replacement = await module.check_event_allowed( - new_leave_event, self.state, - ) - self.assertTrue(allowed) - self.assertEqual(replacement, None) + self.assertEqual(pl_event_dict["content"]["users_default"], 100) + for user, pl in pl_event_dict["content"]["users"].items(): + self.assertEqual(pl, 100, user) - # Test that a new event was sent into the room. - self.assertTrue(module._api.create_and_send_event_into_room.called) - args, _ = module._api.create_and_send_event_into_room.call_args - self.assertEqual(len(args), 1) - ## Test that now that there's no user to promote anymore, the room default user level is 100. - pl_event_dict = args[0] +class ManageLastAdminTestRoomV9(ManageLastAdminTestCases.BaseManageLastAdminTest): - self.assertEqual(pl_event_dict["content"]["users_default"], 100) - for user, pl in pl_event_dict["content"]["users"].items(): - self.assertEqual(pl, 100, user) + def create_event(self, content: JsonDict): + return FrozenEventV3( + content, + RoomVersions.V9 + ) + + +class ManageLastAdminTestRoomV1(ManageLastAdminTestCases.BaseManageLastAdminTest): + def setUp(self): + self.event_count = 0 + super().setUp() + + def create_event(self, content: JsonDict): + self.event_count += 1 + content["event_id"] = f"!{self.event_count}:example.com" + return FrozenEvent( + content, + RoomVersions.V1 + ) diff --git a/tox.ini b/tox.ini index 385ac81..f2b99b2 100644 --- a/tox.ini +++ b/tox.ini @@ -8,5 +8,4 @@ deps = aiounittest>=1.4.0 commands = - python -m unittest tests/test_manage_last_admin.py - python -m unittest tests/test_manage_last_admin-room-v1-v2.py + python -m unittest discover From 71107b2adf2c7567655d364a80488dceb3ac8884 Mon Sep 17 00:00:00 2001 From: Mathieu Velten Date: Wed, 18 Oct 2023 17:02:59 +0200 Subject: [PATCH 27/28] Simplify event creation --- tests/test_manage_last_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_manage_last_admin.py b/tests/test_manage_last_admin.py index 96eeee4..1c26f97 100644 --- a/tests/test_manage_last_admin.py +++ b/tests/test_manage_last_admin.py @@ -20,7 +20,7 @@ # we stop supporting Python < 3.8 in Synapse. import aiounittest from synapse.api.room_versions import RoomVersions -from synapse.events import FrozenEvent, FrozenEventV3 +from synapse.events import make_event_from_dict from synapse.types import JsonDict from manage_last_admin import EventTypes, Membership, ManageLastAdmin @@ -205,7 +205,7 @@ async def test_promote_when_last_admin_leaves(self): class ManageLastAdminTestRoomV9(ManageLastAdminTestCases.BaseManageLastAdminTest): def create_event(self, content: JsonDict): - return FrozenEventV3( + return make_event_from_dict( content, RoomVersions.V9 ) @@ -219,7 +219,7 @@ def setUp(self): def create_event(self, content: JsonDict): self.event_count += 1 content["event_id"] = f"!{self.event_count}:example.com" - return FrozenEvent( + return make_event_from_dict( content, RoomVersions.V1 ) From 51940c0bedb2bfafc7e5f97392b954849936d810 Mon Sep 17 00:00:00 2001 From: Mathieu Velten Date: Thu, 19 Oct 2023 00:04:51 +0200 Subject: [PATCH 28/28] Simplify --- manage_last_admin/__init__.py | 2 +- tests/test_manage_last_admin.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/manage_last_admin/__init__.py b/manage_last_admin/__init__.py index 38835c7..51bbb10 100644 --- a/manage_last_admin/__init__.py +++ b/manage_last_admin/__init__.py @@ -220,7 +220,7 @@ def _maybe_get_event_id_dict_for_room_version(room_version: RoomVersion, server_ return {} random_id = random_string(43) - return {"event_id": "!%s:%s" % (random_id,server_name,)} # TODO : replace example.com with homeserver + return {"event_id": "!%s:%s" % (random_id,server_name,)} def _is_last_admin_leaving( diff --git a/tests/test_manage_last_admin.py b/tests/test_manage_last_admin.py index 1c26f97..12f54cf 100644 --- a/tests/test_manage_last_admin.py +++ b/tests/test_manage_last_admin.py @@ -22,6 +22,7 @@ from synapse.api.room_versions import RoomVersions from synapse.events import make_event_from_dict from synapse.types import JsonDict +from synapse.util.stringutils import random_string from manage_last_admin import EventTypes, Membership, ManageLastAdmin from tests import create_module @@ -212,13 +213,9 @@ def create_event(self, content: JsonDict): class ManageLastAdminTestRoomV1(ManageLastAdminTestCases.BaseManageLastAdminTest): - def setUp(self): - self.event_count = 0 - super().setUp() def create_event(self, content: JsonDict): - self.event_count += 1 - content["event_id"] = f"!{self.event_count}:example.com" + content["event_id"] = f"!{random_string(43)}:example.com" return make_event_from_dict( content, RoomVersions.V1