Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e5d6870
Add HTTP protocol definitions for httpx migration (#22662)
mwdd146980 Feb 18, 2026
721b626
Add library-agnostic HTTP exception hierarchy
mwdd146980 Feb 19, 2026
06d798c
Update PR changelog
mwdd146980 Feb 20, 2026
cc4b634
Rename changelog
mwdd146980 Feb 20, 2026
47747c0
Fix iter_content/iter_lines return type and document the decode_unico…
mwdd146980 Feb 20, 2026
21ad097
Address Noueman's comments
mwdd146980 Feb 20, 2026
2d36811
Remove docstring on http_exceptions.py
mwdd146980 Feb 20, 2026
a53a7ab
Add mock HTTP response (#22677)
mwdd146980 Feb 20, 2026
31a6555
Update changelog: consolidate into 22676.added, drop 22677.added
mwdd146980 Feb 20, 2026
7e54dab
Remove module docstrings from http_testing.py and test_http_testing.py
mwdd146980 Feb 20, 2026
8af6252
Re-export HTTP exceptions and protocol types from http.py
mwdd146980 Feb 20, 2026
7108b1e
Use unittest.mock.MagicMock for raw socket mock in MockHTTPResponse
mwdd146980 Feb 23, 2026
13bb344
Replace _CaseInsensitiveDict with plain lowercased dict in MockHTTPRe…
mwdd146980 Feb 23, 2026
26e48e4
Add mock_http fixture; migrate 9 intg to mock_http (#22710)
mwdd146980 Feb 28, 2026
afd0322
Address Noueman's review comments on PR #22676
mwdd146980 Mar 2, 2026
07b98aa
Fix ruff lint: remove unnecessary parens around single-line string
mwdd146980 Mar 2, 2026
5131417
Add changelog entries for datadog_checks_base and datadog_checks_dev
mwdd146980 Mar 2, 2026
28fec1f
Fix TestUseLatestSpec: assert on _use_latest_spec instead of removed …
mwdd146980 Mar 2, 2026
60ba647
Add changelog entry for vault intg
mwdd146980 Mar 2, 2026
8a7830c
Revise message for vault intg changelog entry
mwdd146980 Mar 2, 2026
67a1872
Clean up MockHTTPResponse: top-level imports, flatten test classes, t…
mwdd146980 Mar 13, 2026
a05a40a
Migrate config assertion tests away from requests
mwdd146980 Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 50 additions & 59 deletions airflow/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,16 @@ def test_service_checks_cannot_connect(aggregator):
'json_resp, expected_healthy_status, expected_healthy_value',
[({'status': 'OK'}, AgentCheck.OK, 1), ({'status': 'KO'}, AgentCheck.CRITICAL, 0), ({}, AgentCheck.CRITICAL, 0)],
)
def test_service_checks_healthy_exp(aggregator, json_resp, expected_healthy_status, expected_healthy_value):
def test_service_checks_healthy_exp(aggregator, mock_http, json_resp, expected_healthy_status, expected_healthy_value):
instance = common.FULL_CONFIG['instances'][0]
check = AirflowCheck('airflow', common.FULL_CONFIG, [instance])

with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value=None):
mock_session = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=mock_session):
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [json_resp]
mock_session.get.return_value = mock_resp
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [json_resp]
mock_http.get.return_value = mock_resp

check.check(None)
with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value=None):
check.check(None)

tags = ['key:my-tag', 'url:http://localhost:8080']

Expand All @@ -54,65 +52,60 @@ def test_service_checks_healthy_exp(aggregator, json_resp, expected_healthy_stat
],
)
def test_service_checks_healthy_stable(
aggregator, metadb_status, scheduler_status, expected_healthy_status, expected_healthy_value
aggregator, mock_http, metadb_status, scheduler_status, expected_healthy_status, expected_healthy_value
): # Stable is only defined in the context of Airflow 2
instance = common.FULL_CONFIG['instances'][0]
check = AirflowCheck('airflow', common.FULL_CONFIG, [instance])

with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
mock_session = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=mock_session):
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': metadb_status}, 'scheduler': {'status': scheduler_status}},
{'status': 'OK'},
]
mock_session.get.return_value = mock_resp
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': metadb_status}, 'scheduler': {'status': scheduler_status}},
{'status': 'OK'},
]
mock_http.get.return_value = mock_resp

check.check(None)
with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
check.check(None)

tags = ['key:my-tag', 'url:http://localhost:8080']

aggregator.assert_service_check('airflow.healthy', expected_healthy_status, tags=tags, count=1)
aggregator.assert_metric('airflow.healthy', expected_healthy_value, tags=tags, count=1)


def test_dag_total_tasks(aggregator, task_instance):
def test_dag_total_tasks(aggregator, mock_http, task_instance):
instance = common.FULL_CONFIG['instances'][0]
check = AirflowCheck('airflow', common.FULL_CONFIG, [instance])

with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
req = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=req):
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
task_instance,
]
req.get.return_value = mock_resp
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
task_instance,
]
mock_http.get.return_value = mock_resp

check.check(None)
with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
check.check(None)

aggregator.assert_metric('airflow.dag.task.total_running', value=1, count=1)


def test_dag_task_ongoing_duration(aggregator, task_instance):
def test_dag_task_ongoing_duration(aggregator, mock_http, task_instance):
instance = common.FULL_CONFIG['instances'][0]
check = AirflowCheck('airflow', common.FULL_CONFIG, [instance])

mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
]
mock_http.get.return_value = mock_resp

with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
req = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=req):
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
]
req.get.return_value = mock_resp
with mock.patch(
'datadog_checks.airflow.airflow.AirflowCheck._get_all_task_instances',
return_value=task_instance.get('task_instances'),
):
check.check(None)
with mock.patch(
'datadog_checks.airflow.airflow.AirflowCheck._get_all_task_instances',
return_value=task_instance.get('task_instances'),
):
check.check(None)

aggregator.assert_metric(
'airflow.dag.task.ongoing_duration',
Expand Down Expand Up @@ -141,23 +134,21 @@ def test_dag_task_ongoing_duration(aggregator, task_instance):
),
],
)
def test_config_collect_ongoing_duration(collect_ongoing_duration, should_call_method):
def test_config_collect_ongoing_duration(mock_http, collect_ongoing_duration, should_call_method):
instance = {**common.FULL_CONFIG['instances'][0], 'collect_ongoing_duration': collect_ongoing_duration}
check = AirflowCheck('airflow', common.FULL_CONFIG, [instance])

mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
]
mock_http.get.return_value = mock_resp

with mock.patch('datadog_checks.airflow.airflow.AirflowCheck._get_version', return_value='2.6.2'):
req = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=req):
mock_resp = mock.MagicMock(status_code=200)
mock_resp.json.side_effect = [
{'metadatabase': {'status': 'healthy'}, 'scheduler': {'status': 'healthy'}},
]
req.get.return_value = mock_resp

with mock.patch(
'datadog_checks.airflow.airflow.AirflowCheck._get_all_task_instances'
) as mock_get_all_task_instances:
check.check(None)

# Assert method calls
mock_get_all_task_instances.assert_has_calls(should_call_method, any_order=False)
with mock.patch(
'datadog_checks.airflow.airflow.AirflowCheck._get_all_task_instances'
) as mock_get_all_task_instances:
check.check(None)

# Assert method calls
mock_get_all_task_instances.assert_has_calls(should_call_method, any_order=False)
55 changes: 26 additions & 29 deletions bentoml/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# (C) Datadog, Inc. 2025-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from unittest.mock import Mock, patch
from unittest.mock import Mock

import pytest
import requests

from datadog_checks.base.constants import ServiceCheck
from datadog_checks.base.utils.http_exceptions import HTTPStatusError
from datadog_checks.bentoml import BentomlCheck
from datadog_checks.dev.utils import get_metadata_metrics

Expand All @@ -21,26 +21,19 @@
def test_bentoml_mock_metrics(dd_run_check, aggregator, mock_http_response):
mock_http_response(file_path=get_fixture_path('metrics.txt'))

with patch('datadog_checks.bentoml.check.BentomlCheck.http') as mock_http:
mock_response = type('MockResponse', (), {'status_code': 200})()
mock_http.get.return_value = mock_response
mock_http.get.return_value.raise_for_status = lambda: None

check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)
check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)

for metric in METRICS:
aggregator.assert_metric(metric)
for metric in METRICS:
aggregator.assert_metric(metric)

for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=1, tags=['test:tag', 'status_code:200'])
for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=1, tags=['test:tag', 'status_code:200'])

aggregator.assert_all_metrics_covered()
assert mock_http.get.call_count == 2
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
aggregator.assert_all_metrics_covered()
aggregator.assert_metric_has_tag('bentoml.service.request.count', 'bentoml_endpoint:/summarize')
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
aggregator.assert_all_metrics_covered()
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
aggregator.assert_metric_has_tag('bentoml.service.request.count', 'bentoml_endpoint:/summarize')
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)


def test_bentoml_mock_invalid_endpoint(dd_run_check, aggregator, mock_http_response):
Expand All @@ -53,21 +46,25 @@ def test_bentoml_mock_invalid_endpoint(dd_run_check, aggregator, mock_http_respo


def test_bentoml_mock_valid_endpoint_invalid_health(dd_run_check, aggregator, mock_http_response):
mock_http_response(file_path=get_fixture_path('metrics.txt'))
session_get_mock = mock_http_response(file_path=get_fixture_path('metrics.txt'))
metrics_response = session_get_mock.return_value

_err = Mock()
_err.status_code = 500
_http_err = requests.HTTPError("500 Internal Server Error")
_http_err.response = _err
_http_err = HTTPStatusError("500 Internal Server Error", response=_err)
_err.raise_for_status.side_effect = _http_err

with patch('datadog_checks.bentoml.check.BentomlCheck.http') as mock_http:
mock_http.get.return_value = _err
def dispatch(url, **_):
if '/livez' in url or '/readyz' in url:
return _err
return metrics_response

check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)
session_get_mock.side_effect = dispatch

check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)

for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=0, tags=['test:tag', 'status_code:500'])
for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=0, tags=['test:tag', 'status_code:500'])

aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
60 changes: 23 additions & 37 deletions consul/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# Licensed under a 3-clause BSD style license (see LICENSE)
import logging

import mock
import pytest

from datadog_checks.consul import ConsulCheck
Expand Down Expand Up @@ -155,25 +154,25 @@ def test_get_nodes_with_service_critical(aggregator):
aggregator.assert_metric('consul.catalog.services_count', value=1, tags=expected_tags)


def test_consul_request(aggregator, instance, mocker):
def test_consul_request(aggregator, instance, mocker, mock_http):
consul_check = ConsulCheck(common.CHECK_NAME, {}, [consul_mocks.MOCK_CONFIG])
mocker.patch("datadog_checks.base.utils.serialization.json.loads")
with mock.patch("datadog_checks.consul.consul.requests.Session.get") as mock_requests_get:

consul_check.consul_request("foo")
url = "{}/{}".format(instance["url"], "foo")
aggregator.assert_service_check("consul.can_connect", ConsulCheck.OK, tags=["url:{}".format(url)], count=1)

aggregator.reset()
mock_http.get.side_effect = Exception("message")
with pytest.raises(Exception):
consul_check.consul_request("foo")
url = "{}/{}".format(instance["url"], "foo")
aggregator.assert_service_check("consul.can_connect", ConsulCheck.OK, tags=["url:{}".format(url)], count=1)

aggregator.reset()
mock_requests_get.side_effect = Exception("message")
with pytest.raises(Exception):
consul_check.consul_request("foo")
aggregator.assert_service_check(
"consul.can_connect",
ConsulCheck.CRITICAL,
tags=["url:{}".format(url)],
count=1,
message="Consul request to {} failed: message".format(url),
)
aggregator.assert_service_check(
"consul.can_connect",
ConsulCheck.CRITICAL,
tags=["url:{}".format(url)],
count=1,
message="Consul request to {} failed: message".format(url),
)


def test_service_checks(aggregator):
Expand Down Expand Up @@ -648,26 +647,13 @@ def test_network_latency_node_name(
),
],
)
def test_config(test_case, extra_config, expected_http_kwargs, mocker):
def test_config(test_case, extra_config, expected_http_kwargs):
instance = extra_config
check = ConsulCheck(common.CHECK_NAME, {}, instances=[instance])
mocker.patch("datadog_checks.base.utils.serialization.json.loads")

with mock.patch('datadog_checks.base.utils.http.requests.Session') as session:
mock_session = mock.MagicMock()
session.return_value = mock_session
mock_session.get.return_value = mock.MagicMock(status_code=200)

check.check(None)

http_wargs = {
'auth': mock.ANY,
'cert': mock.ANY,
'headers': mock.ANY,
'proxies': mock.ANY,
'timeout': mock.ANY,
'verify': mock.ANY,
'allow_redirects': mock.ANY,
}
http_wargs.update(expected_http_kwargs)
mock_session.get.assert_called_with('/v1/status/leader', **http_wargs)
for key, value in expected_http_kwargs.items():
if key == 'headers':
for h_key, h_value in value.items():
assert check.http.get_header(h_key) == h_value
else:
assert check.http.options[key] == value
21 changes: 2 additions & 19 deletions couch/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from copy import deepcopy
from unittest.mock import MagicMock

import mock
import pytest

from datadog_checks.couch import CouchDb
Expand All @@ -30,24 +29,8 @@ def test_config(test_case, extra_config, expected_http_kwargs):
instance.update(extra_config)
check = CouchDb(common.CHECK_NAME, {}, instances=[instance])

r = mock.MagicMock()
with mock.patch('datadog_checks.base.utils.http.requests.Session', return_value=r):
r.get.return_value = mock.MagicMock(status_code=200, content='{}')

check.check(instance)

http_wargs = {
'auth': mock.ANY,
'cert': mock.ANY,
'headers': mock.ANY,
'proxies': mock.ANY,
'timeout': mock.ANY,
'verify': mock.ANY,
'allow_redirects': mock.ANY,
}
http_wargs.update(expected_http_kwargs)

r.get.assert_called_with('http://{}:5984/_all_dbs/'.format(common.HOST), **http_wargs)
for key, value in expected_http_kwargs.items():
assert check.http.options[key] == value


def test_new_version_system_metrics(load_test_data):
Expand Down
4 changes: 2 additions & 2 deletions couchbase/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import pytest
import requests

from datadog_checks.base.utils.http_testing import MockHTTPResponse # noqa: F401
from datadog_checks.couchbase import Couchbase
from datadog_checks.dev import WaitFor, docker_run
from datadog_checks.dev.docker import get_container_ip
from datadog_checks.dev.http import MockResponse

from .common import (
BUCKET_NAME,
Expand Down Expand Up @@ -301,4 +301,4 @@ def mock_http_responses(url, **_params):
pytest.fail("url `{url}` not registered".format(url=url))

with open(os.path.join(HERE, 'fixtures', metrics_file)) as f:
return MockResponse(content=f.read())
return MockHTTPResponse(content=f.read())
Loading
Loading