From: Kar Petrosyan Date: Thu, 27 Feb 2025 15:53:57 +0000 (+0400) Subject: Make all the tests from test_headers and test_cookies to be async X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a8038137022b8618a936b9b3f48dfb76f0ad67f8;p=thirdparty%2Fhttpx.git Make all the tests from test_headers and test_cookies to be async --- diff --git a/tests/client/test_client.py b/tests/client/test_client.py deleted file mode 100644 index 65783901..00000000 --- a/tests/client/test_client.py +++ /dev/null @@ -1,462 +0,0 @@ -from __future__ import annotations - -import typing -from datetime import timedelta - -import chardet -import pytest - -import httpx - - -def autodetect(content): - return chardet.detect(content).get("encoding") - - -def test_get(server): - url = server.url - with httpx.Client(http2=True) as http: - response = http.get(url) - assert response.status_code == 200 - assert response.url == url - assert response.content == b"Hello, world!" - assert response.text == "Hello, world!" - assert response.http_version == "HTTP/1.1" - assert response.encoding == "utf-8" - assert response.request.url == url - assert response.headers - assert response.is_redirect is False - assert repr(response) == "" - assert response.elapsed > timedelta(0) - - -@pytest.mark.parametrize( - "url", - [ - pytest.param("invalid://example.org", id="scheme-not-http(s)"), - pytest.param("://example.org", id="no-scheme"), - pytest.param("http://", id="no-host"), - ], -) -def test_get_invalid_url(server, url): - with httpx.Client() as client: - with pytest.raises((httpx.UnsupportedProtocol, httpx.LocalProtocolError)): - client.get(url) - - -def test_build_request(server): - url = server.url.copy_with(path="/echo_headers") - headers = {"Custom-header": "value"} - - with httpx.Client() as client: - request = client.build_request("GET", url) - request.headers.update(headers) - response = client.send(request) - - assert response.status_code == 200 - assert response.url == url - - assert response.json()["Custom-header"] == "value" - - -def test_build_post_request(server): - url = server.url.copy_with(path="/echo_headers") - headers = {"Custom-header": "value"} - - with httpx.Client() as client: - request = client.build_request("POST", url) - request.headers.update(headers) - response = client.send(request) - - assert response.status_code == 200 - assert response.url == url - - assert response.json()["Content-length"] == "0" - assert response.json()["Custom-header"] == "value" - - -def test_post(server): - with httpx.Client() as client: - response = client.post(server.url, content=b"Hello, world!") - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_post_json(server): - with httpx.Client() as client: - response = client.post(server.url, json={"text": "Hello, world!"}) - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_stream_response(server): - with httpx.Client() as client: - with client.stream("GET", server.url) as response: - content = response.read() - assert response.status_code == 200 - assert content == b"Hello, world!" - - -def test_stream_iterator(server): - body = b"" - - with httpx.Client() as client: - with client.stream("GET", server.url) as response: - for chunk in response.iter_bytes(): - body += chunk - - assert response.status_code == 200 - assert body == b"Hello, world!" - - -def test_raw_iterator(server): - body = b"" - - with httpx.Client() as client: - with client.stream("GET", server.url) as response: - for chunk in response.iter_raw(): - body += chunk - - assert response.status_code == 200 - assert body == b"Hello, world!" - - -def test_cannot_stream_async_request(server): - async def hello_world() -> typing.AsyncIterator[bytes]: # pragma: no cover - yield b"Hello, " - yield b"world!" - - with httpx.Client() as client: - with pytest.raises(RuntimeError): - client.post(server.url, content=hello_world()) - - -def test_raise_for_status(server): - with httpx.Client() as client: - for status_code in (200, 400, 404, 500, 505): - response = client.request( - "GET", server.url.copy_with(path=f"/status/{status_code}") - ) - if 400 <= status_code < 600: - with pytest.raises(httpx.HTTPStatusError) as exc_info: - response.raise_for_status() - assert exc_info.value.response == response - assert exc_info.value.request.url.path == f"/status/{status_code}" - else: - assert response.raise_for_status() is response - - -def test_options(server): - with httpx.Client() as client: - response = client.options(server.url) - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_head(server): - with httpx.Client() as client: - response = client.head(server.url) - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_put(server): - with httpx.Client() as client: - response = client.put(server.url, content=b"Hello, world!") - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_patch(server): - with httpx.Client() as client: - response = client.patch(server.url, content=b"Hello, world!") - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_delete(server): - with httpx.Client() as client: - response = client.delete(server.url) - assert response.status_code == 200 - assert response.reason_phrase == "OK" - - -def test_base_url(server): - base_url = server.url - with httpx.Client(base_url=base_url) as client: - response = client.get("/") - assert response.status_code == 200 - assert response.url == base_url - - -def test_merge_absolute_url(): - client = httpx.Client(base_url="https://www.example.com/") - request = client.build_request("GET", "http://www.example.com/") - assert request.url == "http://www.example.com/" - - -def test_merge_relative_url(): - client = httpx.Client(base_url="https://www.example.com/") - request = client.build_request("GET", "/testing/123") - assert request.url == "https://www.example.com/testing/123" - - -def test_merge_relative_url_with_path(): - client = httpx.Client(base_url="https://www.example.com/some/path") - request = client.build_request("GET", "/testing/123") - assert request.url == "https://www.example.com/some/path/testing/123" - - -def test_merge_relative_url_with_dotted_path(): - client = httpx.Client(base_url="https://www.example.com/some/path") - request = client.build_request("GET", "../testing/123") - assert request.url == "https://www.example.com/some/testing/123" - - -def test_merge_relative_url_with_path_including_colon(): - client = httpx.Client(base_url="https://www.example.com/some/path") - request = client.build_request("GET", "/testing:123") - assert request.url == "https://www.example.com/some/path/testing:123" - - -def test_merge_relative_url_with_encoded_slashes(): - client = httpx.Client(base_url="https://www.example.com/") - request = client.build_request("GET", "/testing%2F123") - assert request.url == "https://www.example.com/testing%2F123" - - client = httpx.Client(base_url="https://www.example.com/base%2Fpath") - request = client.build_request("GET", "/testing") - assert request.url == "https://www.example.com/base%2Fpath/testing" - - -def test_context_managed_transport(): - class Transport(httpx.BaseTransport): - def __init__(self) -> None: - self.events: list[str] = [] - - def close(self): - # The base implementation of httpx.BaseTransport just - # calls into `.close`, so simple transport cases can just override - # this method for any cleanup, where more complex cases - # might want to additionally override `__enter__`/`__exit__`. - self.events.append("transport.close") - - def __enter__(self): - super().__enter__() - self.events.append("transport.__enter__") - - def __exit__(self, *args): - super().__exit__(*args) - self.events.append("transport.__exit__") - - transport = Transport() - with httpx.Client(transport=transport): - pass - - assert transport.events == [ - "transport.__enter__", - "transport.close", - "transport.__exit__", - ] - - -def test_context_managed_transport_and_mount(): - class Transport(httpx.BaseTransport): - def __init__(self, name: str) -> None: - self.name: str = name - self.events: list[str] = [] - - def close(self): - # The base implementation of httpx.BaseTransport just - # calls into `.close`, so simple transport cases can just override - # this method for any cleanup, where more complex cases - # might want to additionally override `__enter__`/`__exit__`. - self.events.append(f"{self.name}.close") - - def __enter__(self): - super().__enter__() - self.events.append(f"{self.name}.__enter__") - - def __exit__(self, *args): - super().__exit__(*args) - self.events.append(f"{self.name}.__exit__") - - transport = Transport(name="transport") - mounted = Transport(name="mounted") - with httpx.Client(transport=transport, mounts={"http://www.example.org": mounted}): - pass - - assert transport.events == [ - "transport.__enter__", - "transport.close", - "transport.__exit__", - ] - assert mounted.events == [ - "mounted.__enter__", - "mounted.close", - "mounted.__exit__", - ] - - -def hello_world(request): - return httpx.Response(200, text="Hello, world!") - - -def test_client_closed_state_using_implicit_open(): - client = httpx.Client(transport=httpx.MockTransport(hello_world)) - - assert not client.is_closed - client.get("http://example.com") - - assert not client.is_closed - client.close() - - assert client.is_closed - - # Once we're close we cannot make any more requests. - with pytest.raises(RuntimeError): - client.get("http://example.com") - - # Once we're closed we cannot reopen the client. - with pytest.raises(RuntimeError): - with client: - pass # pragma: no cover - - -def test_client_closed_state_using_with_block(): - with httpx.Client(transport=httpx.MockTransport(hello_world)) as client: - assert not client.is_closed - client.get("http://example.com") - - assert client.is_closed - with pytest.raises(RuntimeError): - client.get("http://example.com") - - -def echo_raw_headers(request: httpx.Request) -> httpx.Response: - data = [ - (name.decode("ascii"), value.decode("ascii")) - for name, value in request.headers.raw - ] - return httpx.Response(200, json=data) - - -def test_raw_client_header(): - """ - Set a header in the Client. - """ - url = "http://example.org/echo_headers" - headers = {"Example-Header": "example-value"} - - client = httpx.Client( - transport=httpx.MockTransport(echo_raw_headers), headers=headers - ) - response = client.get(url) - - assert response.status_code == 200 - assert response.json() == [ - ["Host", "example.org"], - ["Accept", "*/*"], - ["Accept-Encoding", "gzip, deflate, br, zstd"], - ["Connection", "keep-alive"], - ["User-Agent", f"python-httpx/{httpx.__version__}"], - ["Example-Header", "example-value"], - ] - - -def unmounted(request: httpx.Request) -> httpx.Response: - data = {"app": "unmounted"} - return httpx.Response(200, json=data) - - -def mounted(request: httpx.Request) -> httpx.Response: - data = {"app": "mounted"} - return httpx.Response(200, json=data) - - -def test_mounted_transport(): - transport = httpx.MockTransport(unmounted) - mounts = {"custom://": httpx.MockTransport(mounted)} - - client = httpx.Client(transport=transport, mounts=mounts) - - response = client.get("https://www.example.com") - assert response.status_code == 200 - assert response.json() == {"app": "unmounted"} - - response = client.get("custom://www.example.com") - assert response.status_code == 200 - assert response.json() == {"app": "mounted"} - - -def test_all_mounted_transport(): - mounts = {"all://": httpx.MockTransport(mounted)} - - client = httpx.Client(mounts=mounts) - - response = client.get("https://www.example.com") - assert response.status_code == 200 - assert response.json() == {"app": "mounted"} - - -def test_server_extensions(server): - url = server.url.copy_with(path="/http_version_2") - with httpx.Client(http2=True) as client: - response = client.get(url) - assert response.status_code == 200 - assert response.extensions["http_version"] == b"HTTP/1.1" - - -def test_client_decode_text_using_autodetect(): - # Ensure that a 'default_encoding=autodetect' on the response allows for - # encoding autodetection to be used when no "Content-Type: text/plain; charset=..." - # info is present. - # - # Here we have some french text encoded with ISO-8859-1, rather than UTF-8. - text = ( - "Non-seulement Despréaux ne se trompait pas, mais de tous les écrivains " - "que la France a produits, sans excepter Voltaire lui-même, imprégné de " - "l'esprit anglais par son séjour à Londres, c'est incontestablement " - "Molière ou Poquelin qui reproduit avec l'exactitude la plus vive et la " - "plus complète le fond du génie français." - ) - - def cp1252_but_no_content_type(request): - content = text.encode("ISO-8859-1") - return httpx.Response(200, content=content) - - transport = httpx.MockTransport(cp1252_but_no_content_type) - with httpx.Client(transport=transport, default_encoding=autodetect) as client: - response = client.get("http://www.example.com") - - assert response.status_code == 200 - assert response.reason_phrase == "OK" - assert response.encoding == "ISO-8859-1" - assert response.text == text - - -def test_client_decode_text_using_explicit_encoding(): - # Ensure that a 'default_encoding="..."' on the response is used for text decoding - # when no "Content-Type: text/plain; charset=..."" info is present. - # - # Here we have some french text encoded with ISO-8859-1, rather than UTF-8. - text = ( - "Non-seulement Despréaux ne se trompait pas, mais de tous les écrivains " - "que la France a produits, sans excepter Voltaire lui-même, imprégné de " - "l'esprit anglais par son séjour à Londres, c'est incontestablement " - "Molière ou Poquelin qui reproduit avec l'exactitude la plus vive et la " - "plus complète le fond du génie français." - ) - - def cp1252_but_no_content_type(request): - content = text.encode("ISO-8859-1") - return httpx.Response(200, content=content) - - transport = httpx.MockTransport(cp1252_but_no_content_type) - with httpx.Client(transport=transport, default_encoding=autodetect) as client: - response = client.get("http://www.example.com") - - assert response.status_code == 200 - assert response.reason_phrase == "OK" - assert response.encoding == "ISO-8859-1" - assert response.text == text diff --git a/tests/client/test_cookies.py b/tests/client/test_cookies.py index f0c83525..57824b90 100644 --- a/tests/client/test_cookies.py +++ b/tests/client/test_cookies.py @@ -15,38 +15,43 @@ def get_and_set_cookies(request: httpx.Request) -> httpx.Response: raise NotImplementedError() # pragma: no cover -def test_set_cookie() -> None: +@pytest.mark.anyio +async def test_set_cookie() -> None: """ Send a request including a cookie. """ url = "http://example.org/echo_cookies" cookies = {"example-name": "example-value"} - client = httpx.Client( + async with httpx.AsyncClient( cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) - response = client.get(url) + ) as client: + response = await client.get(url) - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"} -def test_set_per_request_cookie_is_deprecated() -> None: +@pytest.mark.anyio +async def test_set_per_request_cookie_is_deprecated() -> None: """ Sending a request including a per-request cookie is deprecated. """ url = "http://example.org/echo_cookies" cookies = {"example-name": "example-value"} - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - with pytest.warns(DeprecationWarning): - response = client.get(url, cookies=cookies) + async with httpx.AsyncClient( + transport=httpx.MockTransport(get_and_set_cookies) + ) as client: + with pytest.warns(DeprecationWarning): + response = await client.get(url, cookies=cookies) - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"} -def test_set_cookie_with_cookiejar() -> None: +@pytest.mark.anyio +async def test_set_cookie_with_cookiejar() -> None: """ Send a request including a cookie, using a `CookieJar` instance. """ @@ -74,16 +79,17 @@ def test_set_cookie_with_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client( + async with httpx.AsyncClient( cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) - response = client.get(url) + ) as client: + response = await client.get(url) - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"} -def test_setting_client_cookies_to_cookiejar() -> None: +@pytest.mark.anyio +async def test_setting_client_cookies_to_cookiejar() -> None: """ Send a request including a cookie, using a `CookieJar` instance. """ @@ -111,16 +117,17 @@ def test_setting_client_cookies_to_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client( + async with httpx.AsyncClient( cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) - response = client.get(url) + ) as client: + response = await client.get(url) - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"} -def test_set_cookie_with_cookies_model() -> None: +@pytest.mark.anyio +async def test_set_cookie_with_cookies_model() -> None: """ Send a request including a cookie, using a `Cookies` instance. """ @@ -129,40 +136,47 @@ def test_set_cookie_with_cookies_model() -> None: cookies = httpx.Cookies() cookies["example-name"] = "example-value" - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - client.cookies = cookies - response = client.get(url) + async with httpx.AsyncClient( + transport=httpx.MockTransport(get_and_set_cookies) + ) as client: + client.cookies = cookies + response = await client.get(url) - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"} -def test_get_cookie() -> None: +@pytest.mark.anyio +async def test_get_cookie() -> None: url = "http://example.org/set_cookie" - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - response = client.get(url) + async with httpx.AsyncClient( + transport=httpx.MockTransport(get_and_set_cookies) + ) as client: + response = await client.get(url) - assert response.status_code == 200 - assert response.cookies["example-name"] == "example-value" - assert client.cookies["example-name"] == "example-value" + assert response.status_code == 200 + assert response.cookies["example-name"] == "example-value" + assert client.cookies["example-name"] == "example-value" -def test_cookie_persistence() -> None: +@pytest.mark.anyio +async def test_cookie_persistence() -> None: """ Ensure that Client instances persist cookies between requests. """ - client = httpx.Client(transport=httpx.MockTransport(get_and_set_cookies)) - - response = client.get("http://example.org/echo_cookies") - assert response.status_code == 200 - assert response.json() == {"cookies": None} - - response = client.get("http://example.org/set_cookie") - assert response.status_code == 200 - assert response.cookies["example-name"] == "example-value" - assert client.cookies["example-name"] == "example-value" - - response = client.get("http://example.org/echo_cookies") - assert response.status_code == 200 - assert response.json() == {"cookies": "example-name=example-value"} + async with httpx.AsyncClient( + transport=httpx.MockTransport(get_and_set_cookies) + ) as client: + response = await client.get("http://example.org/echo_cookies") + assert response.status_code == 200 + assert response.json() == {"cookies": None} + + response = await client.get("http://example.org/set_cookie") + assert response.status_code == 200 + assert response.cookies["example-name"] == "example-value" + assert client.cookies["example-name"] == "example-value" + + response = await client.get("http://example.org/echo_cookies") + assert response.status_code == 200 + assert response.json() == {"cookies": "example-name=example-value"}