Commit 5260f63
test: close deep-audit test-quality findings (#65)
* docs(planning): audit-test-quality change bundle
* test(errors): cover sync Client terminal status-raising + exception mapping
Add sync mirrors for every async case in test_error_mapping_terminal.py:
2xx pass-through, typed status subclasses, unknown 4xx/5xx fallbacks, 3xx
no-raise, timeout/connect/invalid-url/decoding-error transport mapping, and
closed-client TransportError. Uses Client(httpx2_client=httpx2.Client(...))
pattern per CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(errors): cover CookieConflict→TransportError mapping branch
Add async and sync tests asserting that httpx2.CookieConflict raised inside a
transport handler surfaces as TransportError (not NetworkError), covering the
mapping branch that was exercised only for InvalidURL.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(errors): assert StatusError leaves do not override __init__
Parametrize over all nine StatusError leaf subclasses and assert that none
defines __init__ in its own __dict__, enforcing the CLAUDE.md invariant that
leaf classes must not override StatusError.__init__.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(errors): construct ForbiddenError/ConflictError/UnprocessableEntityError
Extend test_per_status_subclasses_construct parametrize from 6 to all 9 leaf
status subclasses, adding 403/ForbiddenError, 409/ConflictError, and
422/UnprocessableEntityError. Also smoke-tests __str__ on each.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(client): sync mirrors for status-before-decode and DecodeError-is-ClientError
Add test_sync_status_error_raised_before_decoder_runs (4xx with response_model
raises StatusError, not DecodeError) and test_sync_decode_error_caught_by_client_error
(malformed body raises DecodeError which is-a ClientError) for the sync Client,
mirroring the existing async counterparts.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(client): sync Client overload typing tests
Add sync mirrors for all four AsyncClient overload tests: get/send ×
with/without response_model. Verifies at runtime that the sync Client's
overloads resolve httpx2.Response vs typed model correctly.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(retry): correct test_retry_props description (sequential bounds, not interleaving)
The module docstring implied concurrent "retry interleaving" but every test
issues one sequential request per Hypothesis example. Rewrite the docstring
to accurately say it tests sequential retry-policy bounds and explicitly point
to test_threading_with_shared_budget.py for the concurrent case.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(bulkhead): replace flaky sleep with deterministic slot-acquisition barrier
The old test used time.sleep(0.005) hoping all holder threads had acquired
their bulkhead slots before issuing the over-limit requests. On slow CI a
holder might not have acquired yet, causing a spurious pass of the extra
request and a false test outcome.
Replace the sleep with threading.Barrier(max_concurrent + 1, timeout=5.0).
_BarrierHandler.wait() is called from inside each holder's handler — meaning
the slot is already held — and the main thread joins the same barrier. The
main thread only proceeds past the barrier once every holder has acquired
its slot, making the synchronization fully deterministic.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(circuit_breaker): cover TimeoutError as a failure trigger (sync+async)
The circuit breaker catches both NetworkError and TimeoutError as counted
failures, but tests only covered NetworkError (via ConnectError). Add
test_timeout_error_counts_as_failure to both test_circuit_breaker.py (async)
and test_circuit_breaker_sync.py (sync): a handler raising httpx2.ReadTimeout
(maps to httpware TimeoutError) with failure_threshold=2 causes two such
requests to open the circuit, and the third raises CircuitOpenError.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(budget): pin clock so the no-purge deposit-count invariant is enforced
The deposit-count assertion relied on a comment saying the 60s TTL means no
_purge fires during the sub-second run. RetryBudget accepts an injectable
_now clock, so inject _fixed_clock (always returns 0.0). All deposit
timestamps are 0.0, the purge cutoff is 0.0 - 60.0 = -60.0, and the strict
"< cutoff" predicate is never true — making the no-purge guarantee a
provable invariant rather than a time-dependent assumption.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(observability): assert log record fires in the no-active-span path
The test previously passed even if _emit_event became a no-op because its
only assertion was "no exception raised." Strengthen it with caplog: capture
at WARNING level on the test logger and assert that exactly one record fires
with the correct level, message, and event attribute. This confirms the
log-only fallback path actually emitted (not silently swallowed) when OTel
is installed but no tracer is active.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* test(circuit_breaker): suppress A004 for intentional TimeoutError import shadow
httpware.TimeoutError deliberately shadows the Python builtin (noqa A001 at
the definition site). Tests importing it need noqa A004 at the import line.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* style: import Callable from collections.abc (ruff UP035)
An auto-fix left uncommitted by the test-quality sweep; CI lint-ci (no
autofix) caught the committed typing.Callable import.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>1 parent 6a7f6d1 commit 5260f63
11 files changed
Lines changed: 367 additions & 22 deletions
File tree
- planning/changes/active/2026-06-14.05-audit-test-quality
- tests
Lines changed: 40 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
76 | 90 | | |
77 | 91 | | |
78 | 92 | | |
| |||
82 | 96 | | |
83 | 97 | | |
84 | 98 | | |
85 | | - | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
86 | 104 | | |
87 | 105 | | |
88 | 106 | | |
| |||
92 | 110 | | |
93 | 111 | | |
94 | 112 | | |
95 | | - | |
96 | | - | |
| 113 | + | |
| 114 | + | |
97 | 115 | | |
98 | 116 | | |
99 | 117 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
169 | 170 | | |
170 | 171 | | |
171 | 172 | | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
172 | 187 | | |
173 | 188 | | |
174 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
140 | 141 | | |
141 | 142 | | |
142 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
143 | 158 | | |
144 | 159 | | |
145 | 160 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
102 | 120 | | |
103 | 121 | | |
104 | 122 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
0 commit comments