From e2f51b84d094eb2a59a0ce01cdd547f14afb80ca Mon Sep 17 00:00:00 2001 From: Wilhelm Klopp Date: Fri, 13 Jun 2025 09:29:50 +0200 Subject: [PATCH 1/2] Output attempted address in strict mode (#296) * Output attempted address in strict mode * Update test --- mocket/mode.py | 21 +++++++++++++++++---- mocket/socket.py | 2 +- tests/test_mode.py | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mocket/mode.py b/mocket/mode.py index e1da7955..06cd3731 100644 --- a/mocket/mode.py +++ b/mocket/mode.py @@ -31,7 +31,10 @@ def is_allowed(self, location: str | tuple[str, int]) -> bool: return host_allowed or location in self.STRICT_ALLOWED @staticmethod - def raise_not_allowed() -> NoReturn: + def raise_not_allowed( + address: tuple[str, int] | None = None, + data: bytes | None = None, + ) -> NoReturn: current_entries = [ (location, "\n ".join(map(str, entries))) for location, entries in Mocket._entries.items() @@ -39,7 +42,17 @@ def raise_not_allowed() -> NoReturn: formatted_entries = "\n".join( [f" {location}:\n {entries}" for location, entries in current_entries] ) - raise StrictMocketException( - "Mocket tried to use the real `socket` module while STRICT mode was active.\n" - f"Registered entries:\n{formatted_entries}" + msg = ( + "Mocket tried to use the real `socket` module while STRICT mode was active." ) + if address: + host, port = address + msg += f"\nAttempted address: {host}:{port}" + if data: + from mocket.compat import decode_from_bytes + + preview = decode_from_bytes(data).split("\r\n", 1)[0][:200] + msg += f"\nFirst request line: {preview}" + + msg += f"\nRegistered entries:\n{formatted_entries}" + raise StrictMocketException(msg) diff --git a/mocket/socket.py b/mocket/socket.py index 496c9124..cc498a5f 100644 --- a/mocket/socket.py +++ b/mocket/socket.py @@ -229,7 +229,7 @@ def recv(self, buffersize: int, flags: int | None = None) -> bytes: def true_sendall(self, data: bytes, *args: Any, **kwargs: Any) -> bytes: if not MocketMode().is_allowed(self._address): - MocketMode.raise_not_allowed() + MocketMode.raise_not_allowed(self._address, data) # try to get the response from recordings if Mocket._record_storage: diff --git a/tests/test_mode.py b/tests/test_mode.py index ea5905b0..7d4e212d 100644 --- a/tests/test_mode.py +++ b/tests/test_mode.py @@ -52,6 +52,8 @@ def test_strict_mode_error_message(): str(exc_info.value) == """ Mocket tried to use the real `socket` module while STRICT mode was active. +Attempted address: httpbin.local:80 +First request line: GET /ip HTTP/1.1 Registered entries: ('httpbin.local', 80): Entry(method='GET', schema='http', location=('httpbin.local', 80), path='/user.agent', query='') From 1fa45dd359223bb02b7c89c454390f04fbbde445 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Fri, 13 Jun 2025 09:34:32 +0200 Subject: [PATCH 2/2] Make it more HTTP-agnostic. --- mocket/decorators/mocketizer.py | 4 ++-- mocket/mode.py | 7 +++++-- mocket/socket.py | 2 +- tests/test_mode.py | 6 +++--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mocket/decorators/mocketizer.py b/mocket/decorators/mocketizer.py index 4020d52b..fb7c811b 100644 --- a/mocket/decorators/mocketizer.py +++ b/mocket/decorators/mocketizer.py @@ -15,9 +15,9 @@ def __init__( self.instance = instance self.truesocket_recording_dir = truesocket_recording_dir self.namespace = namespace or str(id(self)) - MocketMode().STRICT = strict_mode + MocketMode.STRICT = strict_mode if strict_mode: - MocketMode().STRICT_ALLOWED = strict_mode_allowed or [] + MocketMode.STRICT_ALLOWED = strict_mode_allowed or [] elif strict_mode_allowed: raise ValueError( "Allowed locations are only accepted when STRICT mode is active." diff --git a/mocket/mode.py b/mocket/mode.py index 06cd3731..ac2ca16a 100644 --- a/mocket/mode.py +++ b/mocket/mode.py @@ -9,7 +9,7 @@ from typing import NoReturn -class MocketMode: +class _MocketMode: __shared_state: ClassVar[dict[str, Any]] = {} STRICT: ClassVar = None STRICT_ALLOWED: ClassVar = None @@ -52,7 +52,10 @@ def raise_not_allowed( from mocket.compat import decode_from_bytes preview = decode_from_bytes(data).split("\r\n", 1)[0][:200] - msg += f"\nFirst request line: {preview}" + msg += f"\nSent data: {preview}" msg += f"\nRegistered entries:\n{formatted_entries}" raise StrictMocketException(msg) + + +MocketMode = _MocketMode() diff --git a/mocket/socket.py b/mocket/socket.py index cc498a5f..41b25bcc 100644 --- a/mocket/socket.py +++ b/mocket/socket.py @@ -228,7 +228,7 @@ def recv(self, buffersize: int, flags: int | None = None) -> bytes: raise exc def true_sendall(self, data: bytes, *args: Any, **kwargs: Any) -> bytes: - if not MocketMode().is_allowed(self._address): + if not MocketMode.is_allowed(self._address): MocketMode.raise_not_allowed(self._address, data) # try to get the response from recordings diff --git a/tests/test_mode.py b/tests/test_mode.py index 7d4e212d..bfdb2a79 100644 --- a/tests/test_mode.py +++ b/tests/test_mode.py @@ -53,7 +53,7 @@ def test_strict_mode_error_message(): == """ Mocket tried to use the real `socket` module while STRICT mode was active. Attempted address: httpbin.local:80 -First request line: GET /ip HTTP/1.1 +Sent data: GET /ip HTTP/1.1 Registered entries: ('httpbin.local', 80): Entry(method='GET', schema='http', location=('httpbin.local', 80), path='/user.agent', query='') @@ -69,5 +69,5 @@ def test_strict_mode_false_with_allowed_hosts(): @pytest.mark.parametrize("strict_mode_on", (False, True)) def test_strict_mode_allowed_or_not(strict_mode_on): with Mocketizer(strict_mode=strict_mode_on): - assert MocketMode().is_allowed("foobar.com") is not strict_mode_on - assert MocketMode().is_allowed(("foobar.com", 443)) is not strict_mode_on + assert MocketMode.is_allowed("foobar.com") is not strict_mode_on + assert MocketMode.is_allowed(("foobar.com", 443)) is not strict_mode_on