mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2026-03-13 08:10:39 +08:00
feat: add unit tests for tortoiseorm (#4141)
* feat: add unit tests for tortoiseorm * update CHANGELOG.md --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
This commit is contained in:
@@ -49,6 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
([#4140](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4140))
|
||||
- `opentelemetry-instrumentation-pyramid` Implement new semantic convention opt-in migration
|
||||
([#3982](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3982))
|
||||
- `opentelemetry-instrumentation-tortoiseorm` Add unit tests for Tortoise ORM instrumentation
|
||||
([#4141](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4141))
|
||||
- `opentelemetry-instrumentation-pyramid`: pass request attributes at span creation
|
||||
([#4139](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4139))
|
||||
|
||||
|
||||
@@ -48,7 +48,10 @@ from opentelemetry import trace
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
from opentelemetry.instrumentation.tortoiseorm.package import _instruments
|
||||
from opentelemetry.instrumentation.tortoiseorm.version import __version__
|
||||
from opentelemetry.instrumentation.utils import unwrap
|
||||
from opentelemetry.instrumentation.utils import (
|
||||
is_instrumentation_enabled,
|
||||
unwrap,
|
||||
)
|
||||
from opentelemetry.semconv._incubating.attributes.db_attributes import (
|
||||
DB_NAME,
|
||||
DB_STATEMENT,
|
||||
@@ -270,6 +273,9 @@ class TortoiseORMInstrumentor(BaseInstrumentor):
|
||||
return span_attributes
|
||||
|
||||
async def _do_execute(self, func, instance, args, kwargs):
|
||||
if not is_instrumentation_enabled():
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
exception = None
|
||||
name = args[0].split()[0]
|
||||
|
||||
@@ -297,6 +303,9 @@ class TortoiseORMInstrumentor(BaseInstrumentor):
|
||||
return result
|
||||
|
||||
async def _from_queryset(self, func, modelcls, args, kwargs):
|
||||
if not is_instrumentation_enabled():
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
exception = None
|
||||
name = f"pydantic.{func.__name__}"
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ iso8601==1.1.0
|
||||
packaging==24.0
|
||||
pluggy==1.5.0
|
||||
py-cpuinfo==9.0.0
|
||||
pydantic==2.8.2
|
||||
pydantic_core==2.20.1
|
||||
pydantic==2.12.5
|
||||
pydantic_core==2.41.5
|
||||
pypika-tortoise==0.1.6
|
||||
pytest==7.4.4
|
||||
pytz==2024.1
|
||||
tomli==2.0.1
|
||||
tortoise-orm==0.20.0
|
||||
typing_extensions==4.12.2
|
||||
typing_extensions==4.14.1
|
||||
wrapt==1.16.0
|
||||
zipp==3.19.2
|
||||
-e opentelemetry-instrumentation
|
||||
|
||||
@@ -12,10 +12,27 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import asyncio
|
||||
|
||||
from tortoise import Tortoise, fields, models
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
|
||||
from opentelemetry.instrumentation.utils import suppress_instrumentation
|
||||
from opentelemetry.semconv._incubating.attributes.db_attributes import (
|
||||
DB_NAME,
|
||||
DB_STATEMENT,
|
||||
DB_SYSTEM,
|
||||
)
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
class MockModel(models.Model):
|
||||
id = fields.IntField(pk=True)
|
||||
name = fields.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class TestTortoiseORMInstrumentor(TestBase):
|
||||
@@ -26,9 +43,94 @@ class TestTortoiseORMInstrumentor(TestBase):
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
TortoiseORMInstrumentor().uninstrument()
|
||||
self._async_call(Tortoise._drop_databases())
|
||||
|
||||
def test_tortoise(self):
|
||||
# FIXME This instrumentation has no tests at all and should have some
|
||||
# tests. This is being added just to make pytest not fail because no
|
||||
# tests are being collected for tortoise at the moment.
|
||||
pass
|
||||
# pylint: disable-next=no-self-use
|
||||
def _async_call(self, coro):
|
||||
return asyncio.run(coro)
|
||||
|
||||
# pylint: disable-next=no-self-use
|
||||
async def _init_tortoise(self):
|
||||
await Tortoise.init(
|
||||
db_url="sqlite://:memory:",
|
||||
modules={"models": [__name__]},
|
||||
)
|
||||
await Tortoise.generate_schemas()
|
||||
|
||||
def test_trace_integration(self):
|
||||
async def run():
|
||||
await self._init_tortoise()
|
||||
await MockModel.create(name="Test 1")
|
||||
await MockModel.filter(name="Test 1").first()
|
||||
|
||||
self._async_call(run())
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
|
||||
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
|
||||
self.assertGreaterEqual(len(crud_spans), 2)
|
||||
|
||||
insert_span = next(s for s in crud_spans if s.name == "INSERT")
|
||||
self.assertEqual(insert_span.kind, trace.SpanKind.CLIENT)
|
||||
self.assertEqual(insert_span.attributes[DB_SYSTEM], "sqlite")
|
||||
self.assertEqual(insert_span.attributes[DB_NAME], ":memory:")
|
||||
self.assertIn("INSERT", insert_span.attributes[DB_STATEMENT])
|
||||
|
||||
select_span = next(s for s in crud_spans if s.name == "SELECT")
|
||||
self.assertEqual(select_span.kind, trace.SpanKind.CLIENT)
|
||||
self.assertEqual(select_span.attributes[DB_SYSTEM], "sqlite")
|
||||
self.assertEqual(select_span.attributes[DB_NAME], ":memory:")
|
||||
self.assertIn("SELECT", select_span.attributes[DB_STATEMENT])
|
||||
|
||||
def test_capture_parameters(self):
|
||||
TortoiseORMInstrumentor().uninstrument()
|
||||
TortoiseORMInstrumentor().instrument(capture_parameters=True)
|
||||
|
||||
async def run():
|
||||
await self._init_tortoise()
|
||||
await MockModel.create(name="Test Parameterized")
|
||||
|
||||
self._async_call(run())
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
insert_span = next(s for s in spans if s.name == "INSERT")
|
||||
self.assertIn("db.statement.parameters", insert_span.attributes)
|
||||
self.assertIn(
|
||||
"Test Parameterized",
|
||||
insert_span.attributes["db.statement.parameters"],
|
||||
)
|
||||
|
||||
def test_uninstrument(self):
|
||||
TortoiseORMInstrumentor().uninstrument()
|
||||
|
||||
async def run():
|
||||
await self._init_tortoise()
|
||||
await MockModel.create(name="Test Uninstrumented")
|
||||
|
||||
self._async_call(run())
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
|
||||
self.assertEqual(len(crud_spans), 0)
|
||||
|
||||
def test_suppress_instrumentation(self):
|
||||
async def run():
|
||||
await self._init_tortoise()
|
||||
with suppress_instrumentation():
|
||||
await MockModel.create(name="Test Suppressed")
|
||||
|
||||
self._async_call(run())
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
|
||||
self.assertEqual(len(crud_spans), 0)
|
||||
|
||||
def test_no_op_tracer_provider(self):
|
||||
TortoiseORMInstrumentor().uninstrument()
|
||||
TortoiseORMInstrumentor().instrument(
|
||||
tracer_provider=trace.NoOpTracerProvider()
|
||||
)
|
||||
|
||||
async def run():
|
||||
await self._init_tortoise()
|
||||
await MockModel.create(name="Test NoOp")
|
||||
|
||||
self._async_call(run())
|
||||
spans = self.memory_exporter.get_finished_spans()
|
||||
self.assertEqual(len(spans), 0)
|
||||
|
||||
@@ -57,7 +57,7 @@ dependencies = [
|
||||
"opentelemetry-instrumentation-system-metrics",
|
||||
"opentelemetry-instrumentation-threading",
|
||||
"opentelemetry-instrumentation-tornado",
|
||||
"opentelemetry-instrumentation-tortoiseorm",
|
||||
"opentelemetry-instrumentation-tortoiseorm[instruments]",
|
||||
"opentelemetry-instrumentation-urllib",
|
||||
"opentelemetry-instrumentation-urllib3[instruments]",
|
||||
"opentelemetry-instrumentation-wsgi",
|
||||
|
||||
4
uv.lock
generated
4
uv.lock
generated
@@ -4134,7 +4134,7 @@ dependencies = [
|
||||
{ name = "opentelemetry-instrumentation-system-metrics" },
|
||||
{ name = "opentelemetry-instrumentation-threading" },
|
||||
{ name = "opentelemetry-instrumentation-tornado" },
|
||||
{ name = "opentelemetry-instrumentation-tortoiseorm" },
|
||||
{ name = "opentelemetry-instrumentation-tortoiseorm", extra = ["instruments"] },
|
||||
{ name = "opentelemetry-instrumentation-urllib" },
|
||||
{ name = "opentelemetry-instrumentation-urllib3", extra = ["instruments"] },
|
||||
{ name = "opentelemetry-instrumentation-vertexai", extra = ["instruments"] },
|
||||
@@ -4208,7 +4208,7 @@ requires-dist = [
|
||||
{ name = "opentelemetry-instrumentation-system-metrics", editable = "instrumentation/opentelemetry-instrumentation-system-metrics" },
|
||||
{ name = "opentelemetry-instrumentation-threading", editable = "instrumentation/opentelemetry-instrumentation-threading" },
|
||||
{ name = "opentelemetry-instrumentation-tornado", editable = "instrumentation/opentelemetry-instrumentation-tornado" },
|
||||
{ name = "opentelemetry-instrumentation-tortoiseorm", editable = "instrumentation/opentelemetry-instrumentation-tortoiseorm" },
|
||||
{ name = "opentelemetry-instrumentation-tortoiseorm", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-tortoiseorm" },
|
||||
{ name = "opentelemetry-instrumentation-urllib", editable = "instrumentation/opentelemetry-instrumentation-urllib" },
|
||||
{ name = "opentelemetry-instrumentation-urllib3", extras = ["instruments"], editable = "instrumentation/opentelemetry-instrumentation-urllib3" },
|
||||
{ name = "opentelemetry-instrumentation-vertexai", extras = ["instruments"], editable = "instrumentation-genai/opentelemetry-instrumentation-vertexai" },
|
||||
|
||||
Reference in New Issue
Block a user