Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pytest-httpx for openSUSE:Factory checked in at 2023-01-17 17:35:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest-httpx (Old) and /work/SRC/openSUSE:Factory/.python-pytest-httpx.new.32243 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-httpx" Tue Jan 17 17:35:27 2023 rev:4 rq:1058836 version:0.21.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest-httpx/python-pytest-httpx.changes 2022-08-08 10:02:33.362376869 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest-httpx.new.32243/python-pytest-httpx.changes 2023-01-17 17:35:41.517312709 +0100 @@ -1,0 +2,10 @@ +Mon Jan 16 16:21:19 UTC 2023 - Daniel Garcia <daniel.gar...@suse.com> + +- Update to 0.21.2 + * URL containing non ASCII characters in query can now be matched. + * Requests are now cleared when calling httpx_mock.reset. +- 0.21.1 + * httpx_mock.add_callback now handles async callbacks. +- Unpin httpx and pytest in python metadata + +------------------------------------------------------------------- Old: ---- pytest_httpx-0.21.0-gh.tar.gz New: ---- pytest_httpx-0.21.2-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest-httpx.spec ++++++ --- /var/tmp/diff_new_pack.qiI9eS/_old 2023-01-17 17:35:42.069315911 +0100 +++ /var/tmp/diff_new_pack.qiI9eS/_new 2023-01-17 17:35:42.073315935 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pytest-httpx # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-pytest-httpx -Version: 0.21.0 +Version: 0.21.2 Release: 0 Summary: Send responses to httpx License: MIT @@ -29,7 +29,7 @@ # SECTION test requirements BuildRequires: %{python_module httpx >= 0.23.0} BuildRequires: %{python_module pytest >= 6.0} -BuildRequires: %{python_module pytest-asyncio >= 0.14.0} +BuildRequires: %{python_module pytest-asyncio >= 0.20.0} # /SECTION BuildRequires: fdupes Requires: python-httpx >= 0.23.0 @@ -43,7 +43,8 @@ %prep %setup -q -n pytest_httpx-%{version} # unpin exact version -sed -i '/install_requires/ s/httpx==0./httpx>=0./' setup.py +sed -i '/install_requires/ s/httpx==0.23.\*/httpx/' setup.py +sed -i '/install_requires/ s/pytest>=6.*,<8.\*/pytest/' setup.py %build %python_build ++++++ pytest_httpx-0.21.0-gh.tar.gz -> pytest_httpx-0.21.2-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/.pre-commit-config.yaml new/pytest_httpx-0.21.2/.pre-commit-config.yaml --- old/pytest_httpx-0.21.0/.pre-commit-config.yaml 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/.pre-commit-config.yaml 2022-11-03 22:03:01.000000000 +0100 @@ -1,5 +1,5 @@ repos: - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.10.0 hooks: - id: black \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/CHANGELOG.md new/pytest_httpx-0.21.2/CHANGELOG.md --- old/pytest_httpx-0.21.0/CHANGELOG.md 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/CHANGELOG.md 2022-11-03 22:03:01.000000000 +0100 @@ -6,6 +6,15 @@ ## [Unreleased] +## [0.21.2] - 2022-11-03 +### Fixed +- URL containing non ASCII characters in query can now be matched. +- Requests are now cleared when calling `httpx_mock.reset`. + +## [0.21.1] - 2022-10-20 +### Fixed +- `httpx_mock.add_callback` now handles async callbacks. + ## [0.21.0] - 2022-05-24 ### Changed - Requires [`httpx`](https://www.python-httpx.org)==0.23.\* @@ -223,7 +232,9 @@ ### Added - First release, should be considered as unstable for now as design might change. -[Unreleased]: https://github.com/Colin-b/pytest_httpx/compare/v0.21.0...HEAD +[Unreleased]: https://github.com/Colin-b/pytest_httpx/compare/v0.21.2...HEAD +[0.21.2]: https://github.com/Colin-b/pytest_httpx/compare/v0.21.1...v0.21.2 +[0.21.1]: https://github.com/Colin-b/pytest_httpx/compare/v0.21.0...v0.21.1 [0.21.0]: https://github.com/Colin-b/pytest_httpx/compare/v0.20.0...v0.21.0 [0.20.0]: https://github.com/Colin-b/pytest_httpx/compare/v0.19.0...v0.20.0 [0.19.0]: https://github.com/Colin-b/pytest_httpx/compare/v0.18.0...v0.19.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/CONTRIBUTING.md new/pytest_httpx-0.21.2/CONTRIBUTING.md --- old/pytest_httpx-0.21.0/CONTRIBUTING.md 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/CONTRIBUTING.md 2022-11-03 22:03:01.000000000 +0100 @@ -56,7 +56,7 @@ 1) Go to the *Pull requests* tab and click on the *New pull request* button. 2) *base* should always be set to `develop` and it should be compared to your branch. 3) Title should be a small sentence describing the request. -3) The comment should contain as much information as possible +4) The comment should contain as much information as possible * Actual behavior (before the new code) * Expected behavior (with the new code) * Steps to reproduce (with and without the new code to see the difference) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/README.md new/pytest_httpx-0.21.2/README.md --- old/pytest_httpx-0.21.0/README.md 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/README.md 2022-11-03 22:03:01.000000000 +0100 @@ -5,7 +5,7 @@ <a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Build status" src="https://github.com/Colin-b/pytest_httpx/workflows/Release/badge.svg"></a> <a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Coverage" src="https://img.shields.io/badge/coverage-100%25-brightgreen"></a> <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a> -<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Number of tests" src="https://img.shields.io/badge/tests-153 passed-blue"></a> +<a href="https://github.com/Colin-b/pytest_httpx/actions"><img alt="Number of tests" src="https://img.shields.io/badge/tests-168 passed-blue"></a> <a href="https://pypi.org/project/pytest-httpx/"><img alt="Number of downloads" src="https://img.shields.io/pypi/dm/pytest_httpx"></a> </p> @@ -444,6 +444,38 @@ ``` +Alternatively, callbacks can also be asynchronous. + +As in the following sample simulating network latency on some responses only. + +```python +import asyncio +import httpx +import pytest +from pytest_httpx import HTTPXMock + + +@pytest.mark.asyncio +async def test_dynamic_async_response(httpx_mock: HTTPXMock): + async def simulate_network_latency(request: httpx.Request): + await asyncio.sleep(1) + return httpx.Response( + status_code=200, json={"url": str(request.url)}, + ) + + httpx_mock.add_callback(simulate_network_latency) + httpx_mock.add_response() + + async with httpx.AsyncClient() as client: + responses = await asyncio.gather( + # Response will be received after one second + client.get("https://test_url"), + # Response will instantly be received (1 second before the first request) + client.get("https://test_url") + ) + +``` + ### Raising exceptions You can simulate HTTPX exception throwing by raising an exception in your callback or use `httpx_mock.add_exception` with the exception instance. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/pytest_httpx/_httpx_mock.py new/pytest_httpx-0.21.2/pytest_httpx/_httpx_mock.py --- old/pytest_httpx-0.21.0/pytest_httpx/_httpx_mock.py 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/pytest_httpx/_httpx_mock.py 2022-11-03 22:03:01.000000000 +0100 @@ -1,14 +1,11 @@ +import inspect import re -from typing import List, Union, Optional, Callable, Tuple, Pattern, Any, Dict -from urllib.parse import parse_qs +from typing import List, Union, Optional, Callable, Tuple, Pattern, Any, Dict, Awaitable import httpx from pytest_httpx import _httpx_internals -# re.Pattern was introduced in Python 3.7 -pattern_type = re._pattern_type if hasattr(re, "_pattern_type") else re.Pattern - class _RequestMatcher: def __init__( @@ -36,18 +33,18 @@ if not self.url: return True - if isinstance(self.url, pattern_type): + if isinstance(self.url, re.Pattern): return self.url.match(str(request.url)) is not None # Compare query parameters apart as order of parameters should not matter - request_qs = parse_qs(request.url.query) - qs = parse_qs(self.url.query) + request_params = dict(request.url.params) + params = dict(self.url.params) # Remove the query parameters from the original URL to compare everything besides query parameters request_url = request.url.copy_with(query=None) url = self.url.copy_with(query=None) - return (request_qs == qs) and (url == request_url) + return (request_params == params) and (url == request_url) def _method_match(self, request: httpx.Request) -> bool: if not self.method: @@ -89,7 +86,12 @@ self._callbacks: List[ Tuple[ _RequestMatcher, - Callable[[httpx.Request], Optional[httpx.Response]], + Callable[ + [httpx.Request], + Union[ + Optional[httpx.Response], Awaitable[Optional[httpx.Response]] + ], + ], ] ] = [] @@ -135,7 +137,12 @@ self.add_callback(lambda request: response, **matchers) def add_callback( - self, callback: Callable[[httpx.Request], Optional[httpx.Response]], **matchers + self, + callback: Callable[ + [httpx.Request], + Union[Optional[httpx.Response], Awaitable[Optional[httpx.Response]]], + ], + **matchers, ) -> None: """ Mock the action that will take place if a request match. @@ -180,12 +187,26 @@ response = callback(request) if response: - # Allow to read the response on client side - response.is_stream_consumed = False - response.is_closed = False - if hasattr(response, "_content"): - del response._content - return response + return _unread(response) + + raise httpx.TimeoutException( + self._explain_that_no_response_was_found(request), request=request + ) + + async def _handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + self._requests.append(request) + + callback = self._get_callback(request) + if callback: + response = callback(request) + + if response: + if inspect.isawaitable(response): + response = await response + return _unread(response) raise httpx.TimeoutException( self._explain_that_no_response_was_found(request), request=request @@ -221,7 +242,12 @@ def _get_callback( self, request: httpx.Request - ) -> Optional[Callable[[httpx.Request], Optional[httpx.Response]]]: + ) -> Optional[ + Callable[ + [httpx.Request], + Union[Optional[httpx.Response], Awaitable[Optional[httpx.Response]]], + ] + ]: callbacks = [ (matcher, callback) for matcher, callback in self._callbacks @@ -274,6 +300,7 @@ return requests[0] if requests else None def reset(self, assert_all_responses_were_requested: bool) -> None: + self._requests.clear() not_called = self._reset_callbacks() if assert_all_responses_were_requested: @@ -304,4 +331,13 @@ self.mock = mock async def handle_async_request(self, *args, **kwargs) -> httpx.Response: - return self.mock._handle_request(*args, **kwargs) + return await self.mock._handle_async_request(*args, **kwargs) + + +def _unread(response: httpx.Response) -> httpx.Response: + # Allow to read the response on client side + response.is_stream_consumed = False + response.is_closed = False + if hasattr(response, "_content"): + del response._content + return response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/pytest_httpx/version.py new/pytest_httpx-0.21.2/pytest_httpx/version.py --- old/pytest_httpx-0.21.0/pytest_httpx/version.py 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/pytest_httpx/version.py 2022-11-03 22:03:01.000000000 +0100 @@ -3,4 +3,4 @@ # Major should be incremented in case there is a breaking change. (eg: 2.5.8 -> 3.0.0) # Minor should be incremented in case there is an enhancement. (eg: 2.5.8 -> 2.6.0) # Patch should be incremented in case there is a bug fix. (eg: 2.5.8 -> 2.5.9) -__version__ = "0.21.0" +__version__ = "0.21.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/setup.py new/pytest_httpx-0.21.2/setup.py --- old/pytest_httpx-0.21.0/setup.py 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/setup.py 2022-11-03 22:03:01.000000000 +0100 @@ -44,9 +44,9 @@ extras_require={ "testing": [ # Used to run async test functions - "pytest-asyncio==0.17.*", + "pytest-asyncio==0.20.*", # Used to check coverage - "pytest-cov==3.*", + "pytest-cov==4.*", ] }, python_requires=">=3.7", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/tests/test_httpx_async.py new/pytest_httpx-0.21.2/tests/test_httpx_async.py --- old/pytest_httpx-0.21.0/tests/test_httpx_async.py 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/tests/test_httpx_async.py 2022-11-03 22:03:01.000000000 +0100 @@ -1,4 +1,7 @@ +import asyncio +import math import re +import time import httpx import pytest @@ -166,7 +169,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover async with client.stream(method="GET", url="https://test_url") as response: assert [part async for part in response.aiter_raw()] == [ @@ -176,7 +179,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover @pytest.mark.asyncio @@ -194,7 +197,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover async with client.stream(method="GET", url="https://test_url") as response: assert [part async for part in response.aiter_raw()] == [ @@ -203,7 +206,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover @pytest.mark.asyncio @@ -221,7 +224,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover async with client.stream(method="GET", url="https://test_url") as response: assert [part async for part in response.aiter_raw()] == [ @@ -230,7 +233,7 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover @pytest.mark.asyncio @@ -243,14 +246,14 @@ # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover async with client.stream(method="GET", url="https://test_url") as response: assert [part async for part in response.aiter_raw()] == [] # Assert that stream still behaves the proper way (can only be consumed once per request) with pytest.raises(httpx.StreamConsumed): async for part in response.aiter_raw(): - pass + pass # pragma: no cover @pytest.mark.asyncio @@ -488,6 +491,66 @@ @pytest.mark.asyncio +async def test_async_callback_with_await_statement(httpx_mock: HTTPXMock) -> None: + async def simulate_network_latency(request: httpx.Request): + await asyncio.sleep(1) + return httpx.Response( + status_code=200, + json={"url": str(request.url), "time": time.time()}, + ) + + def instant_response(request: httpx.Request) -> httpx.Response: + return httpx.Response( + status_code=200, json={"url": str(request.url), "time": time.time()} + ) + + httpx_mock.add_callback(simulate_network_latency) + httpx_mock.add_callback(instant_response) + httpx_mock.add_response(json={"url": "not a callback"}) + + async with httpx.AsyncClient() as client: + responses = await asyncio.gather( + client.get("https://slow"), + client.get("https://fast_with_callback"), + client.get("https://fast_with_response"), + ) + slow_response = responses[0].json() + assert slow_response["url"] == "https://slow" + + fast_callback_response = responses[1].json() + assert fast_callback_response["url"] == "https://fast_with_callback" + + fast_response = responses[2].json() + assert fast_response["url"] == "not a callback" + + # Ensure slow request was properly awaited (did not block subsequent async queries) + assert math.isclose(slow_response["time"], fast_callback_response["time"] + 1) + + +@pytest.mark.asyncio +async def test_async_callback_with_pattern_in_url(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json={"url": str(request.url)}) + + async def custom_response2(request: httpx.Request) -> httpx.Response: + return httpx.Response( + status_code=200, + extensions={"http_version": b"HTTP/2.0"}, + json={"url": str(request.url)}, + ) + + httpx_mock.add_callback(custom_response, url=re.compile(".*test.*")) + httpx_mock.add_callback(custom_response2, url="https://unmatched") + + async with httpx.AsyncClient() as client: + response = await client.get("https://unmatched") + assert response.http_version == "HTTP/2.0" + + response = await client.get("https://test_url") + assert response.http_version == "HTTP/1.1" + + +@pytest.mark.asyncio async def test_with_many_responses_urls_instances(httpx_mock: HTTPXMock) -> None: httpx_mock.add_response( url=httpx.URL("https://test_url", params={"param1": "test"}), @@ -763,6 +826,22 @@ @pytest.mark.asyncio +async def test_async_callback_raising_exception(httpx_mock: HTTPXMock) -> None: + async def raise_timeout(request: httpx.Request) -> httpx.Response: + raise httpx.ReadTimeout( + f"Unable to read within {request.extensions['timeout']['read']}", + request=request, + ) + + httpx_mock.add_callback(raise_timeout, url="https://test_url") + + async with httpx.AsyncClient() as client: + with pytest.raises(httpx.ReadTimeout) as exception_info: + await client.get("https://test_url") + assert str(exception_info.value) == "Unable to read within 5.0" + + +@pytest.mark.asyncio async def test_request_exception_raising(httpx_mock: HTTPXMock) -> None: httpx_mock.add_exception( httpx.ReadTimeout("Unable to read within 5.0"), url="https://test_url" @@ -801,6 +880,19 @@ @pytest.mark.asyncio +async def test_async_callback_returning_response(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json={"url": str(request.url)}) + + httpx_mock.add_callback(custom_response, url="https://test_url") + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.json() == {"url": "https://test_url"} + assert response.headers["content-type"] == "application/json" + + +@pytest.mark.asyncio async def test_callback_executed_twice(httpx_mock: HTTPXMock) -> None: def custom_response(request: httpx.Request) -> httpx.Response: return httpx.Response(status_code=200, json=["content"]) @@ -818,6 +910,23 @@ @pytest.mark.asyncio +async def test_async_callback_executed_twice(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json=["content"]) + + httpx_mock.add_callback(custom_response) + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.json() == ["content"] + assert response.headers["content-type"] == "application/json" + + response = await client.post("https://test_url") + assert response.json() == ["content"] + assert response.headers["content-type"] == "application/json" + + +@pytest.mark.asyncio async def test_callback_registered_after_response(httpx_mock: HTTPXMock) -> None: def custom_response(request: httpx.Request) -> httpx.Response: return httpx.Response(status_code=200, json=["content2"]) @@ -841,6 +950,29 @@ @pytest.mark.asyncio +async def test_async_callback_registered_after_response(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json=["content2"]) + + httpx_mock.add_response(json=["content1"]) + httpx_mock.add_callback(custom_response) + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.json() == ["content1"] + assert response.headers["content-type"] == "application/json" + + response = await client.post("https://test_url") + assert response.json() == ["content2"] + assert response.headers["content-type"] == "application/json" + + # Assert that the last registered callback is sent again even if there is a response + response = await client.post("https://test_url") + assert response.json() == ["content2"] + assert response.headers["content-type"] == "application/json" + + +@pytest.mark.asyncio async def test_response_registered_after_callback(httpx_mock: HTTPXMock) -> None: def custom_response(request: httpx.Request) -> httpx.Response: return httpx.Response(status_code=200, json=["content1"]) @@ -864,6 +996,29 @@ @pytest.mark.asyncio +async def test_response_registered_after_async_callback(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json=["content1"]) + + httpx_mock.add_callback(custom_response) + httpx_mock.add_response(json=["content2"]) + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.json() == ["content1"] + assert response.headers["content-type"] == "application/json" + + response = await client.post("https://test_url") + assert response.json() == ["content2"] + assert response.headers["content-type"] == "application/json" + + # Assert that the last registered response is sent again even if there is a callback + response = await client.post("https://test_url") + assert response.json() == ["content2"] + assert response.headers["content-type"] == "application/json" + + +@pytest.mark.asyncio async def test_callback_matching_method(httpx_mock: HTTPXMock) -> None: def custom_response(request: httpx.Request) -> httpx.Response: return httpx.Response(status_code=200, json=["content"]) @@ -880,6 +1035,23 @@ assert response.headers["content-type"] == "application/json" +@pytest.mark.asyncio +async def test_async_callback_matching_method(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json=["content"]) + + httpx_mock.add_callback(custom_response, method="GET") + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.json() == ["content"] + assert response.headers["content-type"] == "application/json" + + response = await client.get("https://test_url2") + assert response.json() == ["content"] + assert response.headers["content-type"] == "application/json" + + def test_request_retrieval_with_more_than_one(testdir: Testdir) -> None: """ Single request cannot be returned if there is more than one matching. @@ -1485,3 +1657,45 @@ async with httpx.AsyncClient() as client: response = await client.get("https://test_url") assert response.elapsed is not None + + +@pytest.mark.asyncio +async def test_elapsed_when_add_async_callback(httpx_mock: HTTPXMock) -> None: + async def custom_response(request: httpx.Request) -> httpx.Response: + return httpx.Response(status_code=200, json={"foo": "bar"}) + + httpx_mock.add_callback(custom_response) + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url") + assert response.elapsed is not None + + +@pytest.mark.asyncio +async def test_non_ascii_url_response(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response(url="https://test_url?query_type=æ°æ®") + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url?query_type=æ°æ®") + assert response.content == b"" + + +@pytest.mark.asyncio +async def test_url_encoded_matching_response(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response(url="https://test_url?a=%E6%95%B0%E6%8D%AE") + + async with httpx.AsyncClient() as client: + response = await client.get("https://test_url?a=æ°æ®") + assert response.content == b"" + + +@pytest.mark.asyncio +async def test_reset_is_removing_requests(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response() + async with httpx.AsyncClient() as client: + await client.get("https://test_url") + + assert len(httpx_mock.get_requests()) == 1 + + httpx_mock.reset(assert_all_responses_were_requested=False) + assert len(httpx_mock.get_requests()) == 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest_httpx-0.21.0/tests/test_httpx_sync.py new/pytest_httpx-0.21.2/tests/test_httpx_sync.py --- old/pytest_httpx-0.21.0/tests/test_httpx_sync.py 2022-05-24 18:29:06.000000000 +0200 +++ new/pytest_httpx-0.21.2/tests/test_httpx_sync.py 2022-11-03 22:03:01.000000000 +0100 @@ -1383,3 +1383,30 @@ with httpx.Client() as client: response = client.get("https://test_url") assert response.elapsed is not None + + +def test_non_ascii_url_response(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response(url="https://test_url?query_type=æ°æ®") + + with httpx.Client() as client: + response = client.get("https://test_url?query_type=æ°æ®") + assert response.content == b"" + + +def test_url_encoded_matching_response(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response(url="https://test_url?a=%E6%95%B0%E6%8D%AE") + + with httpx.Client() as client: + response = client.get("https://test_url?a=æ°æ®") + assert response.content == b"" + + +def test_reset_is_removing_requests(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response() + with httpx.Client() as client: + client.get("https://test_url") + + assert len(httpx_mock.get_requests()) == 1 + + httpx_mock.reset(assert_all_responses_were_requested=False) + assert len(httpx_mock.get_requests()) == 0