Skip to content

Commit 36aa1c2

Browse files
committed
feature: move settings import and update tests
1 parent 4b5837b commit 36aa1c2

2 files changed

Lines changed: 84 additions & 20 deletions

File tree

api/tests/open_telemetry_instrumentation_tests.py

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import signal
23
from unittest.mock import patch, MagicMock
34

@@ -79,9 +80,10 @@ def test_cf_ray_header(self):
7980

8081
# Exported spans should exist
8182
spans = memory_exporter.get_finished_spans()
82-
self.assertEqual(spans[0].resource.attributes.get('service.name'), 'marketing-api')
83-
self.assertEqual(len(spans), 6)
84-
exported_span = spans[5]
83+
# Find the top-level Django HTTP span by name pattern
84+
http_spans = [s for s in spans if s.name.startswith("GET ")]
85+
self.assertEqual(len(http_spans), 1)
86+
exported_span = http_spans[0]
8587
# Since no traceparent was injected, parent should be INVALID
8688
self.assertEqual(exported_span.parent, None)
8789
# Our CF-RAY header should be recorded in span attributes
@@ -111,9 +113,10 @@ def test_traceparent_and_baggage(self):
111113

112114
# Verify a span was exported
113115
spans = memory_exporter.get_finished_spans()
114-
self.assertEqual(len(spans), 6)
116+
http_spans = [s for s in spans if s.name.startswith("GET ")]
117+
self.assertEqual(len(http_spans), 1)
118+
exported_span = http_spans[0]
115119
self.assertEqual(spans[0].resource.attributes.get('service.name'), 'marketing-api')
116-
exported_span = spans[5]
117120
# Check that the trace_id is the same as the injected traceparent
118121
self.assertEqual(format(exported_span.context.trace_id, "032x"), trace_id)
119122

@@ -126,31 +129,92 @@ def test_traceparent_and_baggage(self):
126129
# And should also show up in span attributes (if your request_hook adds it)
127130
self.assertEqual(exported_span.attributes.get("baggage.cf.ray_id"), "xyz")
128131

132+
@patch.dict(os.environ, {'DB_NAME': 'my_app_db'})
129133
def test_mysql_span_has_db_name(self):
130-
"""Simulate a MySQL query and assert db.name attribute is added."""
134+
"""mysql_hook sets db.system, db.name (env default), and db.statement on the span."""
131135
with tracer.start_as_current_span("mysql-test") as span:
132-
span.set_attribute("db.name", "test_db")
133-
span.set_attribute("db.statement", "SELECT 1")
136+
DjangoTelemetry.mysql_hook(span, MagicMock(), MagicMock(), "SELECT 1", ())
134137

135138
spans = memory_exporter.get_finished_spans()
136-
self.assertEqual(len(spans), 1)
137-
exported = spans[0]
139+
mysql_spans = [s for s in spans if s.name == "mysql-test"]
140+
self.assertEqual(len(mysql_spans), 1)
141+
exported = mysql_spans[0]
138142
self.assertEqual(exported.resource.attributes.get('service.name'), 'marketing-api')
139-
self.assertEqual(exported.attributes.get("db.name"), "test_db")
140-
self.assertIn("SELECT", exported.attributes.get("db.statement"))
143+
self.assertEqual(exported.attributes.get("db.system"), "mysql")
144+
self.assertEqual(exported.attributes.get("db.name"), "my_app_db")
145+
self.assertEqual(exported.attributes.get("db.statement"), "SELECT 1")
146+
147+
def test_mysql_hook_skips_non_recording_span(self):
148+
"""mysql_hook sets no attributes when span.is_recording() is False."""
149+
span = MagicMock()
150+
span.is_recording.return_value = False
151+
152+
DjangoTelemetry.mysql_hook(span, MagicMock(), MagicMock(), "SELECT 1", ())
153+
154+
span.set_attribute.assert_not_called()
155+
156+
def test_mysql_hook_swallows_exceptions(self):
157+
"""mysql_hook does not propagate exceptions raised by set_attribute."""
158+
span = MagicMock()
159+
span.is_recording.return_value = True
160+
span.set_attribute.side_effect = RuntimeError("boom")
161+
162+
exception_raised = False
163+
try:
164+
DjangoTelemetry.mysql_hook(span, MagicMock(), MagicMock(), "SELECT 1", ())
165+
except RuntimeError:
166+
exception_raised = True
167+
168+
self.assertFalse(exception_raised, "mysql_hook should catch and not re-raise")
169+
self.assertTrue(span.set_attribute.called, "mysql_hook should have attempted to set attributes")
141170

142171
def test_redis_span_has_key(self):
143-
"""Simulate a Redis command and assert db.redis.key is added."""
172+
"""redis_hook sets db.system, redis.command, and redis.key on the span."""
144173
with tracer.start_as_current_span("redis-test") as span:
145-
span.set_attribute("db.redis.command", "GET")
146-
span.set_attribute("db.redis.key", "my_key")
174+
DjangoTelemetry.redis_hook(span, MagicMock(), ("GET", "my_key"), {})
147175

148176
spans = memory_exporter.get_finished_spans()
149-
self.assertEqual(len(spans), 1)
150-
exported = spans[0]
177+
redis_spans = [s for s in spans if s.name.startswith("redis-test")]
178+
179+
self.assertEqual(len(redis_spans), 1)
180+
exported = redis_spans[0]
151181
self.assertEqual(exported.resource.attributes.get('service.name'), 'marketing-api')
152-
self.assertEqual(exported.attributes.get("db.redis.command"), "GET")
153-
self.assertEqual(exported.attributes.get("db.redis.key"), "my_key")
182+
self.assertEqual(exported.attributes.get("db.system"), "redis")
183+
self.assertEqual(exported.attributes.get("redis.command"), "GET")
184+
self.assertEqual(exported.attributes.get("redis.key"), "my_key")
185+
186+
def test_redis_hook_no_key_in_args(self):
187+
"""redis_hook does not set redis.key when args contains only the command."""
188+
with tracer.start_as_current_span("redis-nokey-test") as span:
189+
DjangoTelemetry.redis_hook(span, MagicMock(), ("DEL",), {})
190+
191+
exported = memory_exporter.get_finished_spans()[0]
192+
self.assertEqual(exported.attributes.get("redis.command"), "DEL")
193+
self.assertIsNone(exported.attributes.get("redis.key"))
194+
195+
def test_redis_hook_skips_non_recording_span(self):
196+
"""redis_hook sets no attributes when span.is_recording() is False."""
197+
span = MagicMock()
198+
span.is_recording.return_value = False
199+
200+
DjangoTelemetry.redis_hook(span, MagicMock(), ("GET", "key"), {})
201+
202+
span.set_attribute.assert_not_called()
203+
204+
def test_redis_hook_swallows_exceptions(self):
205+
"""redis_hook does not propagate exceptions raised by set_attribute."""
206+
span = MagicMock()
207+
span.is_recording.return_value = True
208+
span.set_attribute.side_effect = RuntimeError("boom")
209+
210+
exception_raised = False
211+
try:
212+
DjangoTelemetry.redis_hook(span, MagicMock(), ("GET", "key"), {})
213+
except RuntimeError:
214+
exception_raised = True
215+
216+
self.assertFalse(exception_raised, "redis_hook should catch and not re-raise")
217+
self.assertTrue(span.set_attribute.called, "redis_hook should have attempted to set attributes")
154218

155219
def test_requests_span_has_custom_header(self):
156220
"""Simulate a requests span and assert custom header is captured."""

backend/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.utils.translation import gettext_lazy as _
1515
from dotenv import load_dotenv
1616
import sys
17+
from backend.env_var_eval import env_bool
1718

1819

1920
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@@ -364,7 +365,6 @@
364365

365366
DEV_EMAIL = os.getenv('DEV_EMAIL')
366367

367-
from backend.env_var_eval import env_bool
368368
OTEL_INSTRUMENTATION_ENABLED = env_bool('OTEL_INSTRUMENTATION_ENABLED', True)
369369

370370
if OTEL_INSTRUMENTATION_ENABLED:

0 commit comments

Comments
 (0)