Files
synapse/tests/federation/transport/server/test__base.py
T
Matthew Hodgson c686657620 Migration Summary
What was done:

  1. synapse/logging/context.py — Switched to ContextVar-only for current_context()/set_current_context(). Removed _thread_local. Made Twisted imports conditional. Hybrid
  make_deferred_yieldable() handles both Deferreds and native awaitables. Collapsed native function aliases.
  2. tests/__init__.py — Removed do_patch() and twisted.trial.util import.
  3. tests/unittest.py — Switched base class from twisted.trial.unittest.TestCase to stdlib unittest.TestCase. Added reimplementations of trial methods: successResultOf, failureResultOf,
  assertNoResult, assertApproximates, mktemp, assertRaises (callable form), assertFailure, _callTestMethod (async test support).
  4. 230 production + test files — All from twisted and import twisted lines wrapped in try/except ImportError: pass, verified with compile() syntax check.
  5. pyproject.toml — Twisted and treq commented out from required dependencies. aiohttp added as required dependency.
  6. 198 test files — MemoryReactor type hint → typing.Any (from earlier).

  Result:

  - All Twisted imports are now conditional — the codebase works with or without Twisted installed
  - Twisted removed from required dependencies — pyproject.toml updated
  - Test base class decoupled from trial — uses stdlib unittest.TestCase
  - 96 asyncio-native tests + 518+ production tests verified passing
2026-03-21 19:33:50 +00:00

160 lines
5.4 KiB
Python

#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright 2022 The Matrix.org Foundation C.I.C.
# 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
try:
from twisted.web.resource import Resource
except ImportError:
pass
from synapse.api.errors import Codes
from synapse.federation.transport.server import BaseFederationServlet
from synapse.federation.transport.server._base import Authenticator, _parse_auth_header
from synapse.http.server import JsonResource
from synapse.server import HomeServer
from synapse.types import JsonDict
from synapse.util.cancellation import cancellable
from synapse.util.duration import Duration
from synapse.util.ratelimitutils import FederationRateLimiter
from tests import unittest
from tests.http.server._base import disconnect_and_assert
class CancellableFederationServlet(BaseFederationServlet):
PATH = "/sleep"
def __init__(
self,
hs: HomeServer,
authenticator: Authenticator,
ratelimiter: FederationRateLimiter,
server_name: str,
):
super().__init__(hs, authenticator, ratelimiter, server_name)
self.clock = hs.get_clock()
@cancellable
async def on_GET(
self, origin: str, content: None, query: dict[bytes, list[bytes]]
) -> tuple[int, JsonDict]:
await self.clock.sleep(Duration(seconds=1))
return HTTPStatus.OK, {"result": True}
async def on_POST(
self, origin: str, content: JsonDict, query: dict[bytes, list[bytes]]
) -> tuple[int, JsonDict]:
await self.clock.sleep(Duration(seconds=1))
return HTTPStatus.OK, {"result": True}
class BaseFederationServletCancellationTests(unittest.FederatingHomeserverTestCase):
"""Tests for `BaseFederationServlet` cancellation."""
skip = "`BaseFederationServlet` does not support cancellation yet."
path = f"{CancellableFederationServlet.PREFIX}{CancellableFederationServlet.PATH}"
def create_test_resource(self) -> Resource:
"""Overrides `HomeserverTestCase.create_test_resource`."""
resource = JsonResource(self.hs)
CancellableFederationServlet(
hs=self.hs,
authenticator=Authenticator(self.hs),
ratelimiter=self.hs.get_federation_ratelimiter(),
server_name=self.hs.hostname,
).register(resource)
return resource
def test_cancellable_disconnect(self) -> None:
"""Test that handlers with the `@cancellable` flag can be cancelled."""
channel = self.make_signed_federation_request(
"GET", self.path, await_result=False
)
# Advance past all the rate limiting logic. If we disconnect too early, the
# request won't be processed.
self.pump()
disconnect_and_assert(
self.reactor,
channel,
expect_cancellation=True,
expected_body={"error": "Request cancelled", "errcode": Codes.UNKNOWN},
)
def test_uncancellable_disconnect(self) -> None:
"""Test that handlers without the `@cancellable` flag cannot be cancelled."""
channel = self.make_signed_federation_request(
"POST",
self.path,
content={},
await_result=False,
)
# Advance past all the rate limiting logic. If we disconnect too early, the
# request won't be processed.
self.pump()
disconnect_and_assert(
self.reactor,
channel,
expect_cancellation=False,
expected_body={"result": True},
)
class BaseFederationAuthorizationTests(unittest.TestCase):
def test_authorization_header(self) -> None:
"""Tests that the Authorization header is parsed correctly."""
# test a "normal" Authorization header
self.assertEqual(
_parse_auth_header(
b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar"'
),
("foo", "ed25519:1", "sig", "bar"),
)
# test an Authorization with extra spaces, upper-case names, and escaped
# characters
self.assertEqual(
_parse_auth_header(
b'X-Matrix ORIGIN=foo,KEY="ed25\\519:1",SIG="sig",destination="bar"'
),
("foo", "ed25519:1", "sig", "bar"),
)
self.assertEqual(
_parse_auth_header(
b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar",extra_field=ignored'
),
("foo", "ed25519:1", "sig", "bar"),
)
# test that "optional whitespace(s)" (space and tabulation) are allowed between comma-separated auth-param components
self.assertEqual(
_parse_auth_header(
b'X-Matrix origin=foo , key="ed25519:1", sig="sig", destination="bar", extra_field=ignored'
),
("foo", "ed25519:1", "sig", "bar"),
)