mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-28 20:52:57 +08:00
Fix running async psycopg tests (#2540)
This commit is contained in:

committed by
GitHub

parent
c06fd1dd53
commit
95fea2bfa7
@ -10,7 +10,6 @@ protobuf==3.20.3
|
||||
py==1.11.0
|
||||
py-cpuinfo==9.0.0
|
||||
pytest==7.1.3
|
||||
pytest-asyncio==0.23.5
|
||||
pytest-benchmark==4.0.0
|
||||
tomli==2.0.1
|
||||
typing_extensions==4.9.0
|
||||
|
@ -11,24 +11,9 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# unittest.IsolatedAsyncioTestCase was introduced in Python 3.8. It's use
|
||||
# simplifies the following tests. Without it, the amount of test code
|
||||
# increases significantly, with most of the additional code handling
|
||||
# the asyncio set up.
|
||||
from unittest import TestCase
|
||||
|
||||
class IsolatedAsyncioTestCase(TestCase):
|
||||
def run(self, result=None):
|
||||
self.skipTest(
|
||||
"This test requires Python 3.8 for unittest.IsolatedAsyncioTestCase"
|
||||
)
|
||||
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
|
||||
import grpc
|
||||
import pytest
|
||||
|
||||
import opentelemetry.instrumentation.grpc
|
||||
from opentelemetry import trace
|
||||
@ -65,7 +50,6 @@ class RecordingInterceptor(grpc.aio.UnaryUnaryClientInterceptor):
|
||||
return await continuation(client_call_details, request)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestAioClientInterceptor(TestBase, IsolatedAsyncioTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -11,27 +11,10 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# unittest.IsolatedAsyncioTestCase was introduced in Python 3.8. It's use
|
||||
# simplifies the following tests. Without it, the amount of test code
|
||||
# increases significantly, with most of the additional code handling
|
||||
# the asyncio set up.
|
||||
from unittest import TestCase
|
||||
|
||||
class IsolatedAsyncioTestCase(TestCase):
|
||||
def run(self, result=None):
|
||||
self.skipTest(
|
||||
"This test requires Python 3.8 for unittest.IsolatedAsyncioTestCase"
|
||||
)
|
||||
|
||||
|
||||
import os
|
||||
from unittest import mock
|
||||
from unittest import IsolatedAsyncioTestCase, mock
|
||||
|
||||
import grpc
|
||||
import pytest
|
||||
|
||||
from opentelemetry.instrumentation.grpc import (
|
||||
GrpcAioInstrumentorClient,
|
||||
@ -50,7 +33,6 @@ from ._server import create_test_server
|
||||
from .protobuf import test_server_pb2_grpc # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestAioClientInterceptorFiltered(TestBase, IsolatedAsyncioTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -11,24 +11,9 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# unittest.IsolatedAsyncioTestCase was introduced in Python 3.8. It's use
|
||||
# simplifies the following tests. Without it, the amount of test code
|
||||
# increases significantly, with most of the additional code handling
|
||||
# the asyncio set up.
|
||||
from unittest import TestCase
|
||||
|
||||
class IsolatedAsyncioTestCase(TestCase):
|
||||
def run(self, result=None):
|
||||
self.skipTest(
|
||||
"This test requires Python 3.8 for unittest.IsolatedAsyncioTestCase"
|
||||
)
|
||||
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
|
||||
import grpc
|
||||
import pytest
|
||||
|
||||
from opentelemetry.instrumentation.grpc import GrpcAioInstrumentorClient
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
@ -54,7 +39,6 @@ def response_hook_with_exception(_span, _response):
|
||||
raise Exception() # pylint: disable=broad-exception-raised
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestAioClientInterceptorWithHooks(TestBase, IsolatedAsyncioTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
@ -12,26 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# unittest.IsolatedAsyncioTestCase was introduced in Python 3.8. It's use
|
||||
# simplifies the following tests. Without it, the amount of test code
|
||||
# increases significantly, with most of the additional code handling
|
||||
# the asyncio set up.
|
||||
from unittest import TestCase
|
||||
|
||||
class IsolatedAsyncioTestCase(TestCase):
|
||||
def run(self, result=None):
|
||||
self.skipTest(
|
||||
"This test requires Python 3.8 for unittest.IsolatedAsyncioTestCase"
|
||||
)
|
||||
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
|
||||
import grpc
|
||||
import grpc.aio
|
||||
import pytest
|
||||
|
||||
import opentelemetry.instrumentation.grpc
|
||||
from opentelemetry import trace
|
||||
@ -97,7 +81,6 @@ async def run_with_test_server(
|
||||
return resp
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestOpenTelemetryAioServerInterceptor(TestBase, IsolatedAsyncioTestCase):
|
||||
async def test_instrumentor(self):
|
||||
"""Check that automatic instrumentation configures the interceptor"""
|
||||
|
@ -11,25 +11,10 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
try:
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
except ImportError:
|
||||
# unittest.IsolatedAsyncioTestCase was introduced in Python 3.8. It's use
|
||||
# simplifies the following tests. Without it, the amount of test code
|
||||
# increases significantly, with most of the additional code handling
|
||||
# the asyncio set up.
|
||||
from unittest import TestCase
|
||||
|
||||
class IsolatedAsyncioTestCase(TestCase):
|
||||
def run(self, result=None):
|
||||
self.skipTest(
|
||||
"This test requires Python 3.8 for unittest.IsolatedAsyncioTestCase"
|
||||
)
|
||||
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
|
||||
import grpc
|
||||
import grpc.aio
|
||||
import pytest
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.instrumentation.grpc import (
|
||||
@ -68,7 +53,6 @@ async def run_with_test_server(
|
||||
return resp
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestOpenTelemetryAioServerInterceptor(TestBase, IsolatedAsyncioTestCase):
|
||||
async def test_instrumentor(self):
|
||||
"""Check that automatic instrumentation configures the interceptor"""
|
||||
|
@ -12,9 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import asyncio
|
||||
import types
|
||||
from unittest import mock
|
||||
from unittest import IsolatedAsyncioTestCase, mock
|
||||
|
||||
import psycopg
|
||||
|
||||
@ -114,6 +113,10 @@ class MockAsyncConnection:
|
||||
return cur
|
||||
return MockAsyncCursor()
|
||||
|
||||
def execute(self, query, params=None, *, prepare=None, binary=False):
|
||||
cur = self.cursor()
|
||||
return cur.execute(query, params, prepare=prepare)
|
||||
|
||||
def get_dsn_parameters(self): # pylint: disable=no-self-use
|
||||
return {"dbname": "test"}
|
||||
|
||||
@ -124,7 +127,8 @@ class MockAsyncConnection:
|
||||
return mock.MagicMock(spec=types.MethodType)
|
||||
|
||||
|
||||
class TestPostgresqlIntegration(TestBase):
|
||||
class PostgresqlIntegrationTestMixin:
|
||||
# pylint: disable=invalid-name
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.cursor_mock = mock.patch(
|
||||
@ -148,6 +152,7 @@ class TestPostgresqlIntegration(TestBase):
|
||||
self.connection_sync_mock.start()
|
||||
self.connection_async_mock.start()
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.memory_exporter.clear()
|
||||
@ -159,6 +164,8 @@ class TestPostgresqlIntegration(TestBase):
|
||||
with self.disable_logging():
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
||||
|
||||
class TestPostgresqlIntegration(PostgresqlIntegrationTestMixin, TestBase):
|
||||
# pylint: disable=unused-argument
|
||||
def test_instrumentor(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
@ -221,60 +228,6 @@ class TestPostgresqlIntegration(TestBase):
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
async def test_wrap_async_connection_class_with_cursor(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
async def test_async_connection():
|
||||
acnx = await psycopg.AsyncConnection.connect(database="test")
|
||||
async with acnx as cnx:
|
||||
async with cnx.cursor() as cursor:
|
||||
await cursor.execute("SELECT * FROM test")
|
||||
|
||||
asyncio.run(test_async_connection())
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
span = spans_list[0]
|
||||
|
||||
# Check version and name in span's instrumentation info
|
||||
self.assertEqualSpanInstrumentationInfo(
|
||||
span, opentelemetry.instrumentation.psycopg
|
||||
)
|
||||
|
||||
# check that no spans are generated after uninstrument
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
||||
asyncio.run(test_async_connection())
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
async def test_instrumentor_with_async_connection_class(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
async def test_async_connection():
|
||||
acnx = await psycopg.AsyncConnection.connect(database="test")
|
||||
async with acnx as cnx:
|
||||
await cnx.execute("SELECT * FROM test")
|
||||
|
||||
asyncio.run(test_async_connection())
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
span = spans_list[0]
|
||||
|
||||
# Check version and name in span's instrumentation info
|
||||
self.assertEqualSpanInstrumentationInfo(
|
||||
span, opentelemetry.instrumentation.psycopg
|
||||
)
|
||||
|
||||
# check that no spans are generated after uninstrument
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
asyncio.run(test_async_connection())
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
def test_span_name(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
@ -301,33 +254,6 @@ class TestPostgresqlIntegration(TestBase):
|
||||
self.assertEqual(spans_list[4].name, "query")
|
||||
self.assertEqual(spans_list[5].name, "query")
|
||||
|
||||
async def test_span_name_async(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
cnx = psycopg.AsyncConnection.connect(database="test")
|
||||
async with cnx.cursor() as cursor:
|
||||
await cursor.execute("Test query", ("param1Value", False))
|
||||
await cursor.execute(
|
||||
"""multi
|
||||
line
|
||||
query"""
|
||||
)
|
||||
await cursor.execute("tab\tseparated query")
|
||||
await cursor.execute("/* leading comment */ query")
|
||||
await cursor.execute(
|
||||
"/* leading comment */ query /* trailing comment */"
|
||||
)
|
||||
await cursor.execute("query /* trailing comment */")
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 6)
|
||||
self.assertEqual(spans_list[0].name, "Test")
|
||||
self.assertEqual(spans_list[1].name, "multi")
|
||||
self.assertEqual(spans_list[2].name, "tab")
|
||||
self.assertEqual(spans_list[3].name, "query")
|
||||
self.assertEqual(spans_list[4].name, "query")
|
||||
self.assertEqual(spans_list[5].name, "query")
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def test_not_recording(self):
|
||||
mock_tracer = mock.Mock()
|
||||
@ -348,26 +274,6 @@ class TestPostgresqlIntegration(TestBase):
|
||||
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
async def test_not_recording_async(self):
|
||||
mock_tracer = mock.Mock()
|
||||
mock_span = mock.Mock()
|
||||
mock_span.is_recording.return_value = False
|
||||
mock_tracer.start_span.return_value = mock_span
|
||||
PsycopgInstrumentor().instrument()
|
||||
with mock.patch("opentelemetry.trace.get_tracer") as tracer:
|
||||
tracer.return_value = mock_tracer
|
||||
cnx = psycopg.AsyncConnection.connect(database="test")
|
||||
async with cnx.cursor() as cursor:
|
||||
query = "SELECT * FROM test"
|
||||
cursor.execute(query)
|
||||
self.assertFalse(mock_span.is_recording())
|
||||
self.assertTrue(mock_span.is_recording.called)
|
||||
self.assertFalse(mock_span.set_attribute.called)
|
||||
self.assertFalse(mock_span.set_status.called)
|
||||
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def test_custom_tracer_provider(self):
|
||||
resource = resources.Resource.create({})
|
||||
@ -477,3 +383,108 @@ class TestPostgresqlIntegration(TestBase):
|
||||
cursor.execute(query)
|
||||
kwargs = event_mocked.call_args[1]
|
||||
self.assertEqual(kwargs["enable_commenter"], False)
|
||||
|
||||
|
||||
class TestPostgresqlIntegrationAsync(
|
||||
PostgresqlIntegrationTestMixin, TestBase, IsolatedAsyncioTestCase
|
||||
):
|
||||
async def test_wrap_async_connection_class_with_cursor(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
async def test_async_connection():
|
||||
acnx = await psycopg.AsyncConnection.connect("test")
|
||||
async with acnx as cnx:
|
||||
async with cnx.cursor() as cursor:
|
||||
await cursor.execute("SELECT * FROM test")
|
||||
|
||||
await test_async_connection()
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
span = spans_list[0]
|
||||
|
||||
# Check version and name in span's instrumentation info
|
||||
self.assertEqualSpanInstrumentationInfo(
|
||||
span, opentelemetry.instrumentation.psycopg
|
||||
)
|
||||
|
||||
# check that no spans are generated after uninstrument
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
||||
await test_async_connection()
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
async def test_instrumentor_with_async_connection_class(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
async def test_async_connection():
|
||||
acnx = await psycopg.AsyncConnection.connect("test")
|
||||
async with acnx as cnx:
|
||||
await cnx.execute("SELECT * FROM test")
|
||||
|
||||
await test_async_connection()
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
span = spans_list[0]
|
||||
|
||||
# Check version and name in span's instrumentation info
|
||||
self.assertEqualSpanInstrumentationInfo(
|
||||
span, opentelemetry.instrumentation.psycopg
|
||||
)
|
||||
|
||||
# check that no spans are generated after uninstrument
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
await test_async_connection()
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 1)
|
||||
|
||||
async def test_span_name_async(self):
|
||||
PsycopgInstrumentor().instrument()
|
||||
|
||||
cnx = await psycopg.AsyncConnection.connect("test")
|
||||
async with cnx.cursor() as cursor:
|
||||
await cursor.execute("Test query", ("param1Value", False))
|
||||
await cursor.execute(
|
||||
"""multi
|
||||
line
|
||||
query"""
|
||||
)
|
||||
await cursor.execute("tab\tseparated query")
|
||||
await cursor.execute("/* leading comment */ query")
|
||||
await cursor.execute(
|
||||
"/* leading comment */ query /* trailing comment */"
|
||||
)
|
||||
await cursor.execute("query /* trailing comment */")
|
||||
|
||||
spans_list = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans_list), 6)
|
||||
self.assertEqual(spans_list[0].name, "Test")
|
||||
self.assertEqual(spans_list[1].name, "multi")
|
||||
self.assertEqual(spans_list[2].name, "tab")
|
||||
self.assertEqual(spans_list[3].name, "query")
|
||||
self.assertEqual(spans_list[4].name, "query")
|
||||
self.assertEqual(spans_list[5].name, "query")
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
async def test_not_recording_async(self):
|
||||
mock_tracer = mock.Mock()
|
||||
mock_span = mock.Mock()
|
||||
mock_span.is_recording.return_value = False
|
||||
mock_tracer.start_span.return_value = mock_span
|
||||
PsycopgInstrumentor().instrument()
|
||||
with mock.patch("opentelemetry.trace.get_tracer") as tracer:
|
||||
tracer.return_value = mock_tracer
|
||||
cnx = await psycopg.AsyncConnection.connect("test")
|
||||
async with cnx.cursor() as cursor:
|
||||
query = "SELECT * FROM test"
|
||||
await cursor.execute(query)
|
||||
self.assertFalse(mock_span.is_recording())
|
||||
self.assertTrue(mock_span.is_recording.called)
|
||||
self.assertFalse(mock_span.set_attribute.called)
|
||||
self.assertFalse(mock_span.set_status.called)
|
||||
|
||||
PsycopgInstrumentor().uninstrument()
|
||||
|
Reference in New Issue
Block a user