From 55ca2be0f6758f18c9758f83cf4612bfe9b0edda Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 16 Jan 2026 18:05:26 +0000 Subject: [PATCH] Update unit tests to check MAS config, deactivation works --- .../synapse_guest_module/mas_admin_client.py | 2 +- .../synapse/tests/__init__.py | 24 ++++++++--- .../synapse/tests/test_guest_module.py | 34 ++++++++++++++- .../synapse/tests/test_guest_user_reaper.py | 43 +++++++++++++++++++ 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 50ff0e2480..70e9e1b0f8 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -5,7 +5,7 @@ import base64 import logging -from typing import Any, Dict +from typing import Any, Awaitable, Dict, Optional from synapse.module_api import ModuleApi diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index 04d9c250b8..d6bc7b5f3f 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -9,7 +9,7 @@ import sqlite3 from asyncio import Future -from typing import Any, Awaitable, Callable, Tuple, TypeVar +from typing import Any, Awaitable, Callable, Dict, Tuple, TypeVar from unittest.mock import Mock from synapse.http.client import SimpleHttpClient @@ -89,7 +89,9 @@ async def register_user(localpart: str, admin: bool = False) -> str: return f"@{localpart}:matrix.local" -def create_module() -> Tuple[GuestModule, Mock, SQLiteStore]: +def create_module( + config_override: Dict[str, Any] | None = None, +) -> Tuple[GuestModule, Mock, SQLiteStore]: store = SQLiteStore() _setup_db(store.conn) @@ -111,14 +113,19 @@ def create_module() -> Tuple[GuestModule, Mock, SQLiteStore]: ) # If necessary, give parse_config some configuration to parse. - config = GuestModule.parse_config( - { - "enable_user_reaper": False, - } - ) + config_dict: Dict[str, Any] = { + "enable_user_reaper": False, + } + if config_override is not None: + config_dict.update(config_override) + + config = GuestModule.parse_config(config_dict) module = GuestModule(config, module_api) + if getattr(module, "_mas_tables_ready", None) is not None: + module._mas_tables_ready.set() # type: ignore[union-attr] + return module, module_api, store @@ -127,3 +134,6 @@ def _setup_db(conn: sqlite3.Connection) -> None: conn.execute( "CREATE TABLE users(name text, deactivated smallint, creation_ts bigint)" ) + conn.execute( + "CREATE TABLE guest_module_mas_users(mas_user_id text, created_at bigint)" + ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index f92614c714..01605528a0 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -12,7 +12,7 @@ from synapse.module_api import ProfileInfo, UserProfile from synapse.module_api.errors import ConfigError from synapse.types import UserID -from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.config import GuestModuleConfig, MasConfig from synapse_guest_module.guest_module import GuestModule from tests import create_module @@ -28,10 +28,11 @@ class GuestModuleTest(aiounittest.AsyncTestCase): display_name_suffix=" (Guest)", enable_user_reaper=True, user_expiration_seconds=24 * 60 * 60, + mas=None, ), ) - async def test_parse_config_custom(self) -> None: + async def test_parse_config_no_mas(self) -> None: config = GuestModule.parse_config( { "user_id_prefix": "tmp-", @@ -48,6 +49,35 @@ class GuestModuleTest(aiounittest.AsyncTestCase): display_name_suffix=" (Temporary)", enable_user_reaper=False, user_expiration_seconds=100, + mas=None, + ), + ) + + async def test_parse_config_mas(self) -> None: + config = GuestModule.parse_config( + { + "mas": { + "admin_api_base_url": "https://mas.example.org", + "client_id": "client-id", + "client_secret": "client-secret", + }, + } + ) + + self.assertEqual( + config, + GuestModuleConfig( + user_id_prefix="guest-", + display_name_suffix=" (Guest)", + enable_user_reaper=True, + user_expiration_seconds=24 * 60 * 60, + mas=MasConfig( + admin_api_base_url="https://mas.example.org", + oauth_base_url="https://mas.example.org", + client_id="client-id", + client_secret="client-secret", + client_secret_filepath=None, + ), ), ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index 0a865c3f2d..06815e1427 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -121,3 +121,46 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): ), ] ) + + async def test_deactivate_expired_mas_users_success(self) -> None: + module, module_api, store = create_module( + { + "mas": { + "admin_api_base_url": "https://mas.example.org", + "oauth_base_url": "https://oauth.mas.example.org", + "client_id": "client-id", + "client_secret": "client-secret", + }, + } + ) + + now = int(time.time()) + store.conn.executemany( + "INSERT INTO guest_module_mas_users VALUES (?, ?)", + [ + ["mas-old-1", 0], + ["mas-active", now], + ], + ) + + # These two methods are `AsyncMock`s, so no need to use `make_awaitable`.` + token_response = {"access_token": "mas_admin_token"} + module_api.http_client.post_urlencoded_get_json.return_value = token_response + module_api.http_client.post_json_get_json.return_value = {} + + await module.reaper.deactivate_expired_guest_users() + + deactivate_call = call( + uri="https://mas.example.org/api/admin/v1/users/mas-old-1/deactivate", + post_json={"skip_erase": True}, + headers={"Authorization": ["Bearer mas_admin_token"]}, + ) + self.assertIn( + deactivate_call, + module_api.http_client.post_json_get_json.await_args_list, + ) + + remaining_users = store.conn.execute( + "SELECT mas_user_id FROM guest_module_mas_users" + ).fetchall() + self.assertEqual(remaining_users, [("mas-active",)])