Skip to content

Commit c7c185f

Browse files
lesnik512claude
andcommitted
chore(coverage): close branch-coverage gaps under tightened --cov=. gate
Add targeted tests for uncovered branches in client.py (cookies/limits/auth kwargs, InvalidURL exception mapping, RuntimeError re-raise, and all _request_with_body optional-kwarg branches) and in middleware/__init__.py (__repr__ methods on before_request/after_response/on_error wrappers). Apply # pragma: no cover to test-helper methods that are structural placeholders never invoked (protocol-check __call__, default-arg guards, and the unreachable swallow_all body in the CancelledError test). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent da5ed1c commit c7c185f

4 files changed

Lines changed: 117 additions & 6 deletions

File tree

‎tests/test_client_construction.py‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,32 @@ def test_default_decoder_is_pydantic_decoder() -> None:
5555

5656
def test_explicit_decoder_is_honored() -> None:
5757
class _Stub:
58-
def decode(self, content: bytes, model: type) -> object: # noqa: ARG002
58+
def decode(self, content: bytes, model: type) -> object: # noqa: ARG002 # pragma: no cover
5959
return None
6060

6161
client = AsyncClient(decoder=_Stub())
6262
assert isinstance(client._decoder, _Stub) # noqa: SLF001
6363

6464

65+
@pytest.mark.parametrize(
66+
"kwargs",
67+
[
68+
{"cookies": {"session": "abc"}},
69+
{"limits": httpx2.Limits(max_connections=5)},
70+
{"auth": httpx2.BasicAuth("user", "pass")},
71+
],
72+
)
73+
def test_construction_with_optional_forwarded_kwargs(kwargs: dict) -> None:
74+
"""Exercises cookies/limits/auth branches in __init__ when no httpx2_client is supplied."""
75+
client = AsyncClient(**kwargs)
76+
assert isinstance(client, AsyncClient)
77+
78+
6579
def test_explicit_middleware_is_honored() -> None:
6680
captured: list[str] = []
6781

6882
class _Tag:
69-
async def __call__(self, request, next) -> httpx2.Response: # noqa: A002, ANN001
83+
async def __call__(self, request, next) -> httpx2.Response: # noqa: A002, ANN001 # pragma: no cover
7084
captured.append("tag")
7185
return await next(request)
7286

‎tests/test_client_methods.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,63 @@ def handler(request: httpx2.Request) -> httpx2.Response:
100100
client = AsyncClient(httpx2_client=underlying)
101101
await client.get("/relative")
102102
assert str(captured[0].url) == "https://example.test/relative"
103+
104+
105+
async def test_get_with_cookies_forwarded() -> None:
106+
"""Exercises the cookies branch in _request_with_body."""
107+
captured: list[httpx2.Request] = []
108+
109+
def handler(request: httpx2.Request) -> httpx2.Response:
110+
captured.append(request)
111+
return httpx2.Response(HTTPStatus.OK, request=request)
112+
113+
client = _client_with_handler(handler)
114+
await client.get("https://example.test/x", cookies={"token": "abc"})
115+
assert "token=abc" in captured[0].headers.get("cookie", "")
116+
117+
118+
async def test_get_with_explicit_timeout() -> None:
119+
"""Exercises the timeout branch in _request_with_body."""
120+
client = _client_with_handler(_echo_handler)
121+
response = await client.get("https://example.test/x", timeout=5.0)
122+
assert response.status_code == HTTPStatus.OK
123+
124+
125+
async def test_get_with_extensions() -> None:
126+
"""Exercises the extensions branch in _request_with_body."""
127+
client = _client_with_handler(_echo_handler)
128+
response = await client.get("https://example.test/x", extensions={"trace": True})
129+
assert response.status_code == HTTPStatus.OK
130+
131+
132+
async def test_post_with_content_body() -> None:
133+
"""Exercises the content branch in _request_with_body."""
134+
client = _client_with_handler(_echo_handler)
135+
response = await client.post("https://example.test/x", content=b"raw-bytes")
136+
assert response.json()["content"] == "raw-bytes"
137+
138+
139+
async def test_post_with_data_body() -> None:
140+
"""Exercises the data branch in _request_with_body."""
141+
client = _client_with_handler(_echo_handler)
142+
response = await client.post("https://example.test/x", data={"field": "value"})
143+
assert response.status_code == HTTPStatus.OK
144+
145+
146+
async def test_post_with_files_body() -> None:
147+
"""Exercises the files branch in _request_with_body."""
148+
client = _client_with_handler(_echo_handler)
149+
response = await client.post("https://example.test/x", files={"upload": b"file-content"})
150+
assert response.status_code == HTTPStatus.OK
151+
152+
153+
async def test_runtime_error_without_closed_reraises() -> None:
154+
"""Exercises the RuntimeError re-raise branch in _terminal (error not containing 'closed')."""
155+
156+
def boom(request: httpx2.Request) -> httpx2.Response: # noqa: ARG001
157+
msg = "unexpected internal failure"
158+
raise RuntimeError(msg)
159+
160+
client = _client_with_handler(boom)
161+
with pytest.raises(RuntimeError, match="unexpected internal failure"):
162+
await client.get("https://example.test/x")

‎tests/test_error_mapping_terminal.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ def handler(request: httpx2.Request) -> httpx2.Response: # noqa: ARG001
9191
await client.send(httpx2.Request("GET", "https://example.test/x"))
9292

9393

94+
async def test_httpx2_invalid_url_maps_to_transport_error() -> None:
95+
def handler(request: httpx2.Request) -> httpx2.Response: # noqa: ARG001
96+
msg = "synthetic invalid URL from transport"
97+
raise httpx2.InvalidURL(msg)
98+
99+
client = _client_with_handler(handler)
100+
with pytest.raises(TransportError, match="synthetic invalid URL"):
101+
await client.send(httpx2.Request("GET", "https://example.test/x"))
102+
103+
94104
async def test_send_on_closed_client_raises_transport_error() -> None:
95105
transport = httpx2.MockTransport(lambda req: httpx2.Response(HTTPStatus.OK, request=req))
96106
underlying = httpx2.AsyncClient(transport=transport)

‎tests/test_middleware.py‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ def _make_request(url: str = "https://example.test/x") -> httpx2.Request:
2121

2222

2323
def _make_response(status: int = HTTPStatus.OK, *, request: httpx2.Request | None = None) -> httpx2.Response:
24-
if request is None:
24+
if request is None: # pragma: no cover
2525
request = _make_request()
2626
return httpx2.Response(status, request=request)
2727

2828

2929
async def test_middleware_protocol_is_runtime_checkable() -> None:
3030
class _OkMiddleware:
31-
async def __call__(self, request: httpx2.Request, next: Next) -> httpx2.Response: # noqa: A002
31+
async def __call__(self, request: httpx2.Request, next: Next) -> httpx2.Response: # noqa: A002 # pragma: no cover
3232
return await next(request)
3333

3434
assert isinstance(_OkMiddleware(), Middleware)
@@ -104,7 +104,7 @@ async def test_on_error_decorator_can_translate_exception() -> None:
104104
async def swallow(request: httpx2.Request, exc: Exception) -> httpx2.Response | None:
105105
if isinstance(exc, RuntimeError) and str(exc) == "boom":
106106
return _make_response(HTTPStatus.SERVICE_UNAVAILABLE, request=request)
107-
return None
107+
return None # pragma: no cover
108108

109109
async def terminal(request: httpx2.Request) -> httpx2.Response: # noqa: ARG001
110110
msg = "boom"
@@ -132,12 +132,39 @@ async def terminal(request: httpx2.Request) -> httpx2.Response: # noqa: ARG001
132132
await dispatch(_make_request())
133133

134134

135+
def test_before_request_repr() -> None:
136+
@before_request
137+
async def my_transform(request: httpx2.Request) -> httpx2.Request:
138+
return request # pragma: no cover
139+
140+
assert "before_request" in repr(my_transform)
141+
assert "my_transform" in repr(my_transform)
142+
143+
144+
def test_after_response_repr() -> None:
145+
@after_response
146+
async def my_transform(request: httpx2.Request, response: httpx2.Response) -> httpx2.Response: # noqa: ARG001
147+
return response # pragma: no cover
148+
149+
assert "after_response" in repr(my_transform)
150+
assert "my_transform" in repr(my_transform)
151+
152+
153+
def test_on_error_repr() -> None:
154+
@on_error
155+
async def my_handler(request: httpx2.Request, exc: Exception) -> httpx2.Response | None: # noqa: ARG001
156+
return None # pragma: no cover
157+
158+
assert "on_error" in repr(my_handler)
159+
assert "my_handler" in repr(my_handler)
160+
161+
135162
async def test_on_error_lets_cancelled_propagate() -> None:
136163
@on_error
137164
async def swallow_all(
138165
request: httpx2.Request, # noqa: ARG001
139166
exc: Exception, # noqa: ARG001
140-
) -> httpx2.Response | None:
167+
) -> httpx2.Response | None: # pragma: no cover
141168
msg = "should not catch CancelledError"
142169
raise AssertionError(msg)
143170

0 commit comments

Comments
 (0)