mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-08-02 11:31:52 +08:00
Support cursor based queries (#2501)
* Support cursor based queries * Add unit test * Start of a test case * Update tests * Mock out all connection stuff * Run black * Fix typo * Use old python * Run black on the tests * Add changelog * Fix sorting * Use python 3.8 as baseline --------- Co-authored-by: Diego Hurtado <ocelotl@users.noreply.github.com>
This commit is contained in:
@ -1,4 +1,9 @@
|
||||
from asyncpg import Connection
|
||||
import asyncio
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from asyncpg import Connection, Record, cursor
|
||||
from wrapt import ObjectProxy
|
||||
|
||||
from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
@ -34,3 +39,69 @@ class TestAsyncPGInstrumentation(TestBase):
|
||||
self.assertFalse(
|
||||
hasattr(method, "_opentelemetry_ext_asyncpg_applied")
|
||||
)
|
||||
|
||||
def test_cursor_instrumentation(self):
|
||||
def assert_wrapped(assert_fnc):
|
||||
for cls, methods in [
|
||||
(cursor.Cursor, ("forward", "fetch", "fetchrow")),
|
||||
(cursor.CursorIterator, ("__anext__",)),
|
||||
]:
|
||||
for method_name in methods:
|
||||
method = getattr(cls, method_name, None)
|
||||
assert_fnc(
|
||||
isinstance(method, ObjectProxy),
|
||||
f"{method} isinstance {type(method)}",
|
||||
)
|
||||
|
||||
assert_wrapped(self.assertFalse)
|
||||
AsyncPGInstrumentor().instrument()
|
||||
assert_wrapped(self.assertTrue)
|
||||
AsyncPGInstrumentor().uninstrument()
|
||||
assert_wrapped(self.assertFalse)
|
||||
|
||||
def test_cursor_span_creation(self):
|
||||
"""Test the cursor wrapper if it creates spans correctly."""
|
||||
|
||||
# Mock out all interaction with postgres
|
||||
async def bind_mock(*args, **kwargs):
|
||||
return []
|
||||
|
||||
async def exec_mock(*args, **kwargs):
|
||||
return [], None, True
|
||||
|
||||
conn = mock.Mock()
|
||||
conn.is_closed = lambda: False
|
||||
|
||||
conn._protocol = mock.Mock()
|
||||
conn._protocol.bind = bind_mock
|
||||
conn._protocol.execute = exec_mock
|
||||
conn._protocol.bind_execute = exec_mock
|
||||
conn._protocol.close_portal = bind_mock
|
||||
|
||||
state = mock.Mock()
|
||||
state.closed = False
|
||||
|
||||
apg = AsyncPGInstrumentor()
|
||||
apg.instrument(tracer_provider=self.tracer_provider)
|
||||
|
||||
# init the cursor and fetch a single record
|
||||
crs = cursor.Cursor(conn, "SELECT * FROM test", state, [], Record)
|
||||
asyncio.run(crs._init(1))
|
||||
asyncio.run(crs.fetch(1))
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 1)
|
||||
self.assertEqual(spans[0].name, "CURSOR: SELECT")
|
||||
self.assertTrue(spans[0].status.is_ok)
|
||||
|
||||
# Now test that the StopAsyncIteration of the cursor does not get recorded as an ERROR
|
||||
crs_iter = cursor.CursorIterator(
|
||||
conn, "SELECT * FROM test", state, [], Record, 1, 1
|
||||
)
|
||||
|
||||
with pytest.raises(StopAsyncIteration):
|
||||
asyncio.run(crs_iter.__anext__())
|
||||
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 2)
|
||||
self.assertEqual([span.status.is_ok for span in spans], [True, True])
|
||||
|
Reference in New Issue
Block a user