Hello community, here is the log from the commit of package python-responses for openSUSE:Factory checked in at 2020-03-27 00:27:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-responses (Old) and /work/SRC/openSUSE:Factory/.python-responses.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-responses" Fri Mar 27 00:27:25 2020 rev:11 rq:786429 version:0.10.12 Changes: -------- --- /work/SRC/openSUSE:Factory/python-responses/python-responses.changes 2019-11-04 17:06:28.396265414 +0100 +++ /work/SRC/openSUSE:Factory/.python-responses.new.3160/python-responses.changes 2020-03-27 00:27:28.164322817 +0100 @@ -1,0 +2,22 @@ +Thu Mar 19 08:22:52 UTC 2020 - pgaj...@suse.com + +- version update to 0.10.12 + - Fixed incorrect content-type in `add_callback()` when headers are provided as a list of tuples. + - Fixed invalid README formatted. + - Fixed string formatting in error message. + - Added Python 3.8 support + - Remove Python 3.4 from test suite matrix. + - The `response.request` object now has a `params` attribute that contains the query string parameters from the request that was captured. + - `add_passthru` now supports `re` pattern objects to match URLs. + - ConnectionErrors raised by responses now include more details on the request that was attempted and the mocks registered. + - Fixed regression with `add_callback()` and content-type header. + - Fixed implicit dependency on urllib3>1.23.0 + - Fixed cookie parsing and enabled multiple cookies to be set by using a list of + tuple values. + - Added pypi badges to README. + - Fixed formatting issues in README. + - Quoted cookie values are returned correctly now. + - Improved compatibility for pytest 5 + - Module level method names are no longer generated dynamically improving IDE navigation. + +------------------------------------------------------------------- Old: ---- responses-0.10.6.tar.gz New: ---- responses-0.10.12.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-responses.spec ++++++ --- /var/tmp/diff_new_pack.fY73qi/_old 2020-03-27 00:27:29.624323556 +0100 +++ /var/tmp/diff_new_pack.fY73qi/_new 2020-03-27 00:27:29.628323558 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-responses # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-responses -Version: 0.10.6 +Version: 0.10.12 Release: 0 Summary: A utility library for mocking out the `requests` Python library License: Apache-2.0 @@ -28,8 +28,8 @@ # test requirements BuildRequires: %{python_module cookies} BuildRequires: %{python_module mock} -BuildRequires: %{python_module pytest < 5.0} BuildRequires: %{python_module pytest-localserver} +BuildRequires: %{python_module pytest} BuildRequires: %{python_module requests >= 2.0} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six} ++++++ responses-0.10.6.tar.gz -> responses-0.10.12.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/CHANGES new/responses-0.10.12/CHANGES --- old/responses-0.10.6/CHANGES 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/CHANGES 2020-03-03 03:50:19.000000000 +0100 @@ -1,3 +1,44 @@ +0.10.12 +------- + +- Fixed incorrect content-type in `add_callback()` when headers are provided as a list of tuples. + +0.10.11 +------- + +- Fixed invalid README formatted. +- Fixed string formatting in error message. + +0.10.10 +------ + +- Added Python 3.8 support +- Remove Python 3.4 from test suite matrix. +- The `response.request` object now has a `params` attribute that contains the query string parameters from the request that was captured. +- `add_passthru` now supports `re` pattern objects to match URLs. +- ConnectionErrors raised by responses now include more details on the request that was attempted and the mocks registered. + +0.10.9 +------ + +- Fixed regression with `add_callback()` and content-type header. +- Fixed implicit dependency on urllib3>1.23.0 + +0.10.8 +------ + +- Fixed cookie parsing and enabled multiple cookies to be set by using a list of + tuple values. + +0.10.7 +------ + +- Added pypi badges to README. +- Fixed formatting issues in README. +- Quoted cookie values are returned correctly now. +- Improved compatibility for pytest 5 +- Module level method names are no longer generated dynamically improving IDE navigation. + 0.10.6 ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/PKG-INFO new/responses-0.10.12/PKG-INFO --- old/responses-0.10.6/PKG-INFO 2019-03-15 17:01:04.000000000 +0100 +++ new/responses-0.10.12/PKG-INFO 2020-03-03 03:50:59.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: responses -Version: 0.10.6 +Version: 0.10.12 Summary: A utility library for mocking out the `requests` Python library. Home-page: https://github.com/getsentry/responses Author: David Cramer @@ -8,9 +8,15 @@ Description: Responses ========= + .. image:: https://img.shields.io/pypi/v/responses.svg + :target: https://pypi.python.org/pypi/responses/ + .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master :target: https://travis-ci.org/getsentry/responses + .. image:: https://img.shields.io/pypi/pyversions/responses.svg + :target: https://pypi.org/project/responses/ + A utility library for mocking out the `requests` Python library. .. note:: @@ -240,6 +246,25 @@ ) + You can see params passed in the original ``request`` in ``responses.calls[].request.params``: + + .. code-block:: python + + import responses + import requests + + @responses.activate + def test_request_params(): + responses.add( + method=responses.GET, + url="http://example.com?hello=world", + body="test", + match_querystring=False, + ) + + resp = requests.get('http://example.com', params={"hello": "world"}) + assert response.calls[0].request.params == {"hello": "world"} + Responses as a context manager ------------------------------ @@ -353,7 +378,7 @@ -------------------------- In some cases you may wish to allow for certain requests to pass thru responses - and hit a real server. This can be done with the 'passthru' methods: + and hit a real server. This can be done with the ``add_passthru`` methods: .. code-block:: python @@ -366,6 +391,12 @@ This will allow any requests matching that prefix, that is otherwise not registered as a mock response, to passthru using the standard behavior. + Regex can be used like: + + .. code-block:: python + + responses.add_passthru(re.compile('https://percy.io/\\w+')) + Viewing/Modifying registered responses -------------------------------------- @@ -395,12 +426,10 @@ assert resp.json() == {'data': 2} - ``remove`` takes a ``method`` and ``url`` argument and will remove *all* - matched ``response``s from the registered list. - - Finally, ``clear`` will reset all registered ``response``s - + ``remove`` takes a ``method`` and ``url`` argument and will remove **all** + matched responses from the registered list. + Finally, ``reset`` will reset all registered responses. Contributing ------------ @@ -426,6 +455,19 @@ make develop + Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for + testing. You can run all tests by: + + .. code-block:: shell + + pytest + + And run a single test by: + + .. code-block:: shell + + pytest -k '<test_function_name>' + Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators @@ -438,6 +480,8 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Software Development Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Description-Content-Type: text/x-rst Provides-Extra: tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/README.rst new/responses-0.10.12/README.rst --- old/responses-0.10.6/README.rst 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/README.rst 2020-03-03 03:50:19.000000000 +0100 @@ -1,9 +1,15 @@ Responses ========= +.. image:: https://img.shields.io/pypi/v/responses.svg + :target: https://pypi.python.org/pypi/responses/ + .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master :target: https://travis-ci.org/getsentry/responses +.. image:: https://img.shields.io/pypi/pyversions/responses.svg + :target: https://pypi.org/project/responses/ + A utility library for mocking out the `requests` Python library. .. note:: @@ -233,6 +239,25 @@ ) +You can see params passed in the original ``request`` in ``responses.calls[].request.params``: + +.. code-block:: python + + import responses + import requests + + @responses.activate + def test_request_params(): + responses.add( + method=responses.GET, + url="http://example.com?hello=world", + body="test", + match_querystring=False, + ) + + resp = requests.get('http://example.com', params={"hello": "world"}) + assert response.calls[0].request.params == {"hello": "world"} + Responses as a context manager ------------------------------ @@ -346,7 +371,7 @@ -------------------------- In some cases you may wish to allow for certain requests to pass thru responses -and hit a real server. This can be done with the 'passthru' methods: +and hit a real server. This can be done with the ``add_passthru`` methods: .. code-block:: python @@ -359,6 +384,12 @@ This will allow any requests matching that prefix, that is otherwise not registered as a mock response, to passthru using the standard behavior. +Regex can be used like: + +.. code-block:: python + + responses.add_passthru(re.compile('https://percy.io/\\w+')) + Viewing/Modifying registered responses -------------------------------------- @@ -388,12 +419,10 @@ assert resp.json() == {'data': 2} -``remove`` takes a ``method`` and ``url`` argument and will remove *all* -matched ``response``s from the registered list. - -Finally, ``clear`` will reset all registered ``response``s - +``remove`` takes a ``method`` and ``url`` argument and will remove **all** +matched responses from the registered list. +Finally, ``reset`` will reset all registered responses. Contributing ------------ @@ -418,3 +447,16 @@ .. code-block:: shell make develop + +Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for +testing. You can run all tests by: + +.. code-block:: shell + + pytest + +And run a single test by: + +.. code-block:: shell + + pytest -k '<test_function_name>' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/responses.egg-info/PKG-INFO new/responses-0.10.12/responses.egg-info/PKG-INFO --- old/responses-0.10.6/responses.egg-info/PKG-INFO 2019-03-15 17:01:04.000000000 +0100 +++ new/responses-0.10.12/responses.egg-info/PKG-INFO 2020-03-03 03:50:59.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: responses -Version: 0.10.6 +Version: 0.10.12 Summary: A utility library for mocking out the `requests` Python library. Home-page: https://github.com/getsentry/responses Author: David Cramer @@ -8,9 +8,15 @@ Description: Responses ========= + .. image:: https://img.shields.io/pypi/v/responses.svg + :target: https://pypi.python.org/pypi/responses/ + .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master :target: https://travis-ci.org/getsentry/responses + .. image:: https://img.shields.io/pypi/pyversions/responses.svg + :target: https://pypi.org/project/responses/ + A utility library for mocking out the `requests` Python library. .. note:: @@ -240,6 +246,25 @@ ) + You can see params passed in the original ``request`` in ``responses.calls[].request.params``: + + .. code-block:: python + + import responses + import requests + + @responses.activate + def test_request_params(): + responses.add( + method=responses.GET, + url="http://example.com?hello=world", + body="test", + match_querystring=False, + ) + + resp = requests.get('http://example.com', params={"hello": "world"}) + assert response.calls[0].request.params == {"hello": "world"} + Responses as a context manager ------------------------------ @@ -353,7 +378,7 @@ -------------------------- In some cases you may wish to allow for certain requests to pass thru responses - and hit a real server. This can be done with the 'passthru' methods: + and hit a real server. This can be done with the ``add_passthru`` methods: .. code-block:: python @@ -366,6 +391,12 @@ This will allow any requests matching that prefix, that is otherwise not registered as a mock response, to passthru using the standard behavior. + Regex can be used like: + + .. code-block:: python + + responses.add_passthru(re.compile('https://percy.io/\\w+')) + Viewing/Modifying registered responses -------------------------------------- @@ -395,12 +426,10 @@ assert resp.json() == {'data': 2} - ``remove`` takes a ``method`` and ``url`` argument and will remove *all* - matched ``response``s from the registered list. - - Finally, ``clear`` will reset all registered ``response``s - + ``remove`` takes a ``method`` and ``url`` argument and will remove **all** + matched responses from the registered list. + Finally, ``reset`` will reset all registered responses. Contributing ------------ @@ -426,6 +455,19 @@ make develop + Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for + testing. You can run all tests by: + + .. code-block:: shell + + pytest + + And run a single test by: + + .. code-block:: shell + + pytest -k '<test_function_name>' + Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators @@ -438,6 +480,8 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Software Development Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Description-Content-Type: text/x-rst Provides-Extra: tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/responses.egg-info/requires.txt new/responses-0.10.12/responses.egg-info/requires.txt --- old/responses-0.10.6/responses.egg-info/requires.txt 2019-03-15 17:01:04.000000000 +0100 +++ new/responses-0.10.12/responses.egg-info/requires.txt 2020-03-03 03:50:59.000000000 +0100 @@ -8,8 +8,13 @@ cookies [tests] -pytest coverage<5.0.0,>=3.7.1 pytest-cov pytest-localserver flake8 + +[tests:python_version < "3.5"] +pytest<5.0,>=4.6 + +[tests:python_version >= "3.5"] +pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/responses.py new/responses-0.10.12/responses.py --- old/responses-0.10.6/responses.py 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/responses.py 2020-03-03 03:50:19.000000000 +0100 @@ -23,6 +23,10 @@ from requests.packages.urllib3.response import HTTPResponse except ImportError: from urllib3.response import HTTPResponse +try: + from requests.packages.urllib3.connection import HTTPHeaderDict +except ImportError: + from urllib3.connection import HTTPHeaderDict if six.PY2: from urlparse import urlparse, parse_qsl, urlsplit, urlunsplit @@ -104,6 +108,12 @@ ) +def _ensure_str(s): + if six.PY2: + s = s.encode("utf-8") if isinstance(s, six.text_type) else s + return s + + def _cookies_from_headers(headers): try: import http.cookies as cookies @@ -115,8 +125,10 @@ except ImportError: from cookies import Cookies - resp_cookies = Cookies.from_request(headers["set-cookie"]) - cookies_dict = {v.name: v.value for _, v in resp_cookies.items()} + resp_cookies = Cookies.from_request(_ensure_str(headers["set-cookie"])) + cookies_dict = { + v.name: quote(_ensure_str(v.value)) for _, v in resp_cookies.items() + } return cookiejar_from_dict(cookies_dict) @@ -237,7 +249,7 @@ if self.method != other.method: return False - # Can't simply do a equality check on the objects directly here since __eq__ isn't + # Can't simply do an equality check on the objects directly here since __eq__ isn't # implemented for regex. It might seem to work as regex is using a cache to return # the same regex instances, but it doesn't in all cases. self_url = self.url.pattern if isinstance(self.url, Pattern) else self.url @@ -301,11 +313,11 @@ return False def get_headers(self): - headers = {} + headers = HTTPHeaderDict() # Duplicate headers are legal if self.content_type is not None: headers["Content-Type"] = self.content_type if self.headers: - headers.update(self.headers) + headers.extend(self.headers) return headers def get_response(self, request): @@ -363,12 +375,12 @@ headers = self.get_headers() status = self.status body = _handle_body(self.body) - return HTTPResponse( status=status, reason=six.moves.http_client.responses.get(status), body=body, headers=headers, + original_response=OriginalResponseShim(headers), preload_content=False, ) @@ -393,18 +405,52 @@ if isinstance(body, Exception): raise body + # If the callback set a content-type remove the one + # set in add_callback() so that we don't have multiple + # content type values. + has_content_type = False + if isinstance(r_headers, dict) and "Content-Type" in r_headers: + has_content_type = True + elif isinstance(r_headers, list): + has_content_type = any( + [h for h in r_headers if h and h[0].lower() == "content-type"] + ) + if has_content_type: + headers.pop("Content-Type", None) + body = _handle_body(body) - headers.update(r_headers) + headers.extend(r_headers) return HTTPResponse( status=status, reason=six.moves.http_client.responses.get(status), body=body, headers=headers, + original_response=OriginalResponseShim(headers), preload_content=False, ) +class OriginalResponseShim(object): + """ + Shim for compatibility with older versions of urllib3 + + requests cookie handling depends on responses having a property chain of + `response._original_response.msg` which contains the response headers [1] + + Using HTTPResponse() for this purpose causes compatibility errors with + urllib3<1.23.0. To avoid adding more dependencies we can use this shim. + + [1]: https://github.com/psf/requests/blob/75bdc998e2d/requests/cookies.py#L125 + """ + + def __init__(self, headers): + self.msg = headers + + def isclosed(self): + return True + + class RequestsMock(object): DELETE = "DELETE" GET = "GET" @@ -488,14 +534,18 @@ def add_passthru(self, prefix): """ - Register a URL prefix to passthru any non-matching mock requests to. + Register a URL prefix or regex to passthru any non-matching mock requests to. For example, to allow any request to 'https://example.com', but require mocks for the remainder, you would add the prefix as so: >>> responses.add_passthru('https://example.com') + + Regex can be used like: + + >>> responses.add_passthru(re.compile('https://example.com/\\w+')) """ - if _has_unicode(prefix): + if not isinstance(prefix, Pattern) and _has_unicode(prefix): prefix = _clean_unicode(prefix) self.passthru_prefixes += (prefix,) @@ -583,16 +633,30 @@ def _on_request(self, adapter, request, **kwargs): match = self._find_match(request) resp_callback = self.response_callback + request.params = dict(parse_qsl(request.path_url.replace("/?", ""))) if match is None: - if request.url.startswith(self.passthru_prefixes): + if any( + [ + p.match(request.url) + if isinstance(p, Pattern) + else request.url.startswith(p) + for p in self.passthru_prefixes + ] + ): logger.info("request.allowed-passthru", extra={"url": request.url}) return _real_send(adapter, request, **kwargs) error_msg = ( - "Connection refused by Responses: {0} {1} doesn't " - "match Responses Mock".format(request.method, request.url) + "Connection refused by Responses - the call doesn't " + "match any registered mock.\n\n" + "Request: \n" + "- %s %s\n\n" + "Available matches:\n" % (request.method, request.url) ) + for m in self._matches: + error_msg += "- {} {}\n".format(m.method, m.url) + response = ConnectionError(error_msg) response.request = request @@ -602,7 +666,7 @@ try: response = adapter.build_response(request, match.get_response(request)) - except Exception as response: + except BaseException as response: match.call_count += 1 self._calls.add(request, response) response = resp_callback(response) if resp_callback else response @@ -611,11 +675,6 @@ if not match.stream: response.content # NOQA - try: - response.cookies = _cookies_from_headers(response.headers) - except (KeyError, TypeError): - pass - response = resp_callback(response) if resp_callback else response match.call_count += 1 self._calls.add(request, response) @@ -647,7 +706,52 @@ # expose default mock namespace mock = _default_mock = RequestsMock(assert_all_requests_are_fired=False) -__all__ = ["CallbackResponse", "Response", "RequestsMock"] -for __attr in (a for a in dir(_default_mock) if not a.startswith("_")): - __all__.append(__attr) - globals()[__attr] = getattr(_default_mock, __attr) +__all__ = [ + "CallbackResponse", + "Response", + "RequestsMock", + # Exposed by the RequestsMock class: + "activate", + "add", + "add_callback", + "add_passthru", + "assert_all_requests_are_fired", + "calls", + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "passthru_prefixes", + "PATCH", + "POST", + "PUT", + "remove", + "replace", + "reset", + "response_callback", + "start", + "stop", + "target", +] + +activate = _default_mock.activate +add = _default_mock.add +add_callback = _default_mock.add_callback +add_passthru = _default_mock.add_passthru +assert_all_requests_are_fired = _default_mock.assert_all_requests_are_fired +calls = _default_mock.calls +DELETE = _default_mock.DELETE +GET = _default_mock.GET +HEAD = _default_mock.HEAD +OPTIONS = _default_mock.OPTIONS +passthru_prefixes = _default_mock.passthru_prefixes +PATCH = _default_mock.PATCH +POST = _default_mock.POST +PUT = _default_mock.PUT +remove = _default_mock.remove +replace = _default_mock.replace +reset = _default_mock.reset +response_callback = _default_mock.response_callback +start = _default_mock.start +stop = _default_mock.stop +target = _default_mock.target diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/setup.py new/responses-0.10.12/setup.py --- old/responses-0.10.6/setup.py 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/setup.py 2020-03-03 03:50:19.000000000 +0100 @@ -27,7 +27,8 @@ ] tests_require = [ - "pytest", + "pytest>=4.6,<5.0; python_version < '3.5'", + "pytest; python_version >= '3.5'", "coverage >= 3.7.1, < 5.0.0", "pytest-cov", "pytest-localserver", @@ -53,12 +54,13 @@ setup( name="responses", - version="0.10.6", + version="0.10.12", author="David Cramer", description=("A utility library for mocking out the `requests` Python library."), url="https://github.com/getsentry/responses", license="Apache 2.0", long_description=open("README.rst").read(), + long_description_content_type="text/x-rst", py_modules=["responses"], zip_safe=False, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", @@ -80,6 +82,7 @@ "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Topic :: Software Development", ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/test_responses.py new/responses-0.10.12/test_responses.py --- old/responses-0.10.6/test_responses.py 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/test_responses.py 2020-03-03 03:50:19.000000000 +0100 @@ -410,7 +410,11 @@ body = b"test callback" status = 400 reason = "Bad Request" - headers = {"foo": "bar"} + headers = { + "foo": "bar", + "Content-Type": "application/json", + "Content-Length": "13", + } url = "http://example.com/" def request_callback(request): @@ -423,8 +427,9 @@ assert resp.text == "test callback" assert resp.status_code == status assert resp.reason == reason - assert "foo" in resp.headers - assert resp.headers["foo"] == "bar" + assert "bar" == resp.headers.get("foo") + assert "application/json" == resp.headers.get("Content-Type") + assert "13" == resp.headers.get("Content-Length") run() assert_reset() @@ -494,6 +499,44 @@ assert_reset() +def test_callback_content_type_dict(): + def request_callback(request): + return ( + 200, + {"Content-Type": "application/json"}, + b"foo", + ) + + @responses.activate + def run(): + responses.add_callback("GET", "http://mockhost/.foo", callback=request_callback) + resp = requests.get("http://mockhost/.foo") + assert resp.text == "foo" + assert resp.headers["content-type"] == "application/json" + + run() + assert_reset() + + +def test_callback_content_type_tuple(): + def request_callback(request): + return ( + 200, + [("Content-Type", "application/json")], + b"foo", + ) + + @responses.activate + def run(): + responses.add_callback("GET", "http://mockhost/.foo", callback=request_callback) + resp = requests.get("http://mockhost/.foo") + assert resp.text == "foo" + assert resp.headers["content-type"] == "application/json" + + run() + assert_reset() + + def test_regular_expression_url(): @responses.activate def run(): @@ -657,8 +700,56 @@ assert resp.status_code == status assert "session_id" in resp.cookies assert resp.cookies["session_id"] == "12345" - assert resp.cookies["a"] == "b" - assert resp.cookies["c"] == "d" + assert set(resp.cookies.keys()) == set(["session_id"]) + + run() + assert_reset() + + +def test_response_secure_cookies(): + body = b"test callback" + status = 200 + headers = {"set-cookie": "session_id=12345; a=b; c=d; secure"} + url = "http://example.com/" + + def request_callback(request): + return (status, headers, body) + + @responses.activate + def run(): + responses.add_callback(responses.GET, url, request_callback) + resp = requests.get(url) + assert resp.text == "test callback" + assert resp.status_code == status + assert "session_id" in resp.cookies + assert resp.cookies["session_id"] == "12345" + assert set(resp.cookies.keys()) == set(["session_id"]) + + run() + assert_reset() + + +def test_response_cookies_multiple(): + body = b"test callback" + status = 200 + headers = [ + ("set-cookie", "1P_JAR=2019-12-31-23; path=/; domain=.example.com; HttpOnly"), + ("set-cookie", "NID=some=value; path=/; domain=.example.com; secure"), + ] + url = "http://example.com/" + + def request_callback(request): + return (status, headers, body) + + @responses.activate + def run(): + responses.add_callback(responses.GET, url, request_callback) + resp = requests.get(url) + assert resp.text == "test callback" + assert resp.status_code == status + assert set(resp.cookies.keys()) == set(["1P_JAR", "NID"]) + assert resp.cookies["1P_JAR"] == "2019-12-31-23" + assert resp.cookies["NID"] == "some=value" run() assert_reset() @@ -696,12 +787,15 @@ def test_assert_all_requests_are_fired(): + def request_callback(request): + raise BaseException() + def run(): with pytest.raises(AssertionError) as excinfo: with responses.RequestsMock(assert_all_requests_are_fired=True) as m: m.add(responses.GET, "http://example.com", body=b"test") assert "http://example.com" in str(excinfo.value) - assert responses.GET in str(excinfo) + assert responses.GET in str(excinfo.value) # check that assert_all_requests_are_fired default to True with pytest.raises(AssertionError): @@ -729,6 +823,13 @@ requests.get("http://example.com") assert len(m._matches) == 1 + with responses.RequestsMock(assert_all_requests_are_fired=True) as m: + m.add_callback(responses.GET, "http://example.com", request_callback) + assert len(m._matches) == 1 + with pytest.raises(BaseException): + requests.get("http://example.com") + assert len(m._matches) == 1 + run() assert_reset() @@ -890,6 +991,28 @@ assert_reset() +def test_passthru_regex(httpserver): + httpserver.serve_content("OK", headers={"Content-Type": "text/plain"}) + + @responses.activate + def run(): + responses.add_passthru(re.compile("{}/\\w+".format(httpserver.url))) + responses.add(responses.GET, "{}/one".format(httpserver.url), body="one") + responses.add(responses.GET, "http://example.com/two", body="two") + + resp = requests.get("http://example.com/two") + assert_response(resp, "two") + resp = requests.get("{}/one".format(httpserver.url)) + assert_response(resp, "one") + resp = requests.get("{}/two".format(httpserver.url)) + assert_response(resp, "OK") + resp = requests.get("{}/three".format(httpserver.url)) + assert_response(resp, "OK") + + run() + assert_reset() + + def test_method_named_param(): @responses.activate def run(): @@ -922,3 +1045,40 @@ requests_mock.start() assert len(patch_mock.call_args_list) == 1 assert patch_mock.call_args[1]["target"] == "something.else" + + +def _quote(s): + return responses.quote(responses._ensure_str(s)) + + +def test_cookies_from_headers(): + text = "こんにちは/世界" + quoted_text = _quote(text) + expected = {"x": "a", "y": quoted_text} + headers = {"set-cookie": "; ".join(k + "=" + v for k, v in expected.items())} + cookiejar = responses._cookies_from_headers(headers) + for k, v in cookiejar.items(): + assert isinstance(v, str) + assert v == expected[k] + + +def test_request_param(): + @responses.activate + def run(): + params = {"hello": "world", "example": "params"} + responses.add( + method=responses.GET, + url="http://example.com?hello=world", + body="test", + match_querystring=False, + ) + resp = requests.get("http://example.com", params=params) + assert_response(resp, "test") + assert resp.request.params == params + + resp = requests.get("http://example.com") + assert_response(resp, "test") + assert resp.request.params == {} + + run() + assert_reset() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/responses-0.10.6/tox.ini new/responses-0.10.12/tox.ini --- old/responses-0.10.6/tox.ini 2019-03-15 17:00:18.000000000 +0100 +++ new/responses-0.10.12/tox.ini 2020-03-03 03:50:19.000000000 +0100 @@ -1,5 +1,5 @@ [tox] -envlist = py27,py34,py35,py36,py37 +envlist = py27,py34,py35,py36,py37,py38 [testenv] extras = tests