From d135f20c2984ea76c4ea4df79e0c6af4f73d295b Mon Sep 17 00:00:00 2001 From: Jeremy Voss Date: Thu, 12 Sep 2024 00:36:59 -0700 Subject: [PATCH] Fastapi auto tests (#2860) * ep test passes * cleaned ep test * Corrected mock paths. * with installed works * tests pass * Clean up * Clean up * lint * lint --- CHANGELOG.md | 3 + .../tests/test_fastapi_instrumentation.py | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07510f643c..977f4eea8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `opentelemetry-instrumentation-fastapi` Add autoinstrumentation mechanism tests. + ([#2860](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2860)) + ## Version 1.27.0/0.48b0 () ### Added diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py index 634c74af6b..b8a6ef010e 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py @@ -16,12 +16,13 @@ import unittest from timeit import default_timer -from unittest.mock import patch +from unittest.mock import Mock, patch import fastapi from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware from fastapi.responses import JSONResponse from fastapi.testclient import TestClient +from pkg_resources import DistributionNotFound, iter_entry_points import opentelemetry.instrumentation.fastapi as otel_fastapi from opentelemetry import trace @@ -34,6 +35,9 @@ _server_duration_attrs_old, ) from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware +from opentelemetry.instrumentation.auto_instrumentation._load import ( + _load_instrumentors, +) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -1024,6 +1028,18 @@ def client_response_hook(send_span, scope, message): ) +def get_distribution_with_fastapi(*args, **kwargs): + dist = args[0] + if dist == "fastapi~=0.58": + # Value does not matter. Only whether an exception is thrown + return None + raise DistributionNotFound() + + +def get_distribution_without_fastapi(*args, **kwargs): + raise DistributionNotFound() + + class TestAutoInstrumentation(TestBaseAutoFastAPI): """Test the auto-instrumented variant @@ -1031,6 +1047,46 @@ class TestAutoInstrumentation(TestBaseAutoFastAPI): to both. """ + def test_entry_point_exists(self): + eps = iter_entry_points("opentelemetry_instrumentor") + ep = next(eps) + self.assertEqual(ep.dist.key, "opentelemetry-instrumentation-fastapi") + self.assertEqual( + ep.module_name, "opentelemetry.instrumentation.fastapi" + ) + self.assertEqual(ep.attrs, ("FastAPIInstrumentor",)) + self.assertEqual(ep.name, "fastapi") + self.assertIsNone(next(eps, None)) + + @patch("opentelemetry.instrumentation.dependencies.get_distribution") + def test_instruments_with_fastapi_installed(self, mock_get_distribution): + mock_get_distribution.side_effect = get_distribution_with_fastapi + mock_distro = Mock() + _load_instrumentors(mock_distro) + mock_get_distribution.assert_called_once_with("fastapi~=0.58") + self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 1) + args = mock_distro.load_instrumentor.call_args.args + ep = args[0] + self.assertEqual(ep.dist.key, "opentelemetry-instrumentation-fastapi") + self.assertEqual( + ep.module_name, "opentelemetry.instrumentation.fastapi" + ) + self.assertEqual(ep.attrs, ("FastAPIInstrumentor",)) + self.assertEqual(ep.name, "fastapi") + + @patch("opentelemetry.instrumentation.dependencies.get_distribution") + def test_instruments_without_fastapi_installed( + self, mock_get_distribution + ): + mock_get_distribution.side_effect = get_distribution_without_fastapi + mock_distro = Mock() + _load_instrumentors(mock_distro) + mock_get_distribution.assert_called_once_with("fastapi~=0.58") + with self.assertRaises(DistributionNotFound): + mock_get_distribution("fastapi~=0.58") + self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 0) + mock_distro.load_instrumentor.assert_not_called() + def _create_app(self): # instrumentation is handled by the instrument call resource = Resource.create({"key1": "value1", "key2": "value2"})