test: migrate aioresponses → aiointercept; lift aiohttp to >=3.14.1 (resolves 11 security alerts)#22
Merged
Conversation
aioresponses 0.7.8 (latest, unmaintained) breaks on aiohttp 3.14's required ClientResponse.stream_writer kwarg, which forced an aiohttp <3.14 cap. That cap held back a batch of client-side aiohttp security fixes (CVE-2026-34993, -47265, the -502xx and -542xx series — 11 Dependabot alerts), since every fix lands in 3.14.0/3.14.1. Migrate the aiohttp test mocking to aiointercept, which supports aiohttp 3.14 (it routes requests through a real localhost test server and patches the DNS resolver to intercept external hosts). Its registration surface is aioresponses-compatible (get/head/..., CallbackResult, callback(url, *, headers, ...)), so test bodies are largely unchanged. - tests/_aiomock.py: new sync adapter. aiointercept is an async context manager and the fetch tests are synchronous (fetch_url runs aiohttp on its own daemon thread), so mock_aiohttp() drives start()/stop() on a dedicated lifecycle thread — robust whether or not the caller already has a running event loop (covers the nested-event-loop test). Uses mock_external_urls=True so presigned URLs are intercepted. - tests/test_external_fetch.py / test_external.py / test_http.py / test_otel.py: swap imports to the adapter; exception=<specific> -> exception=True (aiointercept raises ClientConnectionError, still satisfying the existing ClientError / "Failed to resolve" assertions). - test_invalid_content_length_header: a real server can't emit a malformed Content-Length, so this now unit-tests _head_probe's defensive parse directly. - pyproject.toml: external extra aiohttp>=3.9,<3.14 -> aiohttp>=3.14.1; drop the aioresponses dev dep + its mypy override; add aiointercept (ships py.typed). - .github/dependabot.yml: drop the now-obsolete aiohttp >=3.14 ignore. Full suite: 3482 passed, 151 skipped; mypy/ruff/pydoclint/ty clean. Wall time improved (~129s -> ~99s vs the aioresponses baseline). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| loop = asyncio.new_event_loop() | ||
| try: | ||
| loop.run_until_complete(coro) | ||
| except BaseException as exc: # surface failures to the caller thread |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
11 open Dependabot alerts on aiohttp (CVE-2026-34993, -47265, the -502xx / -542xx series) are all fixed in aiohttp 3.14.0/3.14.1 — but we were pinned
aiohttp<3.14because aioresponses 0.7.8 (latest, effectively unmaintained) breaks on aiohttp 3.14's requiredClientResponse.stream_writerkwarg. The cap was the only thing blocking the security fixes.What
Migrate the aiohttp test mocking from aioresponses to aiointercept, which supports aiohttp 3.14 (it routes requests through a real localhost test server + DNS-resolver patching). Its registration API is aioresponses-compatible, so test bodies are largely unchanged.
tests/_aiomock.py(new) — a small sync adapter. aiointercept is an async context manager, but the fetch tests are synchronous (fetch_urlruns aiohttp on its own daemon thread).mock_aiohttp()drivesstart()/stop()on a dedicated lifecycle thread, so it works whether or not the caller already has a running loop (coversTestNestedEventLoop). Usesmock_external_urls=Trueso presigned URLs are intercepted.exception=<specific>→exception=True(aiointercept raisesClientConnectionError, still satisfying the existingaiohttp.ClientError/"Failed to resolve"assertions).test_invalid_content_length_header— a real server can't emit a malformedContent-Length, so this now unit-tests_head_probe's defensive parse directly (same branch, faithful intent).pyproject.toml—externalextraaiohttp>=3.9,<3.14→aiohttp>=3.14.1; drop the aioresponses dev dep + its mypy override; addaiointercept(shipspy.typed)..github/dependabot.yml— drop the now-obsoleteaiohttp >=3.14ignore.Verification
mypy/ruff/pydoclint/tyall clean.🤖 Generated with Claude Code