From 8eae0c7fc5aa99b93252e218f6244283db348ef8 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Sun, 20 Oct 2024 13:06:03 +0200 Subject: [PATCH] `aiohttp` reuses SSLContext instances created at import-time (#259) * `aiohttp` reuses SSLContext instances created at import-time. --- mocket/__init__.py | 11 +++++++++-- mocket/plugins/aiohttp_connector.py | 18 ++++++++++++++++++ tests/test_asyncio.py | 9 +++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 mocket/plugins/aiohttp_connector.py diff --git a/mocket/__init__.py b/mocket/__init__.py index 31cd56fc..8371d554 100644 --- a/mocket/__init__.py +++ b/mocket/__init__.py @@ -1,6 +1,13 @@ from .async_mocket import async_mocketize -from .mocket import Mocket, MocketEntry, Mocketizer, mocketize +from .mocket import FakeSSLContext, Mocket, MocketEntry, Mocketizer, mocketize -__all__ = ("async_mocketize", "mocketize", "Mocket", "MocketEntry", "Mocketizer") +__all__ = ( + "async_mocketize", + "mocketize", + "Mocket", + "MocketEntry", + "Mocketizer", + "FakeSSLContext", +) __version__ = "3.13.1" diff --git a/mocket/plugins/aiohttp_connector.py b/mocket/plugins/aiohttp_connector.py new file mode 100644 index 00000000..353c3af7 --- /dev/null +++ b/mocket/plugins/aiohttp_connector.py @@ -0,0 +1,18 @@ +import contextlib + +from mocket import FakeSSLContext + +with contextlib.suppress(ModuleNotFoundError): + from aiohttp import ClientRequest + from aiohttp.connector import TCPConnector + + class MocketTCPConnector(TCPConnector): + """ + `aiohttp` reuses SSLContext instances created at import-time, + making it more difficult for Mocket to do its job. + This is an attempt to make things smoother, at the cost of + slightly patching the `ClientSession` while testing. + """ + + def _get_ssl_context(self, req: ClientRequest) -> FakeSSLContext: + return FakeSSLContext() diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 59dd474e..bef53009 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -4,10 +4,12 @@ import socket import tempfile +import aiohttp import pytest from mocket import Mocketizer, async_mocketize from mocket.mockhttp import Entry +from mocket.plugins.aiohttp_connector import MocketTCPConnector def test_asyncio_record_replay(event_loop): @@ -45,7 +47,10 @@ async def test_asyncio_connection(): @pytest.mark.asyncio @async_mocketize async def test_aiohttp(): - import aiohttp + """ + The alternative to using the custom `connector` would be importing + `aiohttp` when Mocket is already in control (inside the decorated test). + """ url = "https://bar.foo/" data = {"message": "Hello"} @@ -58,7 +63,7 @@ async def test_aiohttp(): ) async with aiohttp.ClientSession( - timeout=aiohttp.ClientTimeout(total=3) + timeout=aiohttp.ClientTimeout(total=3), connector=MocketTCPConnector() ) as session, session.get(url) as response: response = await response.json() assert response == data