diff --git a/.changelog/4583.changed b/.changelog/4583.changed new file mode 100644 index 0000000000..f83d1a3344 --- /dev/null +++ b/.changelog/4583.changed @@ -0,0 +1 @@ +`opentelemetry-instrumentation-{urllib,urllib3,requests}`: switch http mock library from abandoned httpretty to mocket diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65e081fbe9..93053ab149 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5534,6 +5534,44 @@ jobs: - name: Run tests run: tox -e py313-test-instrumentation-urllib3-1 -- -ra + py314-test-instrumentation-urllib3-0_ubuntu-latest: + name: instrumentation-urllib3-0 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-urllib3-0 -- -ra + + py314-test-instrumentation-urllib3-1_ubuntu-latest: + name: instrumentation-urllib3-1 3.14 Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repo @ SHA - ${{ github.sha }} + uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install tox + run: pip install tox-uv + + - name: Run tests + run: tox -e py314-test-instrumentation-urllib3-1 -- -ra + pypy3-test-instrumentation-urllib3-0_ubuntu-latest: name: instrumentation-urllib3-0 pypy-3.10 Ubuntu runs-on: ubuntu-latest diff --git a/dev-requirements.txt b/dev-requirements.txt index a18cd6a96c..abcf1efe03 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,4 @@ pylint==4.0.5 -httpretty==1.1.4 pyright==v1.1.404 sphinx==7.1.2 sphinx-rtd-theme==2.0.0rc4 diff --git a/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt index f4ce84cd32..8a38e71b41 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-requests/test-requirements.txt @@ -2,7 +2,7 @@ asgiref==3.8.1 certifi==2024.7.4 charset-normalizer==3.3.2 Deprecated==1.2.14 -httpretty==1.1.4 +mocket==3.14.1 idna==3.7 iniconfig==2.0.0 packaging==24.0 diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index bda9d62cf2..4097cc364a 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -6,8 +6,9 @@ import abc from unittest import mock -import httpretty import requests +from mocket import Mocket, Mocketizer +from mocket.mocks.mockhttp import Entry from requests.adapters import BaseAdapter from requests.models import Response @@ -131,8 +132,9 @@ def setUp(self): self.exclude_patch.start() RequestsInstrumentor().instrument() - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.URL, body="Hello!") # pylint: disable=invalid-name def tearDown(self): @@ -140,7 +142,7 @@ def tearDown(self): self.env_patch.stop() _OpenTelemetrySemanticConventionStability._initialized = False RequestsInstrumentor().uninstrument() - httpretty.disable() + self.mocketizer.exit() def assert_span(self, exporter=None, num_spans=1): if exporter is None: @@ -189,8 +191,8 @@ def test_basic(self): def test_basic_new_semconv(self): url_with_port = "http://mock:80/status/200" - httpretty.register_uri( - httpretty.GET, url_with_port, status=200, body="Hello!" + Entry.single_register( + Entry.GET, url_with_port, status=200, body="Hello!" ) result = self.perform_request(url_with_port) self.assertEqual(result.text, "Hello!") @@ -226,8 +228,8 @@ def test_basic_new_semconv(self): def test_basic_both_semconv(self): url_with_port = "http://mock:80/status/200" - httpretty.register_uri( - httpretty.GET, url_with_port, status=200, body="Hello!" + Entry.single_register( + Entry.GET, url_with_port, status=200, body="Hello!" ) result = self.perform_request(url_with_port) self.assertEqual(result.text, "Hello!") @@ -267,9 +269,12 @@ def test_basic_both_semconv(self): span, opentelemetry.instrumentation.requests ) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_nonstandard_http_method(self): - httpretty.register_uri("NONSTANDARD", self.URL, status=405) + Entry.single_register("NONSTANDARD", self.URL, status=405) session = requests.Session() session.request("NONSTANDARD", self.URL) span = self.assert_span() @@ -287,9 +292,12 @@ def test_nonstandard_http_method(self): self.assertIs(span.status.status_code, trace.StatusCode.ERROR) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_nonstandard_http_method_new_semconv(self): - httpretty.register_uri("NONSTANDARD", self.URL, status=405) + Entry.single_register("NONSTANDARD", self.URL, status=405) session = requests.Session() session.request("NONSTANDARD", self.URL) span = self.assert_span() @@ -331,8 +339,8 @@ def response_hook(span, request_obj, response): def test_excluded_urls_explicit(self): url_404 = "http://mock/status/404" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -346,8 +354,8 @@ def test_excluded_urls_explicit(self): def test_excluded_urls_from_env(self): url = "http://localhost/env_excluded_arg/123" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url, status=200, ) @@ -373,8 +381,8 @@ def name_callback(method, url): def test_not_foundbasic(self): url_404 = "http://mock/status/404" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -392,8 +400,8 @@ def test_not_foundbasic(self): def test_not_foundbasic_new_semconv(self): url_404 = "http://mock/status/404" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -412,8 +420,8 @@ def test_not_foundbasic_new_semconv(self): def test_not_foundbasic_both_semconv(self): url_404 = "http://mock/status/404" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -498,7 +506,7 @@ def test_distributed_context(self): span = self.assert_span() - headers = dict(httpretty.last_request().headers) + headers = dict(Mocket.last_request().headers) self.assertIn(MockTextMapPropagator.TRACE_ID_KEY, headers) self.assertEqual( str(span.get_span_context().trace_id), @@ -583,8 +591,8 @@ def test_requests_exception_without_response(self, *_, **__): ) def test_requests_exception_new_semconv(self, *_, **__): url_with_port = "http://mock:80/status/200" - httpretty.register_uri( - httpretty.GET, url_with_port, status=200, body="Hello!" + Entry.single_register( + Entry.GET, url_with_port, status=200, body="Hello!" ) with self.assertRaises(requests.RequestException): self.perform_request(url_with_port) @@ -703,6 +711,7 @@ def test_remove_sensitive_params(self): new_url = ( "http://username:password@mock/status/200?AWSAccessKeyId=secret" ) + Entry.single_register(Entry.GET, new_url, body="Hello!") self.perform_request(new_url) span = self.assert_span() @@ -732,7 +741,7 @@ def test_custom_request_headers_captured(self): "X-Another-Header": "another-value", "X-Excluded-Header": "excluded-value", } - httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") + Entry.single_register(Entry.GET, self.URL, body="Hello!") result = requests.get(self.URL, headers=headers, timeout=5) self.assertEqual(result.text, "Hello!") @@ -763,8 +772,9 @@ def test_custom_response_headers_captured(self): "X-Another-Header": "another-value", "X-Excluded-Header": "excluded-value", } - httpretty.register_uri( - httpretty.GET, self.URL, body="Hello!", adding_headers=headers + Mocket.reset() + Entry.single_register( + Entry.GET, self.URL, body="Hello!", headers=headers ) result = requests.get(self.URL, timeout=5) self.assertEqual(result.text, "Hello!") @@ -786,11 +796,12 @@ def test_custom_headers_not_captured_when_not_configured(self): RequestsInstrumentor().uninstrument() RequestsInstrumentor().instrument() headers = {"X-Request-Header": "request-value"} - httpretty.register_uri( - httpretty.GET, + Mocket.reset() + Entry.single_register( + Entry.GET, self.URL, body="Hello!", - adding_headers={"X-Response-Header": "response-value"}, + headers={"X-Response-Header": "response-value"}, ) result = requests.get(self.URL, headers=headers, timeout=5) self.assertEqual(result.text, "Hello!") @@ -824,11 +835,12 @@ def test_sensitive_headers_sanitized(self): "Set-Cookie": "session=abc123", "X-Secret": "secret", } - httpretty.register_uri( - httpretty.GET, + Mocket.reset() + Entry.single_register( + Entry.GET, self.URL, body="Hello!", - adding_headers=response_headers, + headers=response_headers, ) result = requests.get(self.URL, headers=request_headers, timeout=5) self.assertEqual(result.text, "Hello!") @@ -872,11 +884,12 @@ def test_custom_headers_with_regex(self): "X-Custom-Response-B": "value-B", "X-Other-Response-Header": "other-value", } - httpretty.register_uri( - httpretty.GET, + Mocket.reset() + Entry.single_register( + Entry.GET, self.URL, body="Hello!", - adding_headers=response_headers, + headers=response_headers, ) result = requests.get(self.URL, headers=request_headers, timeout=5) self.assertEqual(result.text, "Hello!") @@ -918,11 +931,12 @@ def test_custom_headers_case_insensitive(self): RequestsInstrumentor().instrument() request_headers = {"X-ReQuESt-HeaDER": "custom-value"} response_headers = {"X-ReSPoNse-HeaDER": "custom-value"} - httpretty.register_uri( - httpretty.GET, + Mocket.reset() + Entry.single_register( + Entry.GET, self.URL, body="Hello!", - adding_headers=response_headers, + headers=response_headers, ) result = requests.get(self.URL, headers=request_headers, timeout=5) self.assertEqual(result.text, "Hello!") @@ -973,15 +987,16 @@ def setUp(self): _OpenTelemetrySemanticConventionStability._initialized = False RequestsInstrumentor().instrument(meter_provider=self.meter_provider) - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.URL, body="Hello!") def tearDown(self): super().tearDown() self.env_patch.stop() _OpenTelemetrySemanticConventionStability._initialized = False RequestsInstrumentor().uninstrument() - httpretty.disable() + self.mocketizer.exit() @staticmethod def perform_request(url: str) -> requests.Response: diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py index 5d602c0fd7..c1de7621ae 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_user_agent_synthetic.py @@ -3,8 +3,9 @@ from unittest import mock -import httpretty import requests +from mocket import Mocketizer +from mocket.mocks.mockhttp import Entry from opentelemetry.instrumentation.requests import RequestsInstrumentor from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( @@ -21,13 +22,14 @@ class TestUserAgentSynthetic(TestBase): def setUp(self): super().setUp() RequestsInstrumentor().instrument() - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body="Hello!") + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.URL, body="Hello!") def tearDown(self): super().tearDown() RequestsInstrumentor().uninstrument() - httpretty.disable() + self.mocketizer.exit() def assert_span(self, num_spans=1): span_list = self.memory_exporter.get_finished_spans() diff --git a/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt index 44284f4e63..5a7c375ce5 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib/test-requirements.txt @@ -1,6 +1,6 @@ asgiref==3.8.1 Deprecated==1.2.14 -httpretty==1.1.4 +mocket==3.14.1 iniconfig==2.0.0 packaging==24.0 pluggy==1.6.0 diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py index 51c6f83b85..d5b70254e0 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py @@ -8,7 +8,8 @@ from urllib import request from urllib.parse import urlencode -import httpretty +from mocket import Mocketizer +from mocket.mocks.mockhttp import Entry from pytest import mark from opentelemetry.instrumentation._semconv import ( @@ -56,16 +57,15 @@ def setUp(self): _OpenTelemetrySemanticConventionStability._initialized = False self.env_patch.start() URLLibInstrumentor().instrument() - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body=b"Hello!") - httpretty.register_uri( - httpretty.POST, self.URL_POST, body=b"Hello World!" - ) + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.URL, body=b"Hello!") + Entry.single_register(Entry.POST, self.URL_POST, body=b"Hello World!") def tearDown(self): super().tearDown() URLLibInstrumentor().uninstrument() - httpretty.disable() + self.mocketizer.exit() # Return Sequence with one histogram def create_histogram_data_points( diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py index d14428b19e..3dd5eeaea9 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_urllib_integration.py @@ -12,7 +12,8 @@ from urllib.error import HTTPError from urllib.request import OpenerDirector -import httpretty +from mocket import Mocket, Mocketizer +from mocket.mocks.mockhttp import Entry import opentelemetry.instrumentation.urllib # pylint: disable=no-name-in-module,import-error from opentelemetry import trace @@ -85,20 +86,21 @@ def setUp(self): self.exclude_patch.start() URLLibInstrumentor().instrument() - httpretty.enable() - httpretty.register_uri(httpretty.GET, self.URL, body=b"Hello!") - httpretty.register_uri( - httpretty.GET, + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.URL, body=b"Hello!") + Entry.single_register( + Entry.GET, self.URL_TIMEOUT, - body=self.timeout_exception_callback, + exception=socket.timeout(), ) - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, self.URL_EXCEPTION, - body=self.base_exception_callback, + exception=Exception("test"), # pylint: disable=broad-exception-raised ) - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, "http://mock/status/500", status=500, ) @@ -107,15 +109,7 @@ def setUp(self): def tearDown(self): super().tearDown() URLLibInstrumentor().uninstrument() - httpretty.disable() - - @staticmethod - def timeout_exception_callback(*_, **__): - raise socket.timeout - - @staticmethod - def base_exception_callback(*_, **__): - raise Exception("test") # pylint: disable=broad-exception-raised + self.mocketizer.exit() def assert_span(self, exporter=None, num_spans=1): if exporter is None: @@ -214,8 +208,8 @@ def test_basic_both_semconv(self): def test_excluded_urls_explicit(self): url_201 = "http://mock/status/201" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_201, status=201, ) @@ -229,8 +223,8 @@ def test_excluded_urls_explicit(self): def test_excluded_urls_from_env(self): url = "http://localhost/env_excluded_arg/123" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url, status=200, ) @@ -244,8 +238,8 @@ def test_excluded_urls_from_env(self): def test_not_foundbasic(self): url_404 = "http://mock/status/404/" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -268,8 +262,8 @@ def test_not_foundbasic(self): def test_not_foundbasic_new_semconv(self): url_404 = "http://mock/status/404/" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -292,8 +286,8 @@ def test_not_foundbasic_new_semconv(self): def test_not_foundbasic_both_semconv(self): url_404 = "http://mock/status/404/" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_404, status=404, ) @@ -410,7 +404,7 @@ def test_distributed_context(self): span = self.assert_span() - headers_ = dict(httpretty.last_request().headers) + headers_ = dict(Mocket.last_request().headers) headers = {} for k, v in headers_.items(): headers[k.lower()] = v @@ -510,6 +504,8 @@ def test_requests_timeout_exception(self, *_, **__): def test_remove_sensitive_params(self): url = "http://username:password@mock/status/200" + Mocket.reset() + Entry.single_register(Entry.GET, url, exception=Exception("test")) with self.assertRaises(Exception): self.perform_request(url) @@ -558,9 +554,12 @@ def test_custom_response_headers_captured(self): "X-Custom-Header": "custom-value", "X-Another-Header": "another-value", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request(url) @@ -613,9 +612,12 @@ def test_sensitive_headers_sanitized(self): "Set-Cookie": "session=abc123", "X-Secret": "secret", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -656,9 +658,12 @@ def test_custom_headers_with_regex(self): "X-Custom-Response-B": "value-B", "X-Other-Response-Header": "other-value", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -702,9 +707,12 @@ def test_custom_headers_case_insensitive(self): ) response_headers = {"X-ReSPoNse-HeaDER": "custom-value"} - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -733,9 +741,12 @@ def test_standard_http_headers_captured(self): "Content-Type": "text/plain", "Server": "TestServer/1.0", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -801,9 +812,12 @@ def test_capture_all_response_headers(self): "X-Response-Two": "value2", "X-Response-Three": "value3", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request(url) @@ -863,9 +877,12 @@ def test_capture_and_sanitize_environment_variables(self): "X-Response-One": "value1", "X-Response-Two": "value2", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, headers=[("x-request-one", "one"), ("x-request-two", "two")] diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt index f2f654f42a..f8a65db692 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-0.txt @@ -1,6 +1,6 @@ asgiref==3.8.1 Deprecated==1.2.14 -httpretty==1.1.4 +mocket==3.14.1 iniconfig==2.0.0 packaging==24.0 pluggy==1.6.0 diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt index 693b6d6db5..b19e1e959e 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt +++ b/instrumentation/opentelemetry-instrumentation-urllib3/test-requirements-1.txt @@ -1,6 +1,6 @@ asgiref==3.8.1 Deprecated==1.2.14 -httpretty==1.1.4 +mocket==3.14.1 iniconfig==2.0.0 packaging==24.0 pluggy==1.6.0 diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py index a075b35bf4..2e38a229d0 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py @@ -5,11 +5,10 @@ import typing from unittest import mock -import httpretty -import httpretty.core -import httpretty.http import urllib3 import urllib3.exceptions +from mocket import Mocket, Mocketizer +from mocket.mocks.mockhttp import Entry from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( @@ -68,18 +67,20 @@ def setUp(self): URLLib3Instrumentor().instrument() - httpretty.enable(allow_net_connect=False) - httpretty.register_uri(httpretty.GET, self.HTTP_URL, body="Hello!") - httpretty.register_uri(httpretty.GET, self.HTTPS_URL, body="Hello!") - httpretty.register_uri(httpretty.POST, self.HTTP_URL, body="Hello!") + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register( + Entry.GET, self.HTTP_URL, body="Hello!", match_querystring=False + ) + Entry.single_register(Entry.GET, self.HTTPS_URL, body="Hello!") + Entry.single_register(Entry.POST, self.HTTP_URL, body="Hello!") def tearDown(self): super().tearDown() self.env_patch.stop() URLLib3Instrumentor().uninstrument() - httpretty.disable() - httpretty.reset() + self.mocketizer.exit() def assert_span(self, exporter=None, num_spans=1): if exporter is None: @@ -252,7 +253,7 @@ def test_schema_url_both_semconv(self): def test_basic_not_found(self): url_404 = "http://mock/status/404" - httpretty.register_uri(httpretty.GET, url_404, status=404) + Entry.single_register(Entry.GET, url_404, status=404) response = self.perform_request(url_404) self.assertEqual(404, response.status) @@ -263,7 +264,7 @@ def test_basic_not_found(self): def test_basic_not_found_new_semconv(self): url_404 = "http://mock/status/404" - httpretty.register_uri(httpretty.GET, url_404, status=404) + Entry.single_register(Entry.GET, url_404, status=404) response = self.perform_request(url_404) self.assertEqual(404, response.status) @@ -274,7 +275,7 @@ def test_basic_not_found_new_semconv(self): def test_basic_not_found_both_semconv(self): url_404 = "http://mock/status/404" - httpretty.register_uri(httpretty.GET, url_404, status=404) + Entry.single_register(Entry.GET, url_404, status=404) response = self.perform_request(url_404) self.assertEqual(404, response.status) @@ -284,9 +285,12 @@ def test_basic_not_found_both_semconv(self): self.assertEqual(404, span.attributes.get("http.status_code")) self.assertIs(trace.status.StatusCode.ERROR, span.status.status_code) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_nonstandard_http_method(self): - httpretty.register_uri( + Entry.single_register( "NONSTANDARD", self.HTTP_URL, body="Hello!", status=405 ) self.perform_request(self.HTTP_URL, method="NONSTANDARD") @@ -295,9 +299,12 @@ def test_nonstandard_http_method(self): self.assertEqual(span.attributes.get("http.method"), "_OTHER") self.assertEqual(span.attributes.get("http.status_code"), 405) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_nonstandard_http_method_new_semconv(self): - httpretty.register_uri( + Entry.single_register( "NONSTANDARD", self.HTTP_URL, body="Hello!", status=405 ) self.perform_request(self.HTTP_URL, method="NONSTANDARD") @@ -309,9 +316,12 @@ def test_nonstandard_http_method_new_semconv(self): ) self.assertEqual(span.attributes.get("http.response.status_code"), 405) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_nonstandard_http_method_both_semconv(self): - httpretty.register_uri( + Entry.single_register( "NONSTANDARD", self.HTTP_URL, body="Hello!", status=405 ) self.perform_request(self.HTTP_URL, method="NONSTANDARD") @@ -327,14 +337,14 @@ def test_nonstandard_http_method_both_semconv(self): def test_basic_http_non_default_port(self): url = "http://mock:666/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") response = self.perform_request(url) self.assert_success_span(response, url) def test_basic_http_absolute_url(self): url = "http://mock:666/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") pool = urllib3.HTTPConnectionPool("mock", port=666) response = pool.request("GET", url) @@ -342,7 +352,7 @@ def test_basic_http_absolute_url(self): def test_url_open_explicit_arg_parameters(self): url = "http://mock:666/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") pool = urllib3.HTTPConnectionPool("mock", port=666) response = pool.urlopen(method="GET", url="/status/200") @@ -350,8 +360,8 @@ def test_url_open_explicit_arg_parameters(self): def test_excluded_urls_explicit(self): url_201 = "http://mock/status/201" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url_201, status=201, ) @@ -365,8 +375,8 @@ def test_excluded_urls_explicit(self): def test_excluded_urls_from_env(self): url = "http://localhost/env_excluded_arg/123" - httpretty.register_uri( - httpretty.GET, + Entry.single_register( + Entry.GET, url, status=200, ) @@ -410,7 +420,7 @@ def test_context_propagation(self): self.assertEqual(b"Hello!", response.data) span = self.assert_span() - headers = dict(httpretty.last_request().headers) + headers = dict(Mocket.last_request().headers) self.assertIn(MockTextMapPropagator.TRACE_ID_KEY, headers) self.assertEqual( @@ -610,9 +620,12 @@ def test_custom_response_headers_captured(self): "X-Custom-Header": "custom-value", "X-Another-Header": "another-value", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request(url) @@ -665,9 +678,12 @@ def test_sensitive_headers_sanitized(self): "Set-Cookie": "session=abc123", "X-Secret": "secret", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -708,9 +724,12 @@ def test_custom_headers_with_regex(self): "X-Custom-Response-B": "value-B", "X-Other-Response-Header": "other-value", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -754,9 +773,12 @@ def test_custom_headers_case_insensitive(self): ) response_headers = {"X-ReSPoNse-HeaDER": "custom-value"} - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -785,9 +807,12 @@ def test_standard_http_headers_captured(self): "Content-Type": "text/plain", "Server": "TestServer/1.0", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, @@ -853,9 +878,12 @@ def test_capture_all_response_headers(self): "X-Response-Two": "value2", "X-Response-Three": "value3", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request(url) @@ -915,9 +943,12 @@ def test_capture_and_sanitize_environment_variables(self): "X-Response-One": "value1", "X-Response-Two": "value2", } - url = "http://mock//capture_headers" - httpretty.register_uri( - httpretty.GET, url, body="Hello!", adding_headers=response_headers + url = "http://mock/capture_headers" + Entry.single_register( + Entry.GET, + url, + body="Hello!", + headers=response_headers, ) self.perform_request( url, headers={"x-request-one": "one", "x-request-two": "two"} @@ -945,7 +976,7 @@ def test_urlopen_positional_headers(self): URLLib3Instrumentor().uninstrument() URLLib3Instrumentor().instrument(captured_request_headers=["X-Test"]) url = "http://mock/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") pool = urllib3.HTTPConnectionPool("mock") headers = {"X-Test": "Value"} response = pool.urlopen("GET", "/status/200", None, headers) @@ -959,7 +990,7 @@ def test_urlopen_all_positional(self): URLLib3Instrumentor().uninstrument() URLLib3Instrumentor().instrument(captured_request_headers=["X-Test"]) url = "http://mock/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") pool = urllib3.HTTPConnectionPool("mock") response = pool.urlopen( "GET", @@ -981,7 +1012,7 @@ def test_urlopen_mixed_args(self): URLLib3Instrumentor().uninstrument() URLLib3Instrumentor().instrument(captured_request_headers=["X-Test"]) url = "http://mock/status/200" - httpretty.register_uri(httpretty.GET, url, body="Hello!") + Entry.single_register(Entry.GET, url, body="Hello!") pool = urllib3.HTTPConnectionPool("mock") response = pool.urlopen( "GET", "/status/200", headers={"X-Test": "Value"}, body=None diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py index 6af7aa1b31..b68ba4a377 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py @@ -5,9 +5,10 @@ from timeit import default_timer from unittest import mock -import httpretty import urllib3 import urllib3.exceptions +from mocket import Mocketizer +from mocket.mocks.mockhttp import Entry from urllib3 import encode_multipart_formdata from opentelemetry.instrumentation._semconv import ( @@ -46,9 +47,10 @@ def setUp(self): _OpenTelemetrySemanticConventionStability._initialized = False self.env_patch.start() URLLib3Instrumentor().instrument() - httpretty.enable(allow_net_connect=False) - httpretty.register_uri(httpretty.GET, self.HTTP_URL, body="Hello!") - httpretty.register_uri(httpretty.POST, self.HTTP_URL, body="Hello!") + self.mocketizer = Mocketizer(strict_mode=True) + self.mocketizer.enter() + Entry.single_register(Entry.GET, self.HTTP_URL, body="Hello!") + Entry.single_register(Entry.POST, self.HTTP_URL, body="Hello!") self.pool = urllib3.PoolManager() def tearDown(self): @@ -57,8 +59,7 @@ def tearDown(self): self.pool.clear() URLLib3Instrumentor().uninstrument() - httpretty.disable() - httpretty.reset() + self.mocketizer.exit() def test_basic_metrics(self): start_time = default_timer() @@ -334,9 +335,12 @@ def test_basic_metrics_both_semconv(self): ], ) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_basic_metrics_nonstandard_http_method(self): - httpretty.register_uri( + Entry.single_register( "NONSTANDARD", self.HTTP_URL, body="", status=405 ) @@ -408,9 +412,12 @@ def test_basic_metrics_nonstandard_http_method(self): ], ) - @mock.patch("httpretty.http.HttpBaseClass.METHODS", ("NONSTANDARD",)) + @mock.patch( + "mocket.mocks.mockhttp.Entry.METHODS", + Entry.METHODS + ("NONSTANDARD",), + ) def test_basic_metrics_nonstandard_http_method_new_semconv(self): - httpretty.register_uri( + Entry.single_register( "NONSTANDARD", self.HTTP_URL, body="", status=405 ) start_time = default_timer() diff --git a/tox.ini b/tox.ini index 04d248d4b1..f8315f4650 100644 --- a/tox.ini +++ b/tox.ini @@ -178,8 +178,7 @@ envlist = ; below mean these dependencies are being used: ; 0: urllib3 >=1.0.0,<2.0.0 ; 1: urllib3 >=2.0.0,<3.0.0 - ; TODO #4200 Add py314 testing after replacing httpretty -- problem with https tests in 3.14 - py3{10,11,12,13}-test-instrumentation-urllib3-{0,1} + py3{10,11,12,13,14}-test-instrumentation-urllib3-{0,1} pypy3-test-instrumentation-urllib3-{0,1} lint-instrumentation-urllib3