Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose expired mocks that would otherwise match the request #129

Merged
merged 6 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",

Expand Down
24 changes: 12 additions & 12 deletions src/pook/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -442,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)
11 changes: 10 additions & 1 deletion src/pook/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import warnings


class PookInvalidBody(Exception):
pass

Expand All @@ -11,7 +14,13 @@ 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):
Expand Down
2 changes: 1 addition & 1 deletion src/pook/matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down
8 changes: 3 additions & 5 deletions src/pook/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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):
Expand All @@ -772,6 +767,9 @@ def match(self, request):
if not matches:
return False, errors

if self._times <= 0:
return False, [f"Mock matches request but is expired.\n{repr(self)}"]

# Register matched request for further inspecion and reference
self._calls.append(request)

Expand Down
6 changes: 1 addition & 5 deletions src/pook/regex.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
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):
Expand Down
7 changes: 0 additions & 7 deletions tests/integration/examples_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
import subprocess
import pytest
from pathlib import Path
Expand All @@ -15,12 +14,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)])
Expand Down
1 change: 0 additions & 1 deletion tests/unit/exceptions_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
32 changes: 32 additions & 0 deletions tests/unit/mock_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -123,3 +124,34 @@ 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, [])
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
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)
Loading