From e4852b3eb30a923fa7d89bb9aaf2553092880ec4 Mon Sep 17 00:00:00 2001 From: Ron Smeral Date: Wed, 13 Nov 2024 19:01:01 +0100 Subject: [PATCH 1/3] Match any network filter instead of all --- src/pook/engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pook/engine.py b/src/pook/engine.py index 811e1b0..40b5c33 100644 --- a/src/pook/engine.py +++ b/src/pook/engine.py @@ -382,7 +382,7 @@ def should_use_network(self, request): Returns: bool """ - return self.networking and all(fn(request) for fn in self.network_filters) + return self.networking and any(fn(request) for fn in self.network_filters) def match(self, request): """ From a8999874695b62bc8c425b23d44aac87238542da Mon Sep 17 00:00:00 2001 From: Ron Smeral Date: Thu, 14 Nov 2024 15:34:28 +0100 Subject: [PATCH 2/3] Add test_network_mode_multiple_hosts --- tests/unit/interceptors/base.py | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/unit/interceptors/base.py b/tests/unit/interceptors/base.py index e6537a0..763dd0f 100644 --- a/tests/unit/interceptors/base.py +++ b/tests/unit/interceptors/base.py @@ -6,6 +6,7 @@ import pytest import pook +from pook.exceptions import PookNoMatches class StandardTests: @@ -86,6 +87,46 @@ def test_network_mode(self, url_404, url_500): assert status == 500 + @pytest.mark.pook + @pytest.mark.parametrize( + "allowed_hosts", + [ + ["mocked.nonexistent"], + ["mocked.nonexistent", "definitely.nonexistent"], + ["mocked.nonexistent", "definitely.nonexistent", "also.nonexistent"], + ], + ) + def test_network_mode_multiple_hosts(self, allowed_hosts): + """Enabling network mode with multiple hosts allows requests to pass for any of the hosts.""" + + pook.enable_network(*allowed_hosts) + + disallowed_host = "disallowed.nonexistent" + + # Ensure disallowed hosts are properly mocked. + mocked_disallowed_host_url = f"http://{disallowed_host}/mocked/path" + pook.get(mocked_disallowed_host_url).reply(200).body("hello from pook") + status, *_ = self.make_request("GET", mocked_disallowed_host_url) + assert status == 200 + + # Ensure unmocked non-allowed hosts can't be reached. + with pytest.raises(PookNoMatches): + self.make_request("GET", f"http://{disallowed_host}/some/unmocked/path") + + # Ensure we can still mock paths on the allowed hosts. + mocked_allowed_host_url = "http://mocked.nonexistent/mocked/path" + pook.get(mocked_allowed_host_url).reply(200).body("hello from pook") + status, *_ = self.make_request("GET", mocked_allowed_host_url) + assert status == 200 + + # Ensure unmocked paths on allowed hosts are not blocked. + # If we don't get PookNoMatches, the filter works. + for allowed_host in allowed_hosts: + try: + self.make_request("GET", f"http://{allowed_host}/some/unmocked/path") + except Exception as err: + assert not isinstance(err, PookNoMatches) + @pytest.mark.pook def test_json_request(self, url_404): """JSON request bodies are correctly matched.""" From b71513940daf54017dbc90ccbd96e54869cf9164 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Thu, 21 Nov 2024 10:03:25 +1100 Subject: [PATCH 3/3] Fix networking mode tests --- src/pook/engine.py | 11 +++- tests/unit/interceptors/base.py | 97 +++++++++++++++++++++------------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/src/pook/engine.py b/src/pook/engine.py index 40b5c33..979f2d0 100644 --- a/src/pook/engine.py +++ b/src/pook/engine.py @@ -382,7 +382,16 @@ def should_use_network(self, request): Returns: bool """ - return self.networking and any(fn(request) for fn in self.network_filters) + if not self.networking: + return False + + if not self.network_filters: + # networking is enabled, and there are no filters, so + # all unmatching requests should be allowed + return True + + # Otherwise, only allow if at least one of the network filters matches + return any(fn(request) for fn in self.network_filters) def match(self, request): """ diff --git a/tests/unit/interceptors/base.py b/tests/unit/interceptors/base.py index 763dd0f..390efae 100644 --- a/tests/unit/interceptors/base.py +++ b/tests/unit/interceptors/base.py @@ -60,6 +60,10 @@ def url_404(self, httpbin): def url_500(self, httpbin): return f"{httpbin.url}/status/500" + @pytest.fixture + def url_401(self, httpbin): + return httpbin + "/status/401" + @pytest.mark.pook def test_activate_deactivate(self, url_404): """Deactivating pook allows requests to go through.""" @@ -87,45 +91,68 @@ def test_network_mode(self, url_404, url_500): assert status == 500 - @pytest.mark.pook - @pytest.mark.parametrize( - "allowed_hosts", - [ - ["mocked.nonexistent"], - ["mocked.nonexistent", "definitely.nonexistent"], - ["mocked.nonexistent", "definitely.nonexistent", "also.nonexistent"], - ], - ) - def test_network_mode_multiple_hosts(self, allowed_hosts): - """Enabling network mode with multiple hosts allows requests to pass for any of the hosts.""" - - pook.enable_network(*allowed_hosts) - - disallowed_host = "disallowed.nonexistent" - - # Ensure disallowed hosts are properly mocked. - mocked_disallowed_host_url = f"http://{disallowed_host}/mocked/path" - pook.get(mocked_disallowed_host_url).reply(200).body("hello from pook") - status, *_ = self.make_request("GET", mocked_disallowed_host_url) - assert status == 200 + mocked_status, mocked_body, *_ = self.make_request("GET", url_404) + + assert mocked_status == 200 + assert mocked_body == b"hello from pook" + + @pytest.mark.pook(allow_pending_mocks=True) + def test_network_mode_hostname(self, url_401): + example_com = "http://example.com" + pook.get(example_com).header("x-pook", "1").reply(200).body("hello from pook") + # httpbin runs on loopback + pook.enable_network("127.0.0.1") + + httpbin_status, *_ = self.make_request("GET", url_401) + + # network is enabled for httpbin hostname so it goes through + assert httpbin_status == 401 - # Ensure unmocked non-allowed hosts can't be reached. with pytest.raises(PookNoMatches): - self.make_request("GET", f"http://{disallowed_host}/some/unmocked/path") + # Make the request without query params to avoid matching the mock + # which should raise a no match exception, as network mode is not enabled + # for example.com hostname + self.make_request("GET", example_com) + + # this matches the mock on the header, so gets 200 with the hello from pook body + example_status, body, *_ = self.make_request( + "GET", example_com, headers=[("x-pook", "1")] + ) - # Ensure we can still mock paths on the allowed hosts. - mocked_allowed_host_url = "http://mocked.nonexistent/mocked/path" - pook.get(mocked_allowed_host_url).reply(200).body("hello from pook") - status, *_ = self.make_request("GET", mocked_allowed_host_url) - assert status == 200 + assert example_status == 200 + assert body == b"hello from pook" - # Ensure unmocked paths on allowed hosts are not blocked. - # If we don't get PookNoMatches, the filter works. - for allowed_host in allowed_hosts: - try: - self.make_request("GET", f"http://{allowed_host}/some/unmocked/path") - except Exception as err: - assert not isinstance(err, PookNoMatches) + @pytest.mark.pook(allow_pending_mocks=True) + def test_multiple_network_filters(self, url_401): + """When multiple network filters are added, only one is required to match for the + request to be allowed through the network.""" + + def has_x_header(request: pook.Request): + return request.headers.get("x-pook") == "x" + + def has_y_header(request: pook.Request): + return request.headers.get("x-pook") == "y" + + pook.enable_network() + + pook.use_network_filter(has_x_header, has_y_header) + + pook.get(url_401).header("x-pook", "z").reply(200).body("hello from pook") + + # Network filter matches, so request is allowed despite not matching a mock + x_status, *_ = self.make_request("GET", url_401, headers=[("x-pook", "x")]) + assert x_status == 401 + + # Network filter matches, so request is allowed despite not matching a mock + y_status, *_ = self.make_request("GET", url_401, headers=[("x-pook", "y")]) + assert y_status == 401 + + # Mock matches, so the response is mocked + z_status, z_body, *_ = self.make_request( + "GET", url_401, headers=[("x-pook", "z")] + ) + assert z_status == 200 + assert z_body == b"hello from pook" @pytest.mark.pook def test_json_request(self, url_404):