mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-28 20:28:17 +00:00
Add unit tests
This commit is contained in:
@@ -19,8 +19,9 @@
|
||||
#
|
||||
#
|
||||
import logging
|
||||
import urllib.parse
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import Mock
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
@@ -39,7 +40,9 @@ from synapse.rest.client import login, room
|
||||
from synapse.server import HomeServer
|
||||
from synapse.storage.controllers.state import server_acl_evaluator_from_event
|
||||
from synapse.types import JsonDict
|
||||
from synapse.types.state import StateEventQuery
|
||||
from synapse.util.clock import Clock
|
||||
from synapse.util.json import json_encoder
|
||||
|
||||
from tests import unittest
|
||||
from tests.test_utils.event_builders import make_test_event, make_test_pdu_event
|
||||
@@ -76,6 +79,119 @@ class FederationServerTests(unittest.FederatingHomeserverTestCase):
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, channel.code, channel.result)
|
||||
self.assertEqual(channel.json_body["errcode"], "M_NOT_JSON")
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_hierarchy_additional_state_query_parameter(self) -> None:
|
||||
"""The federation hierarchy servlet parses repeated MSC4507 JSON params."""
|
||||
u1 = self.register_user("u1", "pass")
|
||||
u1_token = self.login("u1", "pass")
|
||||
|
||||
room_id = self.helper.create_room_as(u1, tok=u1_token)
|
||||
event_type = "m.some_event"
|
||||
self.helper.send_state(
|
||||
room_id,
|
||||
event_type=event_type,
|
||||
body={"foo": ["bar"]},
|
||||
tok=u1_token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
room_id,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {"state_keys": [""]}}},
|
||||
tok=u1_token,
|
||||
)
|
||||
|
||||
query = urllib.parse.urlencode(
|
||||
[
|
||||
(
|
||||
"org.matrix.msc4507.additional_state",
|
||||
json_encoder.encode({"type": event_type, "state_key": ""}),
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
channel = self.make_signed_federation_request(
|
||||
"GET", f"/_matrix/federation/v1/hierarchy/{room_id}?{query}"
|
||||
)
|
||||
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, channel.result)
|
||||
self.assertEqual(
|
||||
channel.json_body["room"]["org.matrix.msc4507.additional_state"],
|
||||
[
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "",
|
||||
"content": {"foo": ["bar"]},
|
||||
"sender": u1,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_hierarchy_additional_state_percent_encoded_state_key(self) -> None:
|
||||
"""MSC4507 query values are percent-decoded exactly once."""
|
||||
room_id = "!unknown:test"
|
||||
event_type = "m.some_event_type"
|
||||
state_key = "%22"
|
||||
room_summary_handler = self.hs.get_room_summary_handler()
|
||||
room_summary_handler.get_federation_hierarchy = AsyncMock(return_value={}) # type: ignore[method-assign]
|
||||
|
||||
query = urllib.parse.urlencode(
|
||||
[
|
||||
(
|
||||
"org.matrix.msc4507.additional_state",
|
||||
json_encoder.encode({"type": event_type, "state_key": state_key}),
|
||||
)
|
||||
]
|
||||
)
|
||||
self.assertIn("%2522", query)
|
||||
|
||||
channel = self.make_signed_federation_request(
|
||||
"GET", f"/_matrix/federation/v1/hierarchy/{room_id}?{query}"
|
||||
)
|
||||
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, channel.result)
|
||||
room_summary_handler.get_federation_hierarchy.assert_awaited_once_with(
|
||||
self.OTHER_SERVER_NAME,
|
||||
room_id,
|
||||
False,
|
||||
(StateEventQuery(event_type, state_key),),
|
||||
)
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_hierarchy_additional_state_rejects_non_string_state_key(self) -> None:
|
||||
"""MSC4507 query objects must omit state_key or use a string."""
|
||||
query = urllib.parse.urlencode(
|
||||
[
|
||||
(
|
||||
"org.matrix.msc4507.additional_state",
|
||||
json_encoder.encode(
|
||||
{"type": "m.some_event_type", "state_key": None}
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
channel = self.make_signed_federation_request(
|
||||
"GET", f"/_matrix/federation/v1/hierarchy/!unknown:test?{query}"
|
||||
)
|
||||
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, channel.code, channel.result)
|
||||
self.assertEqual(channel.json_body["errcode"], "M_INVALID_PARAM")
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_hierarchy_additional_state_rejects_invalid_json(self) -> None:
|
||||
"""MSC4507 query objects must be valid JSON."""
|
||||
query = urllib.parse.urlencode(
|
||||
[("org.matrix.msc4507.additional_state", "not json")]
|
||||
)
|
||||
|
||||
channel = self.make_signed_federation_request(
|
||||
"GET", f"/_matrix/federation/v1/hierarchy/!unknown:test?{query}"
|
||||
)
|
||||
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, channel.code, channel.result)
|
||||
self.assertEqual(channel.json_body["errcode"], "M_NOT_JSON")
|
||||
|
||||
def test_failed_edu_causes_500(self) -> None:
|
||||
"""If the EDU handler fails, /send should return a 500."""
|
||||
|
||||
|
||||
@@ -36,11 +36,17 @@ from synapse.api.constants import (
|
||||
from synapse.api.errors import AuthError, NotFoundError, SynapseError
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.federation.transport.client import TransportLayerClient
|
||||
from synapse.handlers.room_summary import _child_events_comparison_key, _RoomEntry
|
||||
from synapse.handlers.room_summary import (
|
||||
_child_events_comparison_key,
|
||||
_get_permitted_public_state,
|
||||
_parse_public_state,
|
||||
_RoomEntry,
|
||||
)
|
||||
from synapse.rest import admin
|
||||
from synapse.rest.client import login, room
|
||||
from synapse.server import HomeServer
|
||||
from synapse.types import JsonDict, UserID, create_requester
|
||||
from synapse.types.state import StateEventQuery, StateFilter
|
||||
from synapse.util.clock import Clock
|
||||
|
||||
from tests import unittest
|
||||
@@ -120,6 +126,62 @@ class TestSpaceSummarySort(unittest.TestCase):
|
||||
self.assertEqual([ev2, ev1], _order(ev1, ev2))
|
||||
|
||||
|
||||
class PublicStateTestCase(unittest.TestCase):
|
||||
def test_parse_public_state(self) -> None:
|
||||
"""Public state rules are validated and normalized independently."""
|
||||
public_state = _parse_public_state(
|
||||
{
|
||||
"wildcard": {},
|
||||
"limited": {"state_keys": ["one", "two", "one"]},
|
||||
"empty": {"state_keys": []},
|
||||
"invalid_rule": [],
|
||||
"invalid_state_keys": {"state_keys": ["one", 2]},
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
public_state,
|
||||
StateFilter.from_types(
|
||||
[("wildcard", None), ("limited", "one"), ("limited", "two")]
|
||||
),
|
||||
)
|
||||
|
||||
def test_get_permitted_public_state(self) -> None:
|
||||
"""Requested state is intersected with normalized public state rules."""
|
||||
public_state = StateFilter.from_types(
|
||||
[
|
||||
("wildcard", None),
|
||||
("limited", "one"),
|
||||
("limited", "two"),
|
||||
("overlap", "one"),
|
||||
("overlap", "two"),
|
||||
]
|
||||
)
|
||||
|
||||
permitted_state = _get_permitted_public_state(
|
||||
[
|
||||
StateEventQuery("wildcard", "requested"),
|
||||
StateEventQuery("limited"),
|
||||
StateEventQuery("overlap", "two"),
|
||||
StateEventQuery("overlap", "three"),
|
||||
StateEventQuery("private"),
|
||||
],
|
||||
public_state,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
permitted_state,
|
||||
StateFilter.from_types(
|
||||
[
|
||||
("wildcard", "requested"),
|
||||
("limited", "one"),
|
||||
("limited", "two"),
|
||||
("overlap", "two"),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class SpaceSummaryTestCase(unittest.HomeserverTestCase):
|
||||
servlets = [
|
||||
admin.register_servlets_for_client_rest_resource,
|
||||
@@ -246,6 +308,255 @@ class SpaceSummaryTestCase(unittest.HomeserverTestCase):
|
||||
)
|
||||
self._assert_hierarchy(result, expected)
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_federation_hierarchy_additional_state(self) -> None:
|
||||
"""Federation hierarchy can include requested public state for each room."""
|
||||
event_type = "m.security_labels"
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
body={"labels": ["space-label"]},
|
||||
tok=self.token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
self.room,
|
||||
event_type=event_type,
|
||||
body={"labels": ["room-label"]},
|
||||
tok=self.token,
|
||||
)
|
||||
for room_id in (self.space, self.room):
|
||||
self.helper.send_state(
|
||||
room_id,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {"state_keys": [""]}}},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com",
|
||||
self.space,
|
||||
suggested_only=False,
|
||||
additional_state=(StateEventQuery(event_type, ""),),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result["room"]["org.matrix.msc4507.additional_state"],
|
||||
[
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "",
|
||||
"content": {"labels": ["space-label"]},
|
||||
"sender": self.user,
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
result["children"][0]["org.matrix.msc4507.additional_state"],
|
||||
[
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "",
|
||||
"content": {"labels": ["room-label"]},
|
||||
"sender": self.user,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_federation_hierarchy_additional_state_requires_public_state(
|
||||
self,
|
||||
) -> None:
|
||||
"""Requested state is omitted if the room has not declared it public."""
|
||||
event_type = "m.security_labels"
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
body={"labels": ["secret"]},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com",
|
||||
self.space,
|
||||
suggested_only=False,
|
||||
additional_state=(StateEventQuery(event_type, ""),),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(result["room"]["org.matrix.msc4507.additional_state"], [])
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_federation_hierarchy_additional_state_supports_state_key_wildcard(
|
||||
self,
|
||||
) -> None:
|
||||
"""Omitting state_key requests all public state keys for that type."""
|
||||
event_type = "com.example.some_state"
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
state_key="allowed",
|
||||
body={"value": "allowed"},
|
||||
tok=self.token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
state_key="private",
|
||||
body={"value": "private"},
|
||||
tok=self.token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {}}},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com",
|
||||
self.space,
|
||||
suggested_only=False,
|
||||
additional_state=(StateEventQuery(event_type),),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result["room"]["org.matrix.msc4507.additional_state"],
|
||||
[
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "allowed",
|
||||
"content": {"value": "allowed"},
|
||||
"sender": self.user,
|
||||
},
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "private",
|
||||
"content": {"value": "private"},
|
||||
"sender": self.user,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {"state_keys": ["allowed"]}}},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com",
|
||||
self.space,
|
||||
suggested_only=False,
|
||||
additional_state=(StateEventQuery(event_type),),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result["room"]["org.matrix.msc4507.additional_state"],
|
||||
[
|
||||
{
|
||||
"type": event_type,
|
||||
"state_key": "allowed",
|
||||
"content": {"value": "allowed"},
|
||||
"sender": self.user,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_federation_hierarchy_additional_state_omitted_without_query(
|
||||
self,
|
||||
) -> None:
|
||||
"""The federation hierarchy response is unchanged without the MSC query."""
|
||||
event_type = "m.security_labels"
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
body={"labels": ["public"]},
|
||||
tok=self.token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {"state_keys": [""]}}},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com", self.space, suggested_only=False
|
||||
)
|
||||
)
|
||||
|
||||
self.assertNotIn("org.matrix.msc4507.additional_state", result["room"])
|
||||
|
||||
def test_federation_hierarchy_additional_state_disabled_by_default(
|
||||
self,
|
||||
) -> None:
|
||||
"""The MSC4507 hierarchy response field is gated by experimental config."""
|
||||
event_type = "m.security_labels"
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=event_type,
|
||||
body={"labels": ["public"]},
|
||||
tok=self.token,
|
||||
)
|
||||
self.helper.send_state(
|
||||
self.space,
|
||||
event_type=EventTypes.MSC4507PublicState,
|
||||
body={"public_state": {event_type: {"state_keys": [""]}}},
|
||||
tok=self.token,
|
||||
)
|
||||
|
||||
result = self.get_success(
|
||||
self.handler.get_federation_hierarchy(
|
||||
"remote.example.com",
|
||||
self.space,
|
||||
suggested_only=False,
|
||||
additional_state=(StateEventQuery(event_type, ""),),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertNotIn("org.matrix.msc4507.additional_state", result["room"])
|
||||
|
||||
@override_config({"experimental_features": {"msc4507_enabled": True}})
|
||||
def test_msc4507_public_state_default_power_level(self) -> None:
|
||||
"""New rooms require PL100 to send the MSC4507 public-state event."""
|
||||
state_ids = self.get_success(
|
||||
self.hs.get_storage_controllers().state.get_current_state_ids(self.space)
|
||||
)
|
||||
power_levels = self.get_success(
|
||||
self.hs.get_datastores().main.get_event(
|
||||
state_ids[(EventTypes.PowerLevels, "")]
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
power_levels.content["events"][EventTypes.MSC4507PublicState], 100
|
||||
)
|
||||
|
||||
def test_msc4507_public_state_default_power_level_disabled_by_default(
|
||||
self,
|
||||
) -> None:
|
||||
"""The MSC4507 public-state PL rule is gated by experimental config."""
|
||||
state_ids = self.get_success(
|
||||
self.hs.get_storage_controllers().state.get_current_state_ids(self.space)
|
||||
)
|
||||
power_levels = self.get_success(
|
||||
self.hs.get_datastores().main.get_event(
|
||||
state_ids[(EventTypes.PowerLevels, "")]
|
||||
)
|
||||
)
|
||||
|
||||
self.assertNotIn(EventTypes.MSC4507PublicState, power_levels.content["events"])
|
||||
|
||||
@override_config({"rc_room_creation": {"burst_count": 1000, "per_second": 1}})
|
||||
def test_large_space(self) -> None:
|
||||
"""Test a space with a large number of rooms."""
|
||||
@@ -1024,6 +1335,7 @@ class SpaceSummaryTestCase(unittest.HomeserverTestCase):
|
||||
destination: str,
|
||||
room_id: str,
|
||||
suggested_only: bool,
|
||||
additional_state: Iterable[StateEventQuery] = (),
|
||||
) -> JsonDict:
|
||||
nonlocal federation_requests
|
||||
federation_requests += 1
|
||||
|
||||
Reference in New Issue
Block a user