From 6c34e9d649a8f97630a42bff36ea221cb02341d6 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 19:59:11 +1100 Subject: [PATCH 1/6] Remove unnecessary version checks Pook does not support <3.8; aiohttp now supports 3.12 --- pyproject.toml | 3 --- src/pook/regex.py | 5 +---- tests/integration/examples_test.py | 6 ------ 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e1d39cc..ae086b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,9 +60,6 @@ extra-dependencies = [ "urllib3~=1.24", "httpx~=0.26.0", - # aiohttp depends on multidict, so we can't test aiohttp until - # https://github.com/aio-libs/multidict/issues/887 is resolved - # async-timeout is only used for testing aiohttp "aiohttp~=3.8", "async-timeout~=4.0.3", diff --git a/src/pook/regex.py b/src/pook/regex.py index 1e8dd77..ec22344 100644 --- a/src/pook/regex.py +++ b/src/pook/regex.py @@ -1,10 +1,7 @@ import re import sys -if sys.version_info < (3, 7): - Pattern = type(re.compile("")) -else: - Pattern = re.Pattern +Pattern = re.Pattern def isregex_expr(expr): diff --git a/tests/integration/examples_test.py b/tests/integration/examples_test.py index 463673d..f2f9206 100644 --- a/tests/integration/examples_test.py +++ b/tests/integration/examples_test.py @@ -15,12 +15,6 @@ examples.remove("mocket_example.py") -if sys.version_info >= (3, 12): - # See pyproject.toml note on aiohttp dependency - examples.remove("aiohttp_client.py") - examples.remove("decorator_activate_async.py") - - @pytest.mark.parametrize("example", examples) def test_examples(example): result = subprocess.run(["python", "examples/{}".format(example)]) From fb1e14a64813b09d797c9bfd9f0a4b989ad1c1d5 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 19:59:15 +1100 Subject: [PATCH 2/6] Expose expired mocks that otherwise match the request --- src/pook/engine.py | 18 +++++++----------- src/pook/exceptions.py | 7 ++++++- src/pook/matcher.py | 2 +- src/pook/mock.py | 10 +++++----- tests/unit/exceptions_test.py | 1 - tests/unit/mock_test.py | 28 ++++++++++++++++++++++++++++ 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/pook/engine.py b/src/pook/engine.py index b7dd1bc..1000b6b 100644 --- a/src/pook/engine.py +++ b/src/pook/engine.py @@ -3,7 +3,7 @@ from .mock import Mock from .regex import isregex from .mock_engine import MockEngine -from .exceptions import PookNoMatches, PookExpiredMock +from .exceptions import PookNoMatches class Engine(object): @@ -416,16 +416,12 @@ def match(self, request): # Try to match the request against registered mock definitions for mock in self.mocks[:]: - try: - # Return the first matched HTTP request mock - matches, errors = mock.match(request.copy()) - if len(errors): - match_errors += errors - if matches: - return mock - except PookExpiredMock: - # Remove the mock if already expired - self.mocks.remove(mock) + # Return the first matched HTTP request mock + matches, errors = mock.match(request.copy()) + if len(errors): + match_errors += errors + if matches: + return mock # Validate that we have a mock if not self.should_use_network(request): diff --git a/src/pook/exceptions.py b/src/pook/exceptions.py index b5da0a6..25334d8 100644 --- a/src/pook/exceptions.py +++ b/src/pook/exceptions.py @@ -1,3 +1,6 @@ +import warnings + + class PookInvalidBody(Exception): pass @@ -11,7 +14,9 @@ class PookNetworkFilterError(Exception): class PookExpiredMock(Exception): - pass + def __init__(self, *args, **kwargs): + warnings.warn("PookExpiredMock is deprecated and will be removed in a future version of Pook", DeprecationWarning, stacklevel=2) + super().__init__(*args, **kwargs) class PookInvalidArgument(Exception): diff --git a/src/pook/matcher.py b/src/pook/matcher.py index b86333f..9c03c9f 100644 --- a/src/pook/matcher.py +++ b/src/pook/matcher.py @@ -29,7 +29,7 @@ def match(self, request): request (pook.Request): outgoing request to match. Returns: - tuple(bool, list[Exception]): ``True`` if all matcher tests + tuple(bool, list[str]): ``True`` if all matcher tests passes, otherwise ``False``. Also returns an optional list of error exceptions. """ diff --git a/src/pook/mock.py b/src/pook/mock.py index 246aeb3..3b38cae 100644 --- a/src/pook/mock.py +++ b/src/pook/mock.py @@ -7,7 +7,6 @@ from .request import Request from .matcher import MatcherEngine from .helpers import trigger_methods -from .exceptions import PookExpiredMock from .matchers import init as matcher @@ -750,10 +749,6 @@ def match(self, request): the outgoing HTTP request, otherwise ``False``. Also returns an optional list of error exceptions. """ - # If mock already expired, fail it - if self._times <= 0: - raise PookExpiredMock("Mock expired") - # Trigger mock filters for test in self.filters: if not test(request, self): @@ -772,6 +767,11 @@ def match(self, request): if not matches: return False, errors + if self.isdone(): + return False, [ + f"Mock matches request but is expired.\n{repr(self)}" + ] + # Register matched request for further inspecion and reference self._calls.append(request) diff --git a/tests/unit/exceptions_test.py b/tests/unit/exceptions_test.py index 0c64d2d..9245410 100644 --- a/tests/unit/exceptions_test.py +++ b/tests/unit/exceptions_test.py @@ -4,6 +4,5 @@ def test_exceptions(): assert isinstance(ex.PookNoMatches(), Exception) assert isinstance(ex.PookInvalidBody(), Exception) - assert isinstance(ex.PookExpiredMock(), Exception) assert isinstance(ex.PookNetworkFilterError(), Exception) assert isinstance(ex.PookInvalidArgument(), Exception) diff --git a/tests/unit/mock_test.py b/tests/unit/mock_test.py index 35d342d..51a2cd5 100644 --- a/tests/unit/mock_test.py +++ b/tests/unit/mock_test.py @@ -4,6 +4,7 @@ import pook from pook.mock import Mock from pook.request import Request +from pook.exceptions import PookNoMatches from urllib.request import urlopen @@ -123,3 +124,30 @@ def test_mock_params(url, params, req, expected, mock): def test_new_response(mock): assert mock.reply() != mock.reply(new_response=True, json={}) + + +def test_times(mock): + url = "https://example.com" + mock.url(url) + mock.times(2) + + req = Request(url=url) + + assert mock.match(req) == (True, []) + assert mock.match(req) == (True, []) + assert mock.match(req) == (False, ["ExpiredMatcher: Mock is expired"]) + + +@pytest.mark.pook +def test_times_integrated(httpbin): + url = f"{httpbin.url}/status/404" + pook.get(url).times(2).reply(200).body("hello from pook") + + res = urlopen(url) + assert res.read() == "hello from pook" + + res = urlopen(url) + assert res.read() == "hello from pook" + + with pytest.raises(PookNoMatches, match="Mock matches request but is expired."): + urlopen(url) From 42f4c4f71b0e3030fd81561fb37732330920a979 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 20:07:56 +1100 Subject: [PATCH 3/6] Reduce stacktrace noise in pytest logs --- src/pook/engine.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pook/engine.py b/src/pook/engine.py index 1000b6b..81651bb 100644 --- a/src/pook/engine.py +++ b/src/pook/engine.py @@ -438,7 +438,11 @@ def match(self, request): msg += "\n\n=> Detailed matching errors:\n{}\n".format(err) # Raise no matches exception - raise PookNoMatches(msg) + self.no_matches(msg) # Register unmatched request self.unmatched_reqs.append(request) + + def no_matches(self, msg): + """Raise `PookNoMatches` and reduce pytest printed stacktrace noise""" + raise PookNoMatches(msg) From fd9367b88fc961a6e0a9abeabeca51798fdea2b1 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 20:08:01 +1100 Subject: [PATCH 4/6] Lint --- src/pook/exceptions.py | 6 +++++- src/pook/mock.py | 4 +--- src/pook/regex.py | 1 - tests/integration/examples_test.py | 1 - 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pook/exceptions.py b/src/pook/exceptions.py index 25334d8..931a3a4 100644 --- a/src/pook/exceptions.py +++ b/src/pook/exceptions.py @@ -15,7 +15,11 @@ class PookNetworkFilterError(Exception): class PookExpiredMock(Exception): def __init__(self, *args, **kwargs): - warnings.warn("PookExpiredMock is deprecated and will be removed in a future version of Pook", DeprecationWarning, stacklevel=2) + warnings.warn( + "PookExpiredMock is deprecated and will be removed in a future version of Pook", + DeprecationWarning, + stacklevel=2, + ) super().__init__(*args, **kwargs) diff --git a/src/pook/mock.py b/src/pook/mock.py index 3b38cae..95700f7 100644 --- a/src/pook/mock.py +++ b/src/pook/mock.py @@ -768,9 +768,7 @@ def match(self, request): return False, errors if self.isdone(): - return False, [ - f"Mock matches request but is expired.\n{repr(self)}" - ] + return False, [f"Mock matches request but is expired.\n{repr(self)}"] # Register matched request for further inspecion and reference self._calls.append(request) diff --git a/src/pook/regex.py b/src/pook/regex.py index ec22344..a76b6c1 100644 --- a/src/pook/regex.py +++ b/src/pook/regex.py @@ -1,5 +1,4 @@ import re -import sys Pattern = re.Pattern diff --git a/tests/integration/examples_test.py b/tests/integration/examples_test.py index f2f9206..e0d8e6f 100644 --- a/tests/integration/examples_test.py +++ b/tests/integration/examples_test.py @@ -1,4 +1,3 @@ -import sys import subprocess import pytest from pathlib import Path From 4dd2e3a67cb18af68291167c7ae0995eb285d67a Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 20:15:05 +1100 Subject: [PATCH 5/6] Fix old test --- tests/unit/mock_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit/mock_test.py b/tests/unit/mock_test.py index 51a2cd5..856a5e7 100644 --- a/tests/unit/mock_test.py +++ b/tests/unit/mock_test.py @@ -135,7 +135,11 @@ def test_times(mock): assert mock.match(req) == (True, []) assert mock.match(req) == (True, []) - assert mock.match(req) == (False, ["ExpiredMatcher: Mock is expired"]) + matches, errors = mock.match(req) + assert not matches + assert len(errors) == 1 + assert "Mock matches request but is expired." in errors[0] + assert repr(mock) in errors[0] @pytest.mark.pook From 9384236c95e330e0afb0ddbde57bc3bcdf3eee25 Mon Sep 17 00:00:00 2001 From: sarayourfriend Date: Fri, 29 Mar 2024 20:21:27 +1100 Subject: [PATCH 6/6] Do not treat persistent mocks as expired --- src/pook/mock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pook/mock.py b/src/pook/mock.py index 95700f7..ae606d0 100644 --- a/src/pook/mock.py +++ b/src/pook/mock.py @@ -767,7 +767,7 @@ def match(self, request): if not matches: return False, errors - if self.isdone(): + if self._times <= 0: return False, [f"Mock matches request but is expired.\n{repr(self)}"] # Register matched request for further inspecion and reference