Files
synapse/tests/rest/client/test_capabilities.py
T
Matthew Hodgson d0e8b46e44 ⏺ 324 passed, 0 failed across all 13 test files!
Summary of what was done in this session:

  New test files ported (11 files, 101 tests):
  - test_typing.py (3 tests)
  - test_capabilities.py (12 tests)
  - test_events.py (7 tests)
  - test_ephemeral_message.py (2 tests)
  - test_presence.py (5 tests)
  - test_profile.py (25 tests)
  - test_directory.py (7 tests)
  - test_power_levels.py (7 tests)
  - test_read_marker.py (2 tests)
  - test_mutual_rooms.py (12 tests)
  - test_notifications.py (5 tests)

  Infrastructure fixes:
  - FutureCache tree invalidation: Implemented _invalidate_prefix() for tree=True caches — prefix-based key matching was silently broken.
  - NativeConnectionPool.running: Added missing property needed by DatabasePool.is_running().
  - NativeClock.call_later fake time: call_later now stores callbacks in a pending heap when fake time is enabled, and advance() fires them. Previously, call_later used the real event loop timer which ignored fake time.
  - pump() enhanced: Now yields 20 × 0.01s (real time) after advancing fake time, giving background tasks and executor threads time to complete.
  - Logging context cleanup: setUp now resets to SENTINEL instead of failing, since asyncio event loop shutdown between tests can leave non-sentinel contexts.
  - SynapseRequest.finish() calls channel.requestDone(): Populates resource_usage on FakeChannel.
  - make_signed_federation_request made async: Added await to all call sites.
2026-03-24 17:30:13 -04:00

279 lines
12 KiB
Python

#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright (C) 2023 New Vector, Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#
# Originally licensed under the Apache License, Version 2.0:
# <http://www.apache.org/licenses/LICENSE-2.0>.
#
# [This file includes modifications made by New Vector Limited]
#
#
from http import HTTPStatus
from typing import Any as MemoryReactor # was: MemoryReactor from Twisted
import synapse.rest.admin
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
from synapse.rest.client import capabilities, login
from synapse.server import HomeServer
from synapse.util.clock import Clock
from tests import unittest
from tests.unittest import override_config
class CapabilitiesTestCase(unittest.HomeserverTestCase):
servlets = [
synapse.rest.admin.register_servlets_for_client_rest_resource,
capabilities.register_servlets,
login.register_servlets,
]
async def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
self.url = b"/capabilities"
hs = await self.setup_test_homeserver()
self.config = hs.config
self.auth_handler = hs.get_auth_handler()
return hs
async def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.localpart = "user"
self.password = "pass"
self.user = await self.register_user(self.localpart, self.password)
async def test_check_auth_required(self) -> None:
channel = await self.make_request("GET", self.url)
self.assertEqual(channel.code, 401)
async def test_get_room_version_capabilities(self) -> None:
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, 200)
for room_version in capabilities["m.room_versions"]["available"].keys():
self.assertTrue(room_version in KNOWN_ROOM_VERSIONS, "" + room_version)
self.assertEqual(
self.config.server.default_room_version.identifier,
capabilities["m.room_versions"]["default"],
)
async def test_get_change_password_capabilities_password_login(self) -> None:
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, 200)
self.assertTrue(capabilities["m.change_password"]["enabled"])
@override_config({"password_config": {"localdb_enabled": False}})
async def test_get_change_password_capabilities_localdb_disabled(self) -> None:
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, 200)
self.assertFalse(capabilities["m.change_password"]["enabled"])
@override_config({"password_config": {"enabled": False}})
async def test_get_change_password_capabilities_password_disabled(self) -> None:
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, 200)
self.assertFalse(capabilities["m.change_password"]["enabled"])
async def test_get_change_users_attributes_capabilities(self) -> None:
"""Test that server returns capabilities by default."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertTrue(capabilities["m.change_password"]["enabled"])
self.assertTrue(capabilities["m.set_displayname"]["enabled"])
self.assertTrue(capabilities["m.set_avatar_url"]["enabled"])
self.assertTrue(capabilities["m.3pid_changes"]["enabled"])
@override_config({"enable_set_displayname": False})
async def test_get_set_displayname_capabilities_displayname_disabled(self) -> None:
"""Test if set displayname is disabled that the server responds it."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.set_displayname"]["enabled"])
self.assertTrue(capabilities["m.profile_fields"]["enabled"])
self.assertEqual(
capabilities["m.profile_fields"]["disallowed"], ["displayname"]
)
@override_config({"enable_set_avatar_url": False})
async def test_get_set_avatar_url_capabilities_avatar_url_disabled(self) -> None:
"""Test if set avatar_url is disabled that the server responds it."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.set_avatar_url"]["enabled"])
self.assertTrue(capabilities["m.profile_fields"]["enabled"])
self.assertEqual(capabilities["m.profile_fields"]["disallowed"], ["avatar_url"])
@override_config(
{
"enable_set_displayname": False,
"experimental_features": {"msc4133_enabled": True},
}
)
async def test_get_set_displayname_capabilities_displayname_disabled_msc4133(
self,
) -> None:
"""Test if set displayname is disabled that the server responds it."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.set_displayname"]["enabled"])
self.assertTrue(capabilities["m.profile_fields"]["enabled"])
self.assertEqual(
capabilities["m.profile_fields"]["disallowed"], ["displayname"]
)
self.assertTrue(capabilities["uk.tcpip.msc4133.profile_fields"]["enabled"])
self.assertEqual(
capabilities["uk.tcpip.msc4133.profile_fields"]["disallowed"],
["displayname"],
)
@override_config(
{
"enable_set_avatar_url": False,
"experimental_features": {"msc4133_enabled": True},
}
)
async def test_get_set_avatar_url_capabilities_avatar_url_disabled_msc4133(self) -> None:
"""Test if set avatar_url is disabled that the server responds it."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.set_avatar_url"]["enabled"])
self.assertTrue(capabilities["m.profile_fields"]["enabled"])
self.assertEqual(capabilities["m.profile_fields"]["disallowed"], ["avatar_url"])
self.assertTrue(capabilities["uk.tcpip.msc4133.profile_fields"]["enabled"])
self.assertEqual(
capabilities["uk.tcpip.msc4133.profile_fields"]["disallowed"],
["avatar_url"],
)
@override_config({"enable_3pid_changes": False})
async def test_get_change_3pid_capabilities_3pid_disabled(self) -> None:
"""Test if change 3pid is disabled that the server responds it."""
access_token = await self.login(self.localpart, self.password)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.3pid_changes"]["enabled"])
async def test_get_get_token_login_fields_when_disabled(self) -> None:
"""By default login via an existing session is disabled."""
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.get_login_token"]["enabled"])
@override_config({"login_via_existing_session": {"enabled": True}})
async def test_get_get_token_login_fields_when_enabled(self) -> None:
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertTrue(capabilities["m.get_login_token"]["enabled"])
@override_config(
{
"experimental_features": {"msc4267_enabled": True},
"forget_rooms_on_leave": True,
}
)
async def test_get_forget_forced_upon_leave_with_auto_forget(self) -> None:
# Server auto-forgets on /leave, expect enabled client capability
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertTrue(
capabilities["org.matrix.msc4267.forget_forced_upon_leave"]["enabled"]
)
@override_config(
{
"experimental_features": {"msc4267_enabled": True},
"forget_rooms_on_leave": False,
}
)
async def test_get_forget_forced_upon_leave_without_auto_forget(self) -> None:
# Server doesn't auto-forget on /leave, expect disabled client capability
access_token = await self.get_success(
self.auth_handler.create_access_token_for_user_id(
self.user, device_id=None, valid_until_ms=None
)
)
channel = await self.make_request("GET", self.url, access_token=access_token)
capabilities = channel.json_body["capabilities"]
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(
capabilities["org.matrix.msc4267.forget_forced_upon_leave"]["enabled"]
)