|
16 | 16 |
|
17 | 17 | import unittest
|
18 | 18 | from timeit import default_timer
|
19 |
| -from unittest.mock import patch |
| 19 | +from unittest.mock import Mock, patch |
20 | 20 |
|
21 | 21 | import fastapi
|
22 | 22 | from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
23 | 23 | from fastapi.responses import JSONResponse
|
24 | 24 | from fastapi.testclient import TestClient
|
| 25 | +from pkg_resources import DistributionNotFound, iter_entry_points |
25 | 26 |
|
26 | 27 | import opentelemetry.instrumentation.fastapi as otel_fastapi
|
27 | 28 | from opentelemetry import trace
|
|
34 | 35 | _server_duration_attrs_old,
|
35 | 36 | )
|
36 | 37 | from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
| 38 | +from opentelemetry.instrumentation.auto_instrumentation._load import ( |
| 39 | + _load_instrumentors, |
| 40 | +) |
37 | 41 | from opentelemetry.sdk.metrics.export import (
|
38 | 42 | HistogramDataPoint,
|
39 | 43 | NumberDataPoint,
|
@@ -1024,13 +1028,65 @@ def client_response_hook(send_span, scope, message):
|
1024 | 1028 | )
|
1025 | 1029 |
|
1026 | 1030 |
|
| 1031 | +def get_distribution_with_fastapi(*args, **kwargs): |
| 1032 | + dist = args[0] |
| 1033 | + if dist == "fastapi~=0.58": |
| 1034 | + # Value does not matter. Only whether an exception is thrown |
| 1035 | + return None |
| 1036 | + raise DistributionNotFound() |
| 1037 | + |
| 1038 | + |
| 1039 | +def get_distribution_without_fastapi(*args, **kwargs): |
| 1040 | + raise DistributionNotFound() |
| 1041 | + |
| 1042 | + |
1027 | 1043 | class TestAutoInstrumentation(TestBaseAutoFastAPI):
|
1028 | 1044 | """Test the auto-instrumented variant
|
1029 | 1045 |
|
1030 | 1046 | Extending the manual instrumentation as most test cases apply
|
1031 | 1047 | to both.
|
1032 | 1048 | """
|
1033 | 1049 |
|
| 1050 | + def test_entry_point_exists(self): |
| 1051 | + eps = iter_entry_points("opentelemetry_instrumentor") |
| 1052 | + ep = next(eps) |
| 1053 | + self.assertEqual(ep.dist.key, "opentelemetry-instrumentation-fastapi") |
| 1054 | + self.assertEqual( |
| 1055 | + ep.module_name, "opentelemetry.instrumentation.fastapi" |
| 1056 | + ) |
| 1057 | + self.assertEqual(ep.attrs, ("FastAPIInstrumentor",)) |
| 1058 | + self.assertEqual(ep.name, "fastapi") |
| 1059 | + self.assertIsNone(next(eps, None)) |
| 1060 | + |
| 1061 | + @patch("opentelemetry.instrumentation.dependencies.get_distribution") |
| 1062 | + def test_instruments_with_fastapi_installed(self, mock_get_distribution): |
| 1063 | + mock_get_distribution.side_effect = get_distribution_with_fastapi |
| 1064 | + mock_distro = Mock() |
| 1065 | + _load_instrumentors(mock_distro) |
| 1066 | + mock_get_distribution.assert_called_once_with("fastapi~=0.58") |
| 1067 | + self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 1) |
| 1068 | + args = mock_distro.load_instrumentor.call_args.args |
| 1069 | + ep = args[0] |
| 1070 | + self.assertEqual(ep.dist.key, "opentelemetry-instrumentation-fastapi") |
| 1071 | + self.assertEqual( |
| 1072 | + ep.module_name, "opentelemetry.instrumentation.fastapi" |
| 1073 | + ) |
| 1074 | + self.assertEqual(ep.attrs, ("FastAPIInstrumentor",)) |
| 1075 | + self.assertEqual(ep.name, "fastapi") |
| 1076 | + |
| 1077 | + @patch("opentelemetry.instrumentation.dependencies.get_distribution") |
| 1078 | + def test_instruments_without_fastapi_installed( |
| 1079 | + self, mock_get_distribution |
| 1080 | + ): |
| 1081 | + mock_get_distribution.side_effect = get_distribution_without_fastapi |
| 1082 | + mock_distro = Mock() |
| 1083 | + _load_instrumentors(mock_distro) |
| 1084 | + mock_get_distribution.assert_called_once_with("fastapi~=0.58") |
| 1085 | + with self.assertRaises(DistributionNotFound): |
| 1086 | + mock_get_distribution("fastapi~=0.58") |
| 1087 | + self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 0) |
| 1088 | + mock_distro.load_instrumentor.assert_not_called() |
| 1089 | + |
1034 | 1090 | def _create_app(self):
|
1035 | 1091 | # instrumentation is handled by the instrument call
|
1036 | 1092 | resource = Resource.create({"key1": "value1", "key2": "value2"})
|
|
0 commit comments