Skip to content

Swap mock_http_response to MockHTTPResponse#22935

Merged
mwdd146980 merged 8 commits intomwdd146980/httpx-migration-basefrom
mwdd146980/step3b-fixture-swap
Apr 5, 2026
Merged

Swap mock_http_response to MockHTTPResponse#22935
mwdd146980 merged 8 commits intomwdd146980/httpx-migration-basefrom
mwdd146980/step3b-fixture-swap

Conversation

@mwdd146980
Copy link
Copy Markdown
Contributor

@mwdd146980 mwdd146980 commented Mar 17, 2026

Motivation

Part of the httpx migration: decouple the shared mock_http_response test fixture from the requests library so individual integration tests don't need to change when we swap HTTP backends.

Approach

  • Swap the mock_http_response fixture from MockResponse (requests-backed) to MockHTTPResponse (library-agnostic) in the central pytest plugin — this implicitly covers ~102 test files (228 calls) without touching individual test code
  • 13 files needed explicit changes: 9 match string updates, sonatype_nexus fixture override removal, and base package updates
  • Re-add _CaseInsensitiveDict to MockHTTPResponse.headers — previously unnecessary when only used in controlled new tests, now required because production code (e.g. prometheus/mixins.py) accesses response.headers['Content-Type']
  • Fix sonatype_nexus test bug: dict.update() returns None, so json_data=data.update({...}) was passing an empty response body — now builds the dict before passing it

See plan for more details.

agint-review follow-ups

Addressed nits from the agint-review findings:

  • Fix _CaseInsensitiveDict.update() to handle iterable-of-pairs (aligns with httpx's Headers interface)
  • Add test_mock_response_url() for MockHTTPResponse.url coverage
  • Remove dead URL positional args from 7 sonatype_nexus tests

Verification

  • ddev test -fs datadog_checks_base — clean
  • ddev test datadog_checks_base — 1280 passed
  • ddev test sonatype_nexus — 13 passed

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Add the qa/skip-qa label if the PR doesn't need to be tested during QA.
  • If you need to backport this PR to another branch, you can add the backport/<branch-name> label to the PR and it will automatically open a backport PR once this one is merged

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 98.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.39%. Comparing base (889474c) to head (457ad34).
⚠️ Report is 1 commits behind head on mwdd146980/httpx-migration-base.

Additional details and impacted files
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@datadog-prod-us1-5
Copy link
Copy Markdown

datadog-prod-us1-5 Bot commented Mar 17, 2026

⚠️ Tests

Fix all issues with BitsAI or with Cursor

⚠️ Warnings

🧪 3 Tests failed

test_e2e_profile_cisco_asa from test_e2e_core_vs_python.py   View in Datadog   (Fix with Cursor)
[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 01-check-apikey.sh: executing... 
[cont-init.d] 01-check-apikey.sh: exited 0.
[cont-init.d] 50-ci.sh: executing... 
[cont-init.d] 50-ci.sh: exited 0.
[cont-init.d] 50-ecs-managed.sh: executing... 
...
test_e2e_v3_md5_aes from test_e2e_core_vs_python.py   View in Datadog   (Fix with Cursor)
[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 01-check-apikey.sh: executing... 
[cont-init.d] 01-check-apikey.sh: exited 0.
[cont-init.d] 50-ci.sh: executing... 
[cont-init.d] 50-ci.sh: exited 0.
[cont-init.d] 50-ecs-managed.sh: executing... 
...
test_e2e_metrics from test_e2e.py   View in Datadog   (Fix with Cursor)
Metadata assertion errors using metadata.csv:
	- Expect \`prefect.server.task_runs.cancelled.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.completed.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.crashed.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.failed.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.late_start.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.paused\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.pending\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.running\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.throughput\` from metadata.csv but not submitted.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 457ad34 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

Copy link
Copy Markdown
Contributor Author

mwdd146980 commented Mar 17, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@mwdd146980 mwdd146980 changed the title Decouple test fixtures from requests: swap mock_response to MockHTTPResponse Swap mock_http_response to MockHTTPResponse Mar 17, 2026
@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3b-fixture-swap branch from cb98cdb to c4fad19 Compare March 17, 2026 18:34
@mwdd146980
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4fad19d17

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread datadog_checks_dev/datadog_checks/dev/plugin/pytest.py
Comment thread datadog_checks_base/tests/base/utils/http/test_http_testing.py Outdated
Copy link
Copy Markdown
Member

@NouemanKHAL NouemanKHAL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Issues

Bug fix in sonatype_nexus test (good, but worth calling out)

The old code had:

json_data=status_metrics_response_data.update({"gauges": ...}),

dict.update() returns None, so the mock was sending an empty response body. The PR fixes this correctly by building the dict before passing it. This is a genuine bug fix — worth mentioning in the PR description.

_CaseInsensitiveDict is incomplete

The dict only overrides __setitem__, __getitem__, __contains__, and get(). Missing methods that could silently behave incorrectly:

  • __delitem__del headers['Content-Type'] won't find the lowercase-stored key
  • pop() — same issue
  • update() — won't normalize keys from the incoming dict
  • setdefault() — won't normalize the key

For test mocking this is likely sufficient today, but if any production code path calls .pop() or del on response headers, tests would silently use the wrong key. Consider either:

  • Adding the missing methods, or
  • Using requests.structures.CaseInsensitiveDict or httpx.Headers directly (one will be a dependency anyway), or
  • At minimum, adding a comment noting the intentional subset

mock_http_response_per_endpoint still patches requests.Session.get

method: str = 'requests.Session.get',  # default target

If the goal is to decouple from requests, this should either be updated here or tracked for a follow-up.

Minor observations

  • The _CaseInsensitiveDict.__setitem__ and __getitem__ call .lower() unconditionally, which would raise AttributeError on non-string keys. __contains__ handles this with an isinstance check but the others don't. Inconsistent, though HTTP headers are always strings in practice.
  • _CaseInsensitiveDict stores keys lowercased (unlike requests.structures.CaseInsensitiveDict which preserves original case for iteration). Fine for testing but worth a docstring note.
  • PR description says "~102 test files" but only 13 are in the diff. The central fixture swap covers the rest implicitly — might be worth clarifying to avoid reviewer confusion.

@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3b-fixture-swap branch from 461f963 to a11d677 Compare March 19, 2026 18:33
@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3a-widen-om-exceptions branch 2 times, most recently from 01603f0 to 6e9c663 Compare March 19, 2026 19:17
@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3b-fixture-swap branch from a11d677 to 8e65a6f Compare March 19, 2026 19:24
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a934fdb4b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread datadog_checks_dev/datadog_checks/dev/plugin/pytest.py
Comment thread datadog_checks_dev/datadog_checks/dev/plugin/pytest.py
@mwdd146980
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread datadog_checks_base/datadog_checks/base/utils/http_testing.py
Copy link
Copy Markdown
Member

@NouemanKHAL NouemanKHAL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments, non-blockers, LGTM!

response = MockHTTPResponse(json_data={'key': 'value'}, headers=headers)

assert response.headers['content-type'] == 'application/json'
assert response.headers['Content-Type'] == 'application/json'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert response.headers['Content-Type'] == 'application/json'
assert response.headers['content-type'] == 'application/json'

I don't see a reason to touch this test

default_response: MockResponse | None = None,
method: str = 'requests.Session.get',
default_response: MockHTTPResponse | None = None,
method: str = 'requests.Session.get', # TODO(httpx-migration): update default when backend changes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case we can't get rid of this, could we store in a global constant so we would only need to change it in one place?

@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3a-widen-om-exceptions branch from fd558c5 to 08e03be Compare April 3, 2026 19:45
@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3b-fixture-swap branch from 217fc16 to 9f4a6c4 Compare April 3, 2026 19:45
Base automatically changed from mwdd146980/step3a-widen-om-exceptions to mwdd146980/httpx-migration-base April 4, 2026 16:43
@mwdd146980 mwdd146980 requested a review from a team as a code owner April 4, 2026 16:43
datadog-agent-integrations-bot Bot and others added 8 commits April 4, 2026 13:20
Co-authored-by: Kyle-Neale <37895372+Kyle-Neale@users.noreply.github.com>
Replace the requests-coupled MockResponse with the library-agnostic
MockHTTPResponse in the mock_response fixture, decoupling ~102 test
files from the requests library without touching individual test code.

- Swap mock_response fixture to yield MockHTTPResponse
- Fix mock_http_response_per_endpoint fallback to use fixture parameter
- Remove sonatype_nexus local fixture override (now redundant)
- Update match strings in 9 integrations from requests.exceptions.HTTPError
  to HTTPStatusError (our own shared exception class)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document that MockHTTPResponse has a different parameter order than
MockResponse (all callers use keyword args, so not a compatibility
concern).

Fix test_successful_metrics_collection which was passing vacuously:
dict.update() returns None so json_data was always None, the check's
json decode failed silently, no metrics were submitted, and
assert_all_metrics_covered() trivially passed. Provide complete mock
data for both API calls and add individual metric assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Step3b will make MockHTTPResponse the backing for all mock_http_response
fixture users (~102 test files). Production code accesses headers with
original-case keys (e.g. response.headers['Content-Type']), which would
KeyError or silently return wrong values with the plain lowered-key dict.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add missing dict methods (__delitem__, pop, update, setdefault) to
  _CaseInsensitiveDict so tests won't silently break if production code
  uses them on response headers
- Make isinstance guards consistent across all methods
- Replace redundant `assert in` checks with mixed-case .get() per
  reviewer suggestion; add tests for new dict methods
- Add TODO(httpx-migration) comment on per-endpoint fixture default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make raw.read delegate to BytesIO stream so json.load(r.raw) works
(fixes kubelet_base test regression). Add url attribute with default ''
(fixes sonatype_nexus test_timeout_error accessing response.url).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dead args

- Fix _CaseInsensitiveDict.update() to lowercase keys from iterable-of-pairs
- Add test_mock_response_url() for MockHTTPResponse url parameter
- Remove dead URL positional args from 7 sonatype_nexus test calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Revert unnecessary Content-Type → content-type case change in test
- Extract _DEFAULT_MOCK_METHOD constant for requests.Session.get default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mwdd146980 mwdd146980 force-pushed the mwdd146980/httpx-migration-base branch from 5e594eb to 889474c Compare April 4, 2026 17:20
@mwdd146980 mwdd146980 force-pushed the mwdd146980/step3b-fixture-swap branch from 9f4a6c4 to 457ad34 Compare April 4, 2026 17:20
@mwdd146980 mwdd146980 merged commit 8938e5e into mwdd146980/httpx-migration-base Apr 5, 2026
315 of 613 checks passed
@mwdd146980 mwdd146980 deleted the mwdd146980/step3b-fixture-swap branch April 5, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment