import pytest
from eggfetch.compat.httpx import (
Auth,
AsyncClient,
Client,
MockTransport,
Request,
Response,
)
class MultiStepAuth(Auth):
def auth_flow(self, request):
request.headers["x-step"] = "1"
response = yield request
if response.status_code == 401:
request.headers["x-step"] = "2"
response = yield request
class TestSyncHookAuthOrdering:
def test_full_ordering_sync(self):
event_log = []
def on_request(request):
event_log.append(("request_hook", request.headers.get("x-step", "none")))
def on_response(response):
event_log.append(("response_hook", response.status_code))
def handler(request):
step = request.headers.get("x-step", "")
if step == "1":
return Response(401)
return Response(200, text="done")
with Client(
auth=MultiStepAuth(),
transport=MockTransport(handler),
event_hooks={"request": [on_request], "response": [on_response]},
) as client:
resp = client.get("http://testserver/")
assert resp.status_code == 200
assert event_log == [
("request_hook", "1"),
("response_hook", 401),
("request_hook", "2"),
("response_hook", 200),
]
def test_auth_modifies_request_before_hook(self):
hook_saw = []
def on_request(request):
hook_saw.append(request.headers.get("authorization", "missing"))
class SimpleAuth(Auth):
def auth_flow(self, request):
request.headers["authorization"] = "Bearer token"
yield request
def handler(request):
return Response(200, text=request.headers.get("authorization", "none"))
with Client(
auth=SimpleAuth(),
transport=MockTransport(handler),
event_hooks={"request": [on_request], "response": []},
) as client:
resp = client.get("http://testserver/")
assert hook_saw == ["Bearer token"]
assert resp.text == "Bearer token"
def test_response_hook_on_every_hop(self):
response_log = []
def on_response(response):
response_log.append(response.status_code)
def handler(request):
step = request.headers.get("x-step", "")
if step == "1":
return Response(401)
return Response(200)
with Client(
auth=MultiStepAuth(),
transport=MockTransport(handler),
event_hooks={"request": [], "response": [on_response]},
) as client:
resp = client.get("http://testserver/")
assert response_log == [401, 200]
def test_request_hook_error_prevents_dispatch(self):
def bad_hook(request):
raise RuntimeError("hook abort")
def handler(request):
return Response(200, content=b"should-not-reach")
with Client(
transport=MockTransport(handler),
event_hooks={"request": [bad_hook], "response": []},
) as client:
with pytest.raises(RuntimeError, match="hook abort"):
client.get("http://testserver/")
class TestAsyncHookAuthOrdering:
@pytest.mark.asyncio
async def test_full_ordering_async(self):
event_log = []
async def on_request(request):
event_log.append(("request_hook", request.headers.get("x-step", "none")))
async def on_response(response):
event_log.append(("response_hook", response.status_code))
async def handler(request):
step = request.headers.get("x-step", "")
if step == "1":
return Response(401)
return Response(200, text="done")
async with AsyncClient(
auth=MultiStepAuth(),
async_transport=MockTransport(handler),
event_hooks={"request": [on_request], "response": [on_response]},
) as client:
resp = await client.get("http://testserver/")
assert resp.status_code == 200
assert event_log == [
("request_hook", "1"),
("response_hook", 401),
("request_hook", "2"),
("response_hook", 200),
]
@pytest.mark.asyncio
async def test_async_auth_modifies_request_before_hook(self):
hook_saw = []
async def on_request(request):
hook_saw.append(request.headers.get("authorization", "missing"))
class SimpleAuth(Auth):
def auth_flow(self, request):
request.headers["authorization"] = "Bearer async-token"
yield request
async def handler(request):
return Response(200, text=request.headers.get("authorization", "none"))
async with AsyncClient(
auth=SimpleAuth(),
async_transport=MockTransport(handler),
event_hooks={"request": [on_request], "response": []},
) as client:
resp = await client.get("http://testserver/")
assert hook_saw == ["Bearer async-token"]
assert resp.text == "Bearer async-token"
@pytest.mark.asyncio
async def test_async_response_hook_on_every_hop(self):
response_log = []
async def on_response(response):
response_log.append(response.status_code)
async def handler(request):
step = request.headers.get("x-step", "")
if step == "1":
return Response(401)
return Response(200)
async with AsyncClient(
auth=MultiStepAuth(),
async_transport=MockTransport(handler),
event_hooks={"request": [], "response": [on_response]},
) as client:
resp = await client.get("http://testserver/")
assert response_log == [401, 200]