Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
82b8aaf
Redesign mock_http fixture to patch RequestsWrapper at class level
mwdd146980 Feb 24, 2026
f4e689f
Migrate falco tests from requests.Session.get patch to mock_http fixture
mwdd146980 Feb 24, 2026
e67204f
Migrate strimzi tests from requests.Session.get patch to mock_http fi…
mwdd146980 Feb 24, 2026
56475b3
Fix E402: move pytest_plugins after all imports
mwdd146980 Feb 24, 2026
fad8557
Migrate couchbase tests from requests.Session.get patch to mock_http …
mwdd146980 Feb 24, 2026
40b1e59
Rename mock_http fixture to mock_http_client
mwdd146980 Feb 24, 2026
8be2a72
Migrate ray tests from requests.Session.get patch to mock_http_client…
mwdd146980 Feb 24, 2026
9d6a7b7
Migrate tekton tests from requests.Session.get patch to mock_http_cli…
mwdd146980 Feb 24, 2026
5ab9547
Switch from pytest_plugins to direct import for fixture registration
mwdd146980 Feb 24, 2026
693faee
Revert rename: mock_http_client → mock_http
mwdd146980 Feb 25, 2026
0549839
Redesign mock_http: use create_autospec(HTTPClientProtocol) + Propert…
mwdd146980 Feb 25, 2026
3321b99
Shorten docstring
mwdd146980 Feb 25, 2026
8d39453
Migrate kubevirt_api, kubevirt_controller, kubevirt_handler tests to …
mwdd146980 Feb 25, 2026
c4faf9a
Fix Accept header mutation side-effect in OM V2 scraper
mwdd146980 Feb 26, 2026
5040402
Fix bentoml tests broken by scraper sharing check.http
mwdd146980 Feb 26, 2026
05152e7
Fix Vault auth headers lost when OM V2 scraper reuses check.http
mwdd146980 Feb 26, 2026
a12f3ee
Raise NotImplementedError for options_method in mock_http fixture
mwdd146980 Feb 26, 2026
8db2b4f
Move mock_http fixture to datadog_checks_dev pytest plugin
mwdd146980 Feb 26, 2026
93c6cd2
Remove call_count assertion from bentoml test
mwdd146980 Feb 27, 2026
ea5790d
Drop unused get_mock variable in bentoml test
mwdd146980 Feb 27, 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
54 changes: 27 additions & 27 deletions bentoml/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# (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
Expand All @@ -19,28 +19,23 @@


def test_bentoml_mock_metrics(dd_run_check, aggregator, mock_http_response):
mock_http_response(file_path=get_fixture_path('metrics.txt'))
get_mock = 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()
assert get_mock.call_count == 3 # 1 metrics scrape + 2 health endpoints (/livez, /readyz)
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 get_mock.call_count == 3 # 1 metrics scrape + 2 health endpoints (/livez, /readyz)

Asserting this would tie us to the implementation.

IMO: What our users see are metrics and health checks, those are the things we should ensure our code is doing, testing the code itself will only require us to update the tests for every change in the code, even if it doesn't change any output or observed behavior. This is similar to the black-box testing concept.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied suggestion.

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)


def test_bentoml_mock_invalid_endpoint(dd_run_check, aggregator, mock_http_response):
Expand All @@ -53,21 +48,26 @@ 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
_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)
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())
8 changes: 4 additions & 4 deletions couchbase/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ def test_extract_index_tags(instance, test_input, expected_tags):
assert eval(str(test_output)) == expected_tags


def test_unit(dd_run_check, check, instance, mocker, aggregator):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_unit(dd_run_check, check, instance, mock_http, aggregator):
mock_http.get.side_effect = mock_http_responses

dd_run_check(check(instance))

Expand All @@ -140,8 +140,8 @@ def test_unit(dd_run_check, check, instance, mocker, aggregator):
aggregator.assert_metrics_using_metadata(get_metadata_metrics())


def test_unit_query_metrics(dd_run_check, check, instance_query, mocker, aggregator):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_unit_query_metrics(dd_run_check, check, instance_query, mock_http, aggregator):
mock_http.get.side_effect = mock_http_responses

dd_run_check(check(instance_query))

Expand Down
4 changes: 2 additions & 2 deletions datadog_checks_base/datadog_checks/base/checks/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
import unicodedata as _module_unicodedata

from datadog_checks.base.utils.diagnose import Diagnosis
from datadog_checks.base.utils.http import RequestsWrapper
from datadog_checks.base.utils.http_protocol import HTTPClientProtocol
from datadog_checks.base.utils.metadata import MetadataManager

inspect: _module_inspect = lazy_loader.load('inspect')
Expand Down Expand Up @@ -376,7 +376,7 @@ def _get_metric_limit(self, instance=None):
return limit

@property
def http(self) -> RequestsWrapper:
def http(self) -> HTTPClientProtocol:
"""
Provides logic to yield consistent network behavior based on user configuration.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from datadog_checks.base.constants import ServiceCheck
from datadog_checks.base.errors import ConfigurationError
from datadog_checks.base.utils.functions import no_op, return_true
from datadog_checks.base.utils.http import RequestsWrapper


class OpenMetricsScraper:
Expand Down Expand Up @@ -215,18 +214,16 @@ def __init__(self, check, config):

self.raw_line_filter = re.compile('|'.join(raw_line_filters))

self.http = RequestsWrapper(config, self.check.init_config, self.check.HTTP_CONFIG_REMAPPER, self.check.log)
self.http = check.http

self._content_type = ''
self._use_latest_spec = is_affirmative(config.get('use_latest_spec', False))
if self._use_latest_spec:
accept_header = 'application/openmetrics-text;version=1.0.0,application/openmetrics-text;version=0.0.1'
self._accept_header = (
'application/openmetrics-text;version=1.0.0,application/openmetrics-text;version=0.0.1'
)
else:
accept_header = 'text/plain'

# Request the appropriate exposition format
if self.http.options['headers'].get('Accept') == '*/*':
self.http.options['headers']['Accept'] = accept_header
self._accept_header = 'text/plain'

self.use_process_start_time = is_affirmative(config.get('use_process_start_time'))

Expand Down Expand Up @@ -463,6 +460,9 @@ def send_request(self, **kwargs):
"""

kwargs['stream'] = True
extra_headers = kwargs.get('extra_headers', {})
extra_headers['Accept'] = self._accept_header
kwargs['extra_headers'] = extra_headers
return self.http.get(self.endpoint, **kwargs)

def set_dynamic_tags(self, *tags):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(
content: str | bytes = '',
status_code: int = 200,
headers: dict[str, str] | None = None,
json_data: dict[str, Any] | None = None,
json_data: Any = None,
file_path: str | None = None,
cookies: dict[str, str] | None = None,
elapsed_seconds: float = 0.1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,12 +417,12 @@ def test_strict_latest_spec(self, dd_run_check):
check = get_check({'use_latest_spec': True})
check.configure_scrapers()
scraper = check.scrapers['test']
assert scraper.http.options['headers']['Accept'] == (
assert scraper._accept_header == (
'application/openmetrics-text;version=1.0.0,application/openmetrics-text;version=0.0.1'
)

def test_dynamic_spec(self, dd_run_check):
check = get_check({'use_latest_spec': False})
check.configure_scrapers()
scraper = check.scrapers['test']
assert scraper.http.options['headers']['Accept'] == 'text/plain'
assert scraper._accept_header == 'text/plain'
13 changes: 13 additions & 0 deletions datadog_checks_dev/datadog_checks/dev/plugin/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,19 @@ def mock_http_response(mocker, mock_response):
)


@pytest.fixture
def mock_http(mocker):
from unittest.mock import PropertyMock, create_autospec

from datadog_checks.base.checks.base import AgentCheck
from datadog_checks.base.utils.http_protocol import HTTPClientProtocol

client = create_autospec(HTTPClientProtocol)
client.options_method.side_effect = NotImplementedError('HTTP OPTIONS not yet supported in mock_http')
mocker.patch.object(AgentCheck, 'http', new_callable=PropertyMock, return_value=client)
return client


@pytest.fixture
def mock_http_response_per_endpoint(mocker, mock_response):
@overload
Expand Down
14 changes: 5 additions & 9 deletions falco/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# (C) Datadog, Inc. 2025-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from unittest import mock

import pytest

from datadog_checks.base.constants import ServiceCheck
from datadog_checks.dev.http import MockResponse
from datadog_checks.base.utils.http_testing import MockHTTPResponse
from datadog_checks.dev.utils import get_metadata_metrics
from datadog_checks.falco import FalcoCheck

Expand All @@ -22,13 +20,11 @@ def test_empty_instance(dd_run_check):
dd_run_check(check)


def test_check_falco(dd_run_check, aggregator, instance):
mock_responses = [
MockResponse(file_path=get_fixture_path("falco_metrics.txt")),
def test_check_falco(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = [
MockHTTPResponse(file_path=get_fixture_path("falco_metrics.txt")),
]

with mock.patch('requests.Session.get', side_effect=mock_responses):
dd_run_check(FalcoCheck('falco', {}, [instance]))
dd_run_check(FalcoCheck('falco', {}, [instance]))

for metric in METRICS:
aggregator.assert_metric(metric)
Expand Down
4 changes: 2 additions & 2 deletions kubevirt_api/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import pytest
import yaml

from datadog_checks.base.utils.http_testing import MockHTTPResponse # noqa: F401
from datadog_checks.dev import get_here, run_command
from datadog_checks.dev.http import MockResponse
from datadog_checks.dev.kind import kind_run
from datadog_checks.dev.kube_port_forward import port_forward

Expand Down Expand Up @@ -105,4 +105,4 @@ def mock_http_responses(url, **_params):
raise Exception(f"url `{url}` not registered")

with open(os.path.join(HERE, "fixtures", fixtures_file)) as f:
return MockResponse(content=f.read())
return MockHTTPResponse(content=f.read())
28 changes: 14 additions & 14 deletions kubevirt_api/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
pytestmark = [pytest.mark.unit]


def test_check_collects_all_metrics(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_check_collects_all_metrics(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [instance])

Expand Down Expand Up @@ -94,8 +94,8 @@ def test_check_collects_all_metrics(dd_run_check, aggregator, instance, mocker):
aggregator.assert_metrics_using_metadata(get_metadata_metrics())


def test_check_sends_zero_count_for_vms(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_check_sends_zero_count_for_vms(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [instance])

Expand All @@ -115,8 +115,8 @@ def test_check_sends_zero_count_for_vms(dd_run_check, aggregator, instance, mock
aggregator.assert_metric("kubevirt_api.vm.count", value=0)


def test_check_sends_zero_count_for_vmis(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_check_sends_zero_count_for_vmis(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [instance])

Expand Down Expand Up @@ -153,8 +153,8 @@ def test_emits_zero_can_connect_when_service_is_down(dd_run_check, aggregator, i
)


def test_emits_one_can_connect_when_service_is_up(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_emits_one_can_connect_when_service_is_up(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [instance])
check._setup_kube_client = lambda: None
Expand All @@ -170,8 +170,8 @@ def test_emits_one_can_connect_when_service_is_up(dd_run_check, aggregator, inst
)


def test_raise_exception_when_metrics_endpoint_is_bad(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_raise_exception_when_metrics_endpoint_is_bad(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [BAD_METRICS_HOSTNAME_INSTANCE])
check._setup_kube_client = lambda: None
Expand All @@ -189,8 +189,8 @@ def test_raise_exception_when_metrics_endpoint_is_bad(dd_run_check, aggregator,
)


def test_raise_exception_cannot_connect_to_kubernetes_api(dd_run_check, aggregator, instance, mocker, caplog):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_raise_exception_cannot_connect_to_kubernetes_api(dd_run_check, aggregator, instance, mock_http, caplog):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtApiCheck("kubevirt_api", {}, [instance])
with pytest.raises(
Expand All @@ -201,8 +201,8 @@ def test_raise_exception_cannot_connect_to_kubernetes_api(dd_run_check, aggregat
assert "Cannot connect to Kubernetes API:" in caplog.text


def test_log_warning_healthz_endpoint_not_provided(dd_run_check, aggregator, instance, mocker, caplog):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_log_warning_healthz_endpoint_not_provided(dd_run_check, aggregator, instance, mock_http, caplog):
mock_http.get.side_effect = mock_http_responses

new_instance = deepcopy(instance)
new_instance.pop("kubevirt_api_healthz_endpoint")
Expand Down
4 changes: 2 additions & 2 deletions kubevirt_controller/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import pytest
import yaml

from datadog_checks.base.utils.http_testing import MockHTTPResponse # noqa: F401
from datadog_checks.dev import get_here, run_command
from datadog_checks.dev.http import MockResponse
from datadog_checks.dev.kind import kind_run
from datadog_checks.dev.kube_port_forward import port_forward

Expand Down Expand Up @@ -101,4 +101,4 @@ def mock_http_responses(url, **_params):
raise Exception(f"url `{url}` not registered")

with open(os.path.join(HERE, "fixtures", fixtures_file)) as f:
return MockResponse(content=f.read())
return MockHTTPResponse(content=f.read())
8 changes: 4 additions & 4 deletions kubevirt_controller/tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
]


def test_emits_can_connect_one_when_service_is_up(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_emits_can_connect_one_when_service_is_up(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses
check = KubeVirtControllerCheck("kubevirt_controller", {}, [instance])
dd_run_check(check)
aggregator.assert_metric(
Expand All @@ -35,8 +35,8 @@ def test_emits_can_connect_zero_when_service_is_down(dd_run_check, aggregator, i
)


def test_check_collects_all_metrics(dd_run_check, aggregator, instance, mocker):
mocker.patch("requests.Session.get", wraps=mock_http_responses)
def test_check_collects_all_metrics(dd_run_check, aggregator, instance, mock_http):
mock_http.get.side_effect = mock_http_responses

check = KubeVirtControllerCheck("kubevirt_controller", {}, [instance])

Expand Down
Loading
Loading