Skip to content

Commit 3abf770

Browse files
committed
Add tests around the psycopg2 and psycopg2-binary packages to make sure auto instrumentation works for both
1 parent 8835e96 commit 3abf770

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# type: ignore
15+
16+
17+
# Copyright The OpenTelemetry Authors
18+
#
19+
# Licensed under the Apache License, Version 2.0 (the "License");
20+
# you may not use this file except in compliance with the License.
21+
# You may obtain a copy of the License at
22+
#
23+
# http://www.apache.org/licenses/LICENSE-2.0
24+
#
25+
# Unless required by applicable law or agreed to in writing, software
26+
# distributed under the License is distributed on an "AS IS" BASIS,
27+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28+
# See the License for the specific language governing permissions and
29+
# limitations under the License.
30+
from importlib.metadata import PackageNotFoundError
31+
from unittest import TestCase
32+
from unittest.mock import call, patch, Mock
33+
34+
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
35+
from opentelemetry.instrumentation.psycopg2.package import (
36+
_instruments,
37+
_instruments_psycopg2,
38+
_instruments_psycopg2_binary,
39+
)
40+
from opentelemetry.instrumentation.auto_instrumentation._load import (
41+
_load_instrumentors,
42+
)
43+
44+
45+
46+
class TestPsycopg2InstrumentationDependencies(TestCase):
47+
"""
48+
The psycopg2 instrumentation is special in that it can instrument
49+
multiple packages. (psycopg2 and psycopg2-binary)
50+
The tests below are to verify that the correct package is instrumented
51+
depending on which package is installed.
52+
Including the cases where both are installed and neither is installed.
53+
These tests were added after we found a bug where the auto instrumentation
54+
path attempted to use a different dependency check than the manual
55+
instrumentation path. This caused the auto instrumentation to fail.
56+
57+
"""
58+
59+
@patch("opentelemetry.instrumentation.psycopg2.distribution")
60+
def test_instrumentation_dependencies_psycopg2_installed(
61+
self, mock_distribution
62+
) -> None:
63+
instrumentation = Psycopg2Instrumentor()
64+
65+
def _distribution(name):
66+
if name == "psycopg2":
67+
return None
68+
raise PackageNotFoundError
69+
70+
mock_distribution.side_effect = _distribution
71+
package_to_instrument = instrumentation.instrumentation_dependencies()
72+
73+
self.assertEqual(mock_distribution.call_count, 1)
74+
self.assertEqual(
75+
mock_distribution.mock_calls,
76+
[
77+
call("psycopg2"),
78+
],
79+
)
80+
self.assertEqual(package_to_instrument, (_instruments_psycopg2,))
81+
82+
@patch("opentelemetry.instrumentation.psycopg2.distribution")
83+
def test_instrumentation_dependencies_psycopg2_binary_installed(
84+
self, mock_distribution
85+
) -> None:
86+
instrumentation = Psycopg2Instrumentor()
87+
88+
def _distribution(name):
89+
if name == "psycopg2-binary":
90+
return None
91+
raise PackageNotFoundError
92+
93+
mock_distribution.side_effect = _distribution
94+
package_to_instrument = instrumentation.instrumentation_dependencies()
95+
96+
self.assertEqual(mock_distribution.call_count, 2)
97+
self.assertEqual(
98+
mock_distribution.mock_calls,
99+
[
100+
call("psycopg2"),
101+
call("psycopg2-binary"),
102+
]
103+
)
104+
self.assertEqual(
105+
package_to_instrument, (_instruments_psycopg2_binary,)
106+
)
107+
108+
@patch("opentelemetry.instrumentation.psycopg2.distribution")
109+
def test_instrumentation_dependencies_both_installed(
110+
self, mock_distribution
111+
) -> None:
112+
instrumentation = Psycopg2Instrumentor()
113+
114+
def _distribution(name):
115+
# Function raises PackageNotFoundError
116+
# if name is not in the list. We will
117+
# not raise it for both names
118+
return None
119+
120+
mock_distribution.side_effect = _distribution
121+
package_to_instrument = instrumentation.instrumentation_dependencies()
122+
123+
self.assertEqual(mock_distribution.call_count, 1)
124+
self.assertEqual(
125+
mock_distribution.mock_calls, [call("psycopg2")]
126+
)
127+
self.assertEqual(
128+
package_to_instrument, (_instruments_psycopg2,)
129+
)
130+
131+
@patch("opentelemetry.instrumentation.psycopg2.distribution")
132+
def test_instrumentation_dependencies_none_installed(
133+
self, mock_distribution
134+
) -> None:
135+
instrumentation = Psycopg2Instrumentor()
136+
137+
def _distribution(name):
138+
# Function raises PackageNotFoundError
139+
# if name is not in the list. We will
140+
# raise it for both names to simulate
141+
# neither being installed
142+
raise PackageNotFoundError
143+
144+
mock_distribution.side_effect = _distribution
145+
package_to_instrument = instrumentation.instrumentation_dependencies()
146+
147+
assert mock_distribution.call_count == 2
148+
assert mock_distribution.mock_calls == [
149+
call("psycopg2"),
150+
call("psycopg2-binary"),
151+
]
152+
assert package_to_instrument == _instruments
153+
154+
self.assertEqual(mock_distribution.call_count, 2)
155+
self.assertEqual(
156+
mock_distribution.mock_calls,
157+
[
158+
call("psycopg2"),
159+
call("psycopg2-binary"),
160+
],
161+
)
162+
self.assertEqual(package_to_instrument, _instruments)
163+
164+
# This test is to verify that the auto instrumentation path
165+
# will auto instrument psycopg2 or psycopg2-binary is installed.
166+
# Note there is only one test here but it is run twice in tox
167+
# once with the psycopg2 package installed and once with
168+
# psycopg2-binary installed.
169+
@patch("opentelemetry.instrumentation.auto_instrumentation._load._logger")
170+
def test_instruments_with_psycopg2_installed(self, mock_logger):
171+
172+
def _instrumentation_loaded_successfully_call():
173+
return call("Instrumented %s", "psycopg2")
174+
175+
mock_distro = Mock()
176+
mock_distro.load_instrumentor.return_value = None
177+
_load_instrumentors(mock_distro)
178+
self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 1)
179+
(ep,) = mock_distro.load_instrumentor.call_args.args
180+
self.assertEqual(ep.name, "psycopg2")
181+
mock_logger.debug.assert_has_calls(
182+
[_instrumentation_loaded_successfully_call()]
183+
)

0 commit comments

Comments
 (0)