Clean up 'backend' fixture (#664)

* Clean up 'backend' fixture

* Add docstring to 'async_environment' fixture
This commit is contained in:
Florimond Manca 2019-12-21 16:08:40 +01:00 committed by GitHub
parent 56c8edaf66
commit d0427bead0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 164 additions and 114 deletions

View File

@ -5,7 +5,8 @@ import pytest
import httpx
async def test_get(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_get(server):
url = server.url
async with httpx.Client() as client:
response = await client.get(url)
@ -17,7 +18,8 @@ async def test_get(server, backend):
assert response.elapsed > timedelta(seconds=0)
async def test_build_request(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_build_request(server):
url = server.url.copy_with(path="/echo_headers")
headers = {"Custom-header": "value"}
async with httpx.Client() as client:
@ -31,36 +33,24 @@ async def test_build_request(server, backend):
assert response.json()["Custom-header"] == "value"
@pytest.mark.asyncio
async def test_get_no_backend(server):
"""
Verify that the client is capable of making a simple request if not given a backend.
"""
url = server.url
async with httpx.Client() as client:
response = await client.get(url)
assert response.status_code == 200
assert response.text == "Hello, world!"
assert response.http_version == "HTTP/1.1"
assert response.headers
assert repr(response) == "<Response [200 OK]>"
async def test_post(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_post(server):
url = server.url
async with httpx.Client() as client:
response = await client.post(url, data=b"Hello, world!")
assert response.status_code == 200
async def test_post_json(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_post_json(server):
url = server.url
async with httpx.Client() as client:
response = await client.post(url, json={"text": "Hello, world!"})
assert response.status_code == 200
async def test_stream_response(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_stream_response(server):
async with httpx.Client() as client:
async with client.stream("GET", server.url) as response:
body = await response.read()
@ -70,7 +60,8 @@ async def test_stream_response(server, backend):
assert response.content == b"Hello, world!"
async def test_access_content_stream_response(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_access_content_stream_response(server):
async with httpx.Client() as client:
async with client.stream("GET", server.url) as response:
pass
@ -80,7 +71,8 @@ async def test_access_content_stream_response(server, backend):
response.content
async def test_stream_request(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_stream_request(server):
async def hello_world():
yield b"Hello, "
yield b"world!"
@ -90,7 +82,8 @@ async def test_stream_request(server, backend):
assert response.status_code == 200
async def test_raise_for_status(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_raise_for_status(server):
async with httpx.Client() as client:
for status_code in (200, 400, 404, 500, 505):
response = await client.request(
@ -105,40 +98,46 @@ async def test_raise_for_status(server, backend):
assert response.raise_for_status() is None
async def test_options(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_options(server):
async with httpx.Client() as client:
response = await client.options(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"
async def test_head(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_head(server):
async with httpx.Client() as client:
response = await client.head(server.url)
assert response.status_code == 200
assert response.text == ""
async def test_put(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_put(server):
async with httpx.Client() as client:
response = await client.put(server.url, data=b"Hello, world!")
assert response.status_code == 200
async def test_patch(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_patch(server):
async with httpx.Client() as client:
response = await client.patch(server.url, data=b"Hello, world!")
assert response.status_code == 200
async def test_delete(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_delete(server):
async with httpx.Client() as client:
response = await client.delete(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"
async def test_100_continue(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_100_continue(server):
headers = {"Expect": "100-continue"}
data = b"Echo request body"
@ -151,7 +150,8 @@ async def test_100_continue(server, backend):
assert response.content == data
async def test_uds(uds_server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_uds(uds_server):
url = uds_server.url
uds = uds_server.config.uds
assert uds is not None
@ -162,15 +162,8 @@ async def test_uds(uds_server, backend):
assert response.encoding == "iso-8859-1"
@pytest.mark.parametrize(
"backend",
[
pytest.param("asyncio", marks=pytest.mark.asyncio),
pytest.param("trio", marks=pytest.mark.trio),
],
)
async def test_explicit_backend(server, backend):
async with httpx.Client(backend=backend) as client:
async def test_explicit_backend(server, async_environment):
async with httpx.Client(backend=async_environment) as client:
response = await client.get(server.url)
assert response.status_code == 200
assert response.text == "Hello, world!"

View File

@ -103,7 +103,8 @@ class MockDispatch(Dispatcher):
return Response(codes.OK, content=b"Hello, world!", request=request)
async def test_no_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_no_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/no_redirect"
response = await client.get(url)
@ -112,7 +113,8 @@ async def test_no_redirect(backend):
await response.next()
async def test_redirect_301(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_301():
client = Client(dispatch=MockDispatch())
response = await client.post("https://example.org/redirect_301")
assert response.status_code == codes.OK
@ -120,7 +122,8 @@ async def test_redirect_301(backend):
assert len(response.history) == 1
async def test_redirect_302(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_302():
client = Client(dispatch=MockDispatch())
response = await client.post("https://example.org/redirect_302")
assert response.status_code == codes.OK
@ -128,7 +131,8 @@ async def test_redirect_302(backend):
assert len(response.history) == 1
async def test_redirect_303(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_303():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/redirect_303")
assert response.status_code == codes.OK
@ -136,7 +140,8 @@ async def test_redirect_303(backend):
assert len(response.history) == 1
async def test_disallow_redirects(backend):
@pytest.mark.usefixtures("async_environment")
async def test_disallow_redirects():
client = Client(dispatch=MockDispatch())
response = await client.post(
"https://example.org/redirect_303", allow_redirects=False
@ -153,7 +158,8 @@ async def test_disallow_redirects(backend):
assert len(response.history) == 1
async def test_relative_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_relative_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/relative_redirect")
assert response.status_code == codes.OK
@ -161,7 +167,8 @@ async def test_relative_redirect(backend):
assert len(response.history) == 1
async def test_no_scheme_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_no_scheme_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/no_scheme_redirect")
assert response.status_code == codes.OK
@ -169,7 +176,8 @@ async def test_no_scheme_redirect(backend):
assert len(response.history) == 1
async def test_fragment_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_fragment_redirect():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/relative_redirect#fragment")
assert response.status_code == codes.OK
@ -177,7 +185,8 @@ async def test_fragment_redirect(backend):
assert len(response.history) == 1
async def test_multiple_redirects(backend):
@pytest.mark.usefixtures("async_environment")
async def test_multiple_redirects():
client = Client(dispatch=MockDispatch())
response = await client.get("https://example.org/multiple_redirects?count=20")
assert response.status_code == codes.OK
@ -193,13 +202,15 @@ async def test_multiple_redirects(backend):
assert len(response.history[1].history) == 1
async def test_too_many_redirects(backend):
@pytest.mark.usefixtures("async_environment")
async def test_too_many_redirects():
client = Client(dispatch=MockDispatch())
with pytest.raises(TooManyRedirects):
await client.get("https://example.org/multiple_redirects?count=21")
async def test_too_many_redirects_calling_next(backend):
@pytest.mark.usefixtures("async_environment")
async def test_too_many_redirects_calling_next():
client = Client(dispatch=MockDispatch())
url = "https://example.org/multiple_redirects?count=21"
response = await client.get(url, allow_redirects=False)
@ -208,13 +219,15 @@ async def test_too_many_redirects_calling_next(backend):
response = await response.next()
async def test_redirect_loop(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_loop():
client = Client(dispatch=MockDispatch())
with pytest.raises(RedirectLoop):
await client.get("https://example.org/redirect_loop")
async def test_redirect_loop_calling_next(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_loop_calling_next():
client = Client(dispatch=MockDispatch())
url = "https://example.org/redirect_loop"
response = await client.get(url, allow_redirects=False)
@ -223,7 +236,8 @@ async def test_redirect_loop_calling_next(backend):
response = await response.next()
async def test_cross_domain_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_cross_domain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/cross_domain"
headers = {"Authorization": "abc"}
@ -232,7 +246,8 @@ async def test_cross_domain_redirect(backend):
assert "authorization" not in response.json()["headers"]
async def test_same_domain_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_same_domain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.org/cross_domain"
headers = {"Authorization": "abc"}
@ -241,7 +256,8 @@ async def test_same_domain_redirect(backend):
assert response.json()["headers"]["authorization"] == "abc"
async def test_body_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_body_redirect():
"""
A 308 redirect should preserve the request body.
"""
@ -254,7 +270,8 @@ async def test_body_redirect(backend):
assert "content-length" in response.json()["headers"]
async def test_no_body_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_no_body_redirect():
"""
A 303 redirect should remove the request body.
"""
@ -267,7 +284,8 @@ async def test_no_body_redirect(backend):
assert "content-length" not in response.json()["headers"]
async def test_cannot_redirect_streaming_body(backend):
@pytest.mark.usefixtures("async_environment")
async def test_cannot_redirect_streaming_body():
client = Client(dispatch=MockDispatch())
url = "https://example.org/redirect_body"
@ -278,7 +296,8 @@ async def test_cannot_redirect_streaming_body(backend):
await client.post(url, data=streaming_body())
async def test_cross_subdomain_redirect(backend):
@pytest.mark.usefixtures("async_environment")
async def test_cross_subdomain_redirect():
client = Client(dispatch=MockDispatch())
url = "https://example.com/cross_subdomain"
response = await client.get(url)
@ -324,7 +343,8 @@ class MockCookieDispatch(Dispatcher):
return Response(status_code, headers=headers, request=request)
async def test_redirect_cookie_behavior(backend):
@pytest.mark.usefixtures("async_environment")
async def test_redirect_cookie_behavior():
client = Client(dispatch=MockCookieDispatch())
# The client is not logged in.

View File

@ -32,6 +32,30 @@ ENVIRONMENT_VARIABLES = {
}
@pytest.fixture(
params=[
pytest.param("asyncio", marks=pytest.mark.asyncio),
pytest.param("trio", marks=pytest.mark.trio),
]
)
def async_environment(request: typing.Any) -> str:
"""
Mark a test function to be run on both asyncio and trio.
Equivalent to having a pair of tests, each respectively marked with
'@pytest.mark.asyncio' and '@pytest.mark.trio'.
Intended usage:
```
@pytest.mark.usefixtures("async_environment")
async def my_async_test():
...
```
"""
return request.param
@pytest.fixture(scope="function", autouse=True)
def clean_environ() -> typing.Dict[str, typing.Any]:
"""Keeps os.environ clean for every test without having to mock os.environ"""
@ -49,21 +73,6 @@ def clean_environ() -> typing.Dict[str, typing.Any]:
os.environ.update(original_environ)
@pytest.fixture(
params=[
# pytest uses the marks to set up the specified async environment and run
# 'async def' test functions. The "auto" backend should then auto-detect
# the environment it's running in.
# Passing the backend explicitly, e.g. `backend="asyncio"`,
# is tested separately.
pytest.param("auto", marks=pytest.mark.asyncio),
pytest.param("auto", marks=pytest.mark.trio),
]
)
def backend(request):
return request.param
async def app(scope, receive, send):
assert scope["type"] == "http"
if scope["path"].startswith("/slow_response"):
@ -269,19 +278,17 @@ class TestServer(Server):
@pytest.fixture
def restart(backend):
def restart():
"""Restart the running server from an async test function.
This fixture deals with possible differences between the environment of the
test function and that of the server.
"""
asyncio_backend = AsyncioBackend()
backend_implementation = lookup_backend(backend)
async def restart(server):
await backend_implementation.run_in_threadpool(
asyncio_backend.run, server.restart
)
backend = lookup_backend()
await backend.run_in_threadpool(asyncio_backend.run, server.restart)
return restart

View File

@ -1,8 +1,11 @@
import pytest
import httpx
from httpx.dispatch.connection_pool import ConnectionPool
async def test_keepalive_connections(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_keepalive_connections(server):
"""
Connections should default to staying in a keep-alive state.
"""
@ -18,7 +21,8 @@ async def test_keepalive_connections(server, backend):
assert len(http.keepalive_connections) == 1
async def test_keepalive_timeout(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_keepalive_timeout(server):
"""
Keep-alive connections should timeout.
"""
@ -49,7 +53,8 @@ async def test_keepalive_timeout(server, backend):
assert len(http.keepalive_connections) == 0
async def test_differing_connection_keys(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_differing_connection_keys(server):
"""
Connections to differing connection keys should result in multiple connections.
"""
@ -65,7 +70,8 @@ async def test_differing_connection_keys(server, backend):
assert len(http.keepalive_connections) == 2
async def test_soft_limit(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_soft_limit(server):
"""
The soft_limit config should limit the maximum number of keep-alive connections.
"""
@ -83,7 +89,8 @@ async def test_soft_limit(server, backend):
assert len(http.keepalive_connections) == 1
async def test_streaming_response_holds_connection(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_streaming_response_holds_connection(server):
"""
A streaming request should hold the connection open until the response is read.
"""
@ -98,7 +105,8 @@ async def test_streaming_response_holds_connection(server, backend):
assert len(http.keepalive_connections) == 1
async def test_multiple_concurrent_connections(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_multiple_concurrent_connections(server):
"""
Multiple conncurrent requests should open multiple conncurrent connections.
"""
@ -120,7 +128,8 @@ async def test_multiple_concurrent_connections(server, backend):
assert len(http.keepalive_connections) == 2
async def test_close_connections(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_close_connections(server):
"""
Using a `Connection: close` header should close the connection.
"""
@ -132,7 +141,8 @@ async def test_close_connections(server, backend):
assert len(http.keepalive_connections) == 0
async def test_standard_response_close(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_standard_response_close(server):
"""
A standard close should keep the connection open.
"""
@ -144,7 +154,8 @@ async def test_standard_response_close(server, backend):
assert len(http.keepalive_connections) == 1
async def test_premature_response_close(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_premature_response_close(server):
"""
A premature close should close the connection.
"""
@ -155,9 +166,8 @@ async def test_premature_response_close(server, backend):
assert len(http.keepalive_connections) == 0
async def test_keepalive_connection_closed_by_server_is_reestablished(
server, restart, backend
):
@pytest.mark.usefixtures("async_environment")
async def test_keepalive_connection_closed_by_server_is_reestablished(server, restart):
"""
Upon keep-alive connection closed by remote a new connection
should be reestablished.
@ -175,8 +185,9 @@ async def test_keepalive_connection_closed_by_server_is_reestablished(
assert len(http.keepalive_connections) == 1
@pytest.mark.usefixtures("async_environment")
async def test_keepalive_http2_connection_closed_by_server_is_reestablished(
server, restart, backend
server, restart
):
"""
Upon keep-alive connection closed by remote a new connection
@ -195,7 +206,8 @@ async def test_keepalive_http2_connection_closed_by_server_is_reestablished(
assert len(http.keepalive_connections) == 1
async def test_connection_closed_free_semaphore_on_acquire(server, restart, backend):
@pytest.mark.usefixtures("async_environment")
async def test_connection_closed_free_semaphore_on_acquire(server, restart):
"""
Verify that max_connections semaphore is released
properly on a disconnected connection.

View File

@ -4,7 +4,8 @@ import httpx
from httpx.dispatch.connection import HTTPConnection
async def test_get(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_get(server):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request("GET", server.url)
await response.read()
@ -12,13 +13,15 @@ async def test_get(server, backend):
assert response.content == b"Hello, world!"
async def test_post(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_post(server):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request("GET", server.url, data=b"Hello, world!")
assert response.status_code == 200
async def test_premature_close(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_premature_close(server):
with pytest.raises(httpx.ConnectionClosed):
async with HTTPConnection(origin=server.url) as conn:
response = await conn.request(
@ -27,7 +30,8 @@ async def test_premature_close(server, backend):
await response.read()
async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file, backend):
@pytest.mark.usefixtures("async_environment")
async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file):
"""
An HTTPS request, with default SSL configuration set on the client.
"""
@ -38,7 +42,8 @@ async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file, backe
assert response.content == b"Hello, world!"
async def test_https_get_with_sll_overrides(https_server, ca_cert_pem_file, backend):
@pytest.mark.usefixtures("async_environment")
async def test_https_get_with_sll_overrides(https_server, ca_cert_pem_file):
"""
An HTTPS request, with SSL configuration set on the request.
"""

View File

@ -102,7 +102,8 @@ async def test_http2_reconnect():
assert json.loads(response_2.content) == {"method": "GET", "path": "/2", "body": ""}
async def test_http2_settings_in_handshake(backend):
@pytest.mark.asyncio
async def test_http2_settings_in_handshake():
backend = MockHTTP2Backend(app=app)
async with Client(backend=backend, http2=True) as client:
@ -137,7 +138,8 @@ async def test_http2_settings_in_handshake(backend):
assert changed_setting.new_value == expected_settings[setting_code]
async def test_http2_live_request(backend):
@pytest.mark.usefixtures("async_environment")
async def test_http2_live_request():
async with Client(http2=True) as client:
try:
resp = await client.get("https://nghttp2.org/httpbin/anything")

View File

@ -5,7 +5,8 @@ import httpx
from .utils import MockRawSocketBackend
async def test_proxy_tunnel_success(backend):
@pytest.mark.asyncio
async def test_proxy_tunnel_success():
raw_io = MockRawSocketBackend(
data_to_send=(
[
@ -41,8 +42,9 @@ async def test_proxy_tunnel_success(backend):
assert recv[2].startswith(b"GET / HTTP/1.1\r\nhost: example.com\r\n")
@pytest.mark.asyncio
@pytest.mark.parametrize("status_code", [300, 304, 308, 401, 500])
async def test_proxy_tunnel_non_2xx_response(backend, status_code):
async def test_proxy_tunnel_non_2xx_response(status_code):
raw_io = MockRawSocketBackend(
data_to_send=(
[
@ -78,7 +80,8 @@ async def test_proxy_tunnel_non_2xx_response(backend, status_code):
)
async def test_proxy_tunnel_start_tls(backend):
@pytest.mark.asyncio
async def test_proxy_tunnel_start_tls():
raw_io = MockRawSocketBackend(
data_to_send=(
[
@ -141,8 +144,9 @@ async def test_proxy_tunnel_start_tls(backend):
assert recv[4].startswith(b"GET /target HTTP/1.1\r\nhost: example.com\r\n")
@pytest.mark.asyncio
@pytest.mark.parametrize("proxy_mode", ["FORWARD_ONLY", "DEFAULT"])
async def test_proxy_forwarding(backend, proxy_mode):
async def test_proxy_forwarding(proxy_mode):
raw_io = MockRawSocketBackend(
data_to_send=(
[

View File

@ -29,8 +29,9 @@ async def read_response(stream, timeout: Timeout, should_contain: bytes) -> byte
return response
async def test_start_tls_on_tcp_socket_stream(https_server, backend):
backend = lookup_backend(backend)
@pytest.mark.usefixtures("async_environment")
async def test_start_tls_on_tcp_socket_stream(https_server):
backend = lookup_backend()
ctx = SSLConfig().load_ssl_context_no_verify()
timeout = Timeout(5)
@ -55,8 +56,9 @@ async def test_start_tls_on_tcp_socket_stream(https_server, backend):
await stream.close()
async def test_start_tls_on_uds_socket_stream(https_uds_server, backend):
backend = lookup_backend(backend)
@pytest.mark.usefixtures("async_environment")
async def test_start_tls_on_uds_socket_stream(https_uds_server):
backend = lookup_backend()
ctx = SSLConfig().load_ssl_context_no_verify()
timeout = Timeout(5)
@ -81,11 +83,12 @@ async def test_start_tls_on_uds_socket_stream(https_uds_server, backend):
await stream.close()
async def test_concurrent_read(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_concurrent_read(server):
"""
Regression test for: https://github.com/encode/httpx/issues/527
"""
backend = lookup_backend(backend)
backend = lookup_backend()
stream = await backend.open_tcp_stream(
server.url.host, server.url.port, ssl_context=None, timeout=Timeout(5)
)

View File

@ -3,7 +3,8 @@ import pytest
import httpx
async def test_read_timeout(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_read_timeout(server):
timeout = httpx.Timeout(read_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
@ -11,7 +12,8 @@ async def test_read_timeout(server, backend):
await client.get(server.url.copy_with(path="/slow_response"))
async def test_write_timeout(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_write_timeout(server):
timeout = httpx.Timeout(write_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
@ -20,7 +22,8 @@ async def test_write_timeout(server, backend):
await client.put(server.url.copy_with(path="/slow_response"), data=data)
async def test_connect_timeout(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_connect_timeout(server):
timeout = httpx.Timeout(connect_timeout=1e-6)
async with httpx.Client(timeout=timeout) as client:
@ -29,7 +32,8 @@ async def test_connect_timeout(server, backend):
await client.get("http://10.255.255.1/")
async def test_pool_timeout(server, backend):
@pytest.mark.usefixtures("async_environment")
async def test_pool_timeout(server):
pool_limits = httpx.PoolLimits(hard_limit=1)
timeout = httpx.Timeout(pool_timeout=1e-4)