Skip to content

Commit 0b379be

Browse files
herin049xrmx
andauthored
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>
1 parent c1fe016 commit 0b379be

6 files changed

Lines changed: 126 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4949
([#4140](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4140))
5050
- `opentelemetry-instrumentation-pyramid` Implement new semantic convention opt-in migration
5151
([#3982](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3982))
52+
- `opentelemetry-instrumentation-tortoiseorm` Add unit tests for Tortoise ORM instrumentation
53+
([#4141](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4141))
5254
- `opentelemetry-instrumentation-pyramid`: pass request attributes at span creation
5355
([#4139](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4139))
5456

instrumentation/opentelemetry-instrumentation-tortoiseorm/src/opentelemetry/instrumentation/tortoiseorm/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@
4848
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
4949
from opentelemetry.instrumentation.tortoiseorm.package import _instruments
5050
from opentelemetry.instrumentation.tortoiseorm.version import __version__
51-
from opentelemetry.instrumentation.utils import unwrap
51+
from opentelemetry.instrumentation.utils import (
52+
is_instrumentation_enabled,
53+
unwrap,
54+
)
5255
from opentelemetry.semconv._incubating.attributes.db_attributes import (
5356
DB_NAME,
5457
DB_STATEMENT,
@@ -270,6 +273,9 @@ def _hydrate_span_from_args(self, connection, query, parameters) -> dict:
270273
return span_attributes
271274

272275
async def _do_execute(self, func, instance, args, kwargs):
276+
if not is_instrumentation_enabled():
277+
return await func(*args, **kwargs)
278+
273279
exception = None
274280
name = args[0].split()[0]
275281

@@ -297,6 +303,9 @@ async def _do_execute(self, func, instance, args, kwargs):
297303
return result
298304

299305
async def _from_queryset(self, func, modelcls, args, kwargs):
306+
if not is_instrumentation_enabled():
307+
return await func(*args, **kwargs)
308+
300309
exception = None
301310
name = f"pydantic.{func.__name__}"
302311

instrumentation/opentelemetry-instrumentation-tortoiseorm/test-requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ iso8601==1.1.0
77
packaging==24.0
88
pluggy==1.5.0
99
py-cpuinfo==9.0.0
10-
pydantic==2.8.2
11-
pydantic_core==2.20.1
10+
pydantic==2.12.5
11+
pydantic_core==2.41.5
1212
pypika-tortoise==0.1.6
1313
pytest==7.4.4
1414
pytz==2024.1
1515
tomli==2.0.1
1616
tortoise-orm==0.20.0
17-
typing_extensions==4.12.2
17+
typing_extensions==4.14.1
1818
wrapt==1.16.0
1919
zipp==3.19.2
2020
-e opentelemetry-instrumentation

instrumentation/opentelemetry-instrumentation-tortoiseorm/tests/test_tortoiseorm_instrumentation.py

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,27 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import asyncio
16+
17+
from tortoise import Tortoise, fields, models
18+
19+
from opentelemetry import trace
1520
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
21+
from opentelemetry.instrumentation.utils import suppress_instrumentation
22+
from opentelemetry.semconv._incubating.attributes.db_attributes import (
23+
DB_NAME,
24+
DB_STATEMENT,
25+
DB_SYSTEM,
26+
)
1627
from opentelemetry.test.test_base import TestBase
1728

18-
# pylint: disable=too-many-public-methods
29+
30+
class MockModel(models.Model):
31+
id = fields.IntField(pk=True)
32+
name = fields.TextField()
33+
34+
def __str__(self):
35+
return self.name
1936

2037

2138
class TestTortoiseORMInstrumentor(TestBase):
@@ -26,9 +43,94 @@ def setUp(self):
2643
def tearDown(self):
2744
super().tearDown()
2845
TortoiseORMInstrumentor().uninstrument()
46+
self._async_call(Tortoise._drop_databases())
47+
48+
# pylint: disable-next=no-self-use
49+
def _async_call(self, coro):
50+
return asyncio.run(coro)
51+
52+
# pylint: disable-next=no-self-use
53+
async def _init_tortoise(self):
54+
await Tortoise.init(
55+
db_url="sqlite://:memory:",
56+
modules={"models": [__name__]},
57+
)
58+
await Tortoise.generate_schemas()
59+
60+
def test_trace_integration(self):
61+
async def run():
62+
await self._init_tortoise()
63+
await MockModel.create(name="Test 1")
64+
await MockModel.filter(name="Test 1").first()
65+
66+
self._async_call(run())
67+
spans = self.memory_exporter.get_finished_spans()
68+
69+
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
70+
self.assertGreaterEqual(len(crud_spans), 2)
71+
72+
insert_span = next(s for s in crud_spans if s.name == "INSERT")
73+
self.assertEqual(insert_span.kind, trace.SpanKind.CLIENT)
74+
self.assertEqual(insert_span.attributes[DB_SYSTEM], "sqlite")
75+
self.assertEqual(insert_span.attributes[DB_NAME], ":memory:")
76+
self.assertIn("INSERT", insert_span.attributes[DB_STATEMENT])
77+
78+
select_span = next(s for s in crud_spans if s.name == "SELECT")
79+
self.assertEqual(select_span.kind, trace.SpanKind.CLIENT)
80+
self.assertEqual(select_span.attributes[DB_SYSTEM], "sqlite")
81+
self.assertEqual(select_span.attributes[DB_NAME], ":memory:")
82+
self.assertIn("SELECT", select_span.attributes[DB_STATEMENT])
83+
84+
def test_capture_parameters(self):
85+
TortoiseORMInstrumentor().uninstrument()
86+
TortoiseORMInstrumentor().instrument(capture_parameters=True)
87+
88+
async def run():
89+
await self._init_tortoise()
90+
await MockModel.create(name="Test Parameterized")
91+
92+
self._async_call(run())
93+
spans = self.memory_exporter.get_finished_spans()
94+
insert_span = next(s for s in spans if s.name == "INSERT")
95+
self.assertIn("db.statement.parameters", insert_span.attributes)
96+
self.assertIn(
97+
"Test Parameterized",
98+
insert_span.attributes["db.statement.parameters"],
99+
)
100+
101+
def test_uninstrument(self):
102+
TortoiseORMInstrumentor().uninstrument()
103+
104+
async def run():
105+
await self._init_tortoise()
106+
await MockModel.create(name="Test Uninstrumented")
107+
108+
self._async_call(run())
109+
spans = self.memory_exporter.get_finished_spans()
110+
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
111+
self.assertEqual(len(crud_spans), 0)
112+
113+
def test_suppress_instrumentation(self):
114+
async def run():
115+
await self._init_tortoise()
116+
with suppress_instrumentation():
117+
await MockModel.create(name="Test Suppressed")
118+
119+
self._async_call(run())
120+
spans = self.memory_exporter.get_finished_spans()
121+
crud_spans = [s for s in spans if s.name in ("INSERT", "SELECT")]
122+
self.assertEqual(len(crud_spans), 0)
123+
124+
def test_no_op_tracer_provider(self):
125+
TortoiseORMInstrumentor().uninstrument()
126+
TortoiseORMInstrumentor().instrument(
127+
tracer_provider=trace.NoOpTracerProvider()
128+
)
129+
130+
async def run():
131+
await self._init_tortoise()
132+
await MockModel.create(name="Test NoOp")
29133

30-
def test_tortoise(self):
31-
# FIXME This instrumentation has no tests at all and should have some
32-
# tests. This is being added just to make pytest not fail because no
33-
# tests are being collected for tortoise at the moment.
34-
pass
134+
self._async_call(run())
135+
spans = self.memory_exporter.get_finished_spans()
136+
self.assertEqual(len(spans), 0)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ dependencies = [
5757
"opentelemetry-instrumentation-system-metrics",
5858
"opentelemetry-instrumentation-threading",
5959
"opentelemetry-instrumentation-tornado",
60-
"opentelemetry-instrumentation-tortoiseorm",
60+
"opentelemetry-instrumentation-tortoiseorm[instruments]",
6161
"opentelemetry-instrumentation-urllib",
6262
"opentelemetry-instrumentation-urllib3[instruments]",
6363
"opentelemetry-instrumentation-wsgi",

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)