Add test that required state is immediately returned

This commit is contained in:
Erik Johnston
2026-04-27 11:52:33 +01:00
parent d201b45d51
commit b580c1c7f2
2 changed files with 96 additions and 5 deletions
@@ -2245,3 +2245,69 @@ class SlidingSyncRoomsRequiredStateTestCase(SlidingSyncBase):
response_body["rooms"][room_id]["required_state"][0]["event_id"],
first_event_id,
)
def test_changing_required_state_returns_immediately(self) -> None:
"""Test that if we change the required state, then we return immediately
with the new required state."""
user1_id = self.register_user("user1", "pass")
user1_tok = self.login(user1_id, "pass")
room_id1 = self.helper.create_room_as(user1_id, tok=user1_tok)
# Make an initial sync request with no required state
sync_body = {
"lists": {
"foo-list": {
"ranges": [[0, 1]],
"required_state": [],
"timeline_limit": 0,
}
}
}
response_body, from_token = self.do_sync(sync_body, tok=user1_tok)
# We should see no required state
self.assertIsNone(response_body["rooms"][room_id1].get("required_state"))
# Get the state_map before we change the state as this is the final state we
# expect to see when we update the required state.
state_map = self.get_success(
self.storage_controllers.state.get_current_state(room_id1)
)
# There is no new data, and so making another sync request will block.
channel = self.make_sync_request(
sync_body,
since=from_token,
tok=user1_tok,
timeout_ms=10_000,
await_result=False,
)
self.reactor.advance(0.1) # Allow the request to start processing
self.reactor.advance(9.5)
self.assertFalse(channel.is_finished())
# Advance past the timeout to make sure the request finishes. (We do this
# to ensure log contexts don't leak between tests).
self.reactor.advance(1)
self.assertTrue(channel.is_finished())
# Now update the sliding sync requests to include a required state
# event, and make another sync request.
sync_body["lists"]["foo-list"]["required_state"] = [
[EventTypes.Create, ""],
]
response_body, _ = self.do_sync(
sync_body, since=from_token, tok=user1_tok, timeout_ms=10_000
)
# We should see the new required state immediately without waiting.
self._assertRequiredStateIncludes(
response_body["rooms"][room_id1]["required_state"],
{
state_map[(EventTypes.Create, "")],
},
exact=True,
)
@@ -12,6 +12,7 @@
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#
import logging
import urllib.parse
from typing import Any, Iterable, Literal
from unittest.mock import AsyncMock
@@ -81,7 +82,13 @@ class SlidingSyncBase(unittest.HomeserverTestCase):
return config
def make_sync_request(
self, sync_body: JsonDict, *, since: str | None = None, tok: str
self,
sync_body: JsonDict,
*,
since: str | None = None,
tok: str,
timeout_ms: int | None = None,
await_result: bool = True,
) -> FakeChannel:
"""Make a sliding sync request with given body.
@@ -89,25 +96,40 @@ class SlidingSyncBase(unittest.HomeserverTestCase):
sync_body: The full request body to use
since: Optional since token
tok: Access token to use
timeout_ms: Optional timeout in milliseconds to use for the request.
await_result: Whether to block and wait for the result before returning.
Returns:
A tuple of the response body and the `pos` field.
"""
sync_path = self.sync_endpoint
query_params: dict[str, Any] = {}
if since:
sync_path += f"?pos={since}"
query_params["pos"] = since
if timeout_ms is not None:
query_params["timeout"] = timeout_ms
if query_params:
query_str = urllib.parse.urlencode(query_params)
sync_path += f"?{query_str}"
channel = self.make_request(
method="POST",
path=sync_path,
content=sync_body,
access_token=tok,
await_result=await_result,
)
return channel
def do_sync(
self, sync_body: JsonDict, *, since: str | None = None, tok: str
self,
sync_body: JsonDict,
*,
since: str | None = None,
tok: str,
timeout_ms: int | None = None,
) -> tuple[JsonDict, str]:
"""Do a sliding sync request with given body.
@@ -117,11 +139,14 @@ class SlidingSyncBase(unittest.HomeserverTestCase):
sync_body: The full request body to use
since: Optional since token
tok: Access token to use
timeout_ms: Optional timeout in milliseconds to use for the request.
Returns:
A tuple of the response body and the `pos` field.
"""
channel = self.make_sync_request(sync_body, since=since, tok=tok)
channel = self.make_sync_request(
sync_body, since=since, tok=tok, timeout_ms=timeout_ms
)
self.assertEqual(channel.code, 200, channel.json_body)
return channel.json_body, channel.json_body["pos"]