Draft
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files🚀 New features to boost your workflow:
|
NouemanKHAL
reviewed
Feb 20, 2026
NouemanKHAL
reviewed
Feb 20, 2026
NouemanKHAL
reviewed
Feb 20, 2026
datadog_checks_base/datadog_checks/base/utils/http_exceptions.py
Outdated
Show resolved
Hide resolved
beb72ad to
07a9003
Compare
3 tasks
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This was referenced Feb 20, 2026
97854d1 to
8fc9e4f
Compare
This was referenced Feb 23, 2026
102bf30 to
6f90874
Compare
* Add HTTP protocol definitions for httpx migration Introduce HTTPClientProtocol and HTTPResponseProtocol as library-agnostic interfaces, enabling test decoupling before migrating from requests to httpx. These protocols define the contract that both RequestsWrapper and the future HTTPXClient must implement. There should be zero runtime overhead and no impact on existing code. * Add changelog entry * Add pragma no cover to protocol stubs for coverage Protocol method stubs with ellipsis are compile-time type hints that are never executed. Add pragma no cover comments to indicate these lines are intentionally not covered by runtime tests. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add close() method to HTTPResponseProtocol Production code calls response.close() in finally blocks to release connections back to the pool (e.g., openmetrics/mixins.py:549, openmetrics/v2/scraper/base_scraper.py:447, prometheus/mixins.py:388). Without close() in the protocol, new HTTP client implementations could pass protocol checks but raise AttributeError when these code paths execute, or leak connections. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Apply suggestions from code review Co-authored-by: NouemanKHAL <noueman.khalikine@datadoghq.com> * Apply suggestion from @NouemanKHAL Co-authored-by: NouemanKHAL <noueman.khalikine@datadoghq.com> * Apply suggestion from @NouemanKHAL, delete this file Co-authored-by: NouemanKHAL <noueman.khalikine@datadoghq.com> * Remove protocol interface tests and fix type annotation Delete test_http_protocol.py as testing protocol interfaces provides no value - we would only be testing Python's structural subtyping internals rather than actual implementation behavior. Also fix typo in HTTPResponseProtocol.__enter__ return type annotation where ResponseProtocol was referenced instead of HTTPResponseProtocol. * Run code formatter (remove trailing newline) --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: NouemanKHAL <noueman.khalikine@datadoghq.com>
Introduces HTTPError, HTTPRequestError, HTTPStatusError, HTTPTimeoutError, HTTPConnectionError, and HTTPSSLError as abstract, library-agnostic exception types. These provide a stable interface for the httpx migration — checks that handle HTTP errors can catch these types without caring whether the underlying client is requests or httpx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…de behavior Both methods return bytes when decode_unicode=False and str when decode_unicode=True. Iterator[bytes] was wrong for the decode_unicode=True case (e.g. the openmetrics mixin calls iter_lines(decode_unicode=True) and processes results as strings). The precise fix would use @overload with Literal[True]/Literal[False] but that adds verbosity with no practical benefit at this stage. Iterator[bytes | str] is the honest union — correct for both runtime cases. Also aligns delimiter type to bytes | str | None, matching http_testing.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add MockHTTPResponse for library-agnostic HTTP response mocking MockHTTPResponse implements HTTPResponseProtocol without depending on requests or httpx. It supports the full response API (.json(), .text, .content, .status_code, .headers, .cookies, .elapsed), streaming via iter_content() and iter_lines(), raise_for_status() using HTTPStatusError, and context manager usage. Demonstrates usage by migrating test_authtoken.py from MockResponse to MockHTTPResponse — a drop-in replacement with protocol compliance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Expand MockHTTPResponse usage to test_openmetrics.py and test_kerberos_unit.py Also adds three missing API surface members discovered during migration: - encoding attribute (accessed by openmetrics mixin before iter_lines) - close() no-op (called by openmetrics mixin after response processing) - Removes _stream_consumed guard so the same instance can be reused across repeated mock.MagicMock(return_value=...) calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Reformat with ddev test --fmt * Fix headers mutation and add case-insensitive header dict Two correctness fixes in MockHTTPResponse: 1. When json_data and headers are both provided, the caller's headers dict was mutated in-place via setdefault(). Now copies before modifying. 2. self.headers was a plain dict (case-sensitive). HTTP headers are case-insensitive per RFC 7230 §3.2. Replace with _CaseInsensitiveDict that stores keys lowercased, matching requests.Response behaviour. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No other file in the codebase uses module-level docstrings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds convenience re-exports so integrations can import HTTP exceptions from the existing datadog_checks.base.utils.http import path rather than a new module, supporting a phased migration to httpx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sponse Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Redesign mock_http fixture to patch RequestsWrapper at class level
Patches get/post/put/delete/head/patch on RequestsWrapper so all three
HTTP paths are intercepted: AgentCheck.http, OpenMetrics V2 scraper,
and kube* health check handlers. Real RequestsWrapper instances are
still created, so check.http.options remains accessible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate falco tests from requests.Session.get patch to mock_http fixture
Replace direct mock.patch('requests.Session.get') with the library-agnostic
mock_http fixture and MockHTTPResponse from http_testing.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate strimzi tests from requests.Session.get patch to mock_http fixture
Replace direct mocker.patch('requests.Session.get') with the library-agnostic
mock_http fixture and MockHTTPResponse from http_testing.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix E402: move pytest_plugins after all imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate couchbase tests from requests.Session.get patch to mock_http fixture
Replace direct mocker.patch('requests.Session.get') with the library-agnostic
mock_http fixture and MockHTTPResponse from http_testing.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Rename mock_http fixture to mock_http_client
The fixture mocks the HTTP client (RequestsWrapper), not responses.
mock_http_client is more precise and avoids confusion with the existing
mock_http_response fixture from datadog_checks.dev.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate ray tests from requests.Session.get patch to mock_http_client fixture
Replace direct mocker.patch('requests.Session.get') with the library-agnostic
mock_http_client fixture and MockHTTPResponse from http_testing.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate tekton tests from requests.Session.get patch to mock_http_client fixture
Replace direct mocker.patch('requests.Session.get') with the library-agnostic
mock_http_client fixture and MockHTTPResponse from http_testing.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Switch from pytest_plugins to direct import for fixture registration
Use direct import with noqa: F401 to match the existing codebase pattern.
No other integration uses pytest_plugins for fixture registration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert rename: mock_http_client → mock_http
The fixture name mock_http_client was added in the previous commit, but the
team prefers the shorter mock_http name. Revert all usages across integrations
and the fixture definition in http_testing.py.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Redesign mock_http: use create_autospec(HTTPClientProtocol) + PropertyMock on AgentCheck.http
- mock_http now patches AgentCheck.http via PropertyMock with a create_autospec(HTTPClientProtocol)
client, constraining the mock to the protocol interface and enforcing call signatures
- AgentCheck.http return type updated from RequestsWrapper to HTTPClientProtocol
- OM V2 scraper reuses check.http directly instead of constructing its own RequestsWrapper;
options access guarded with hasattr for mock compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Shorten docstring
* Migrate kubevirt_api, kubevirt_controller, kubevirt_handler tests to mock_http fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix Accept header mutation side-effect in OM V2 scraper
base_scraper.py shares check.http via `self.http = check.http`. The
previous code mutated `self.http.options['headers']['Accept']` which
bled through to check.http since they are the same object, breaking
tests that assert check.http.options['headers']['Accept'] == '*/*'.
Fix: store the accept header as self._accept_header and inject it
per-request via extra_headers in send_request. Using extra_headers
(not headers=) is required because RequestsWrapper._request uses
ChainMap — passing headers= directly would shadow the entire session
headers dict rather than merging a single key.
Update test assertions in three files to check scraper._accept_header
instead of scraper.http.options['headers']['Accept'].
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix bentoml tests broken by scraper sharing check.http
After self.http = check.http in the OM V2 scraper, patching
BentomlCheck.http at the class level intercepted scraper HTTP calls too,
causing AttributeError on the minimal mock response. Replace both
patch('BentomlCheck.http') blocks with URL-based dispatch on the
already-patched requests.Session.get mock.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix Vault auth headers lost when OM V2 scraper reuses check.http
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Raise NotImplementedError for options_method in mock_http fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Move mock_http fixture to datadog_checks_dev pytest plugin
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove call_count assertion from bentoml test
Asserting on the number of HTTP calls ties the test to implementation
details rather than observable behavior (metrics, service checks).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Drop unused get_mock variable in bentoml test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- base_scraper: use self.check.http instead of local check.http parameter - base_scraper: move accept_header from instance variable to local in send_request - openmetrics tests: replace scraper._accept_header assertion with call_args header inspection - bentoml tests: remove duplicate assert_all_metrics_covered call - bentoml tests: replace requests.HTTPError with HTTPStatusError from http_exceptions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…_accept_header Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rim comments Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add get_header/set_header (case-insensitive) to RequestsWrapper and HTTPClientProtocol, then migrate 16 integration test suites from patching requests.Session internals to asserting against check.http.options and using the mock_http fixture. - Add get_header/set_header to RequestsWrapper with case-insensitive key lookup; use set_header in Vault's __init__ auth header setup - Extend HTTPClientProtocol with options property and get/set_header - Migrate config assertion tests: airflow, consul, couch, druid, ecs_fargate, envoy, etcd, gitlab_runner, marathon, mesos_master, mesos_slave, nginx, openmetrics, php_fpm, squid, teamcity - Update mock_http fixture to expose client.options as a real dict - Flatten test classes to top-level functions in test_headers.py - Remove dead assertions in rabbitmq and torchserve tests
932c02a to
a05a40a
Compare
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.

What does this PR do?
Introduces library-agnostic HTTP infrastructure and decouples integration tests from
requests, laying the groundwork for therequests→httpxmigration across ~76 integrations.Motivation
Migrating from
requeststohttpxrequires decoupling tests fromrequestsinternals first — otherwise regressions are undetectable. This PR establishes that boundary. See the RFC for full context.Approach
Phase 1: Protocol definitions —
HTTPClientProtocol,HTTPResponseProtocol, a library-agnostic exception hierarchy, andMockHTTPResponse(response object independent ofrequests.Response). Re-exports inhttp.pyfor convenient imports.Step 1 (PR #22710, merged) —
mock_httppytest fixture (auto-loaded via ddev plugin) that patchesAgentCheck.httpat the property level. The OM V2 scraper was changed to reusecheck.http, so a single patch covers both. 9 integrations migrated.Step 2 (PR #22722, merged) — Added
get_header()/set_header()(case-insensitive) toRequestsWrapperandHTTPClientProtocol. Migrated 19 config assertion tests from patchingrequests.Sessionto asserting againstcheck.http.optionsandcheck.http.get_header(). Production code in vault updated to useset_header(). Mock fixture extended withclient.optionsas a real dict.28 integrations now use the library-agnostic test layer on this branch.
See: Test Decoupling Plan, Step 1, Step 2
Status
Work in progress. This PR is the feature branch — subsequent steps are stacked PRs that merge here after review.
Verification
MockHTTPResponsecovered by dedicated testsddev test -fsReview checklist (to be filled by reviewers)
qa/skip-qalabel if the PR doesn't need to be tested during QA.backport/<branch-name>label to the PR and it will automatically open a backport PR once this one is merged