|
2 | 2 |
|
3 | 3 | import os
|
4 | 4 | import pytest
|
| 5 | +from datetime import datetime |
5 | 6 |
|
6 | 7 | from django import VERSION as DJANGO_VERSION
|
7 | 8 | from django.db import connections
|
|
15 | 16 |
|
16 | 17 | from sentry_sdk.consts import SPANDATA
|
17 | 18 | from sentry_sdk.integrations.django import DjangoIntegration
|
| 19 | +from sentry_sdk.tracing_utils import record_sql_queries |
18 | 20 |
|
19 | 21 | from tests.conftest import unpack_werkzeug_response
|
20 | 22 | from tests.integrations.django.utils import pytest_mark_django_db_decorator
|
21 | 23 | from tests.integrations.django.myapp.wsgi import application
|
22 | 24 |
|
| 25 | +try: |
| 26 | + from unittest import mock |
| 27 | +except ImportError: |
| 28 | + import mock |
| 29 | + |
23 | 30 |
|
24 | 31 | @pytest.fixture
|
25 | 32 | def client():
|
@@ -228,3 +235,134 @@ def test_query_source_with_in_app_include(sentry_init, client, capture_events):
|
228 | 235 | break
|
229 | 236 | else:
|
230 | 237 | raise AssertionError("No db span found")
|
| 238 | + |
| 239 | + |
| 240 | +@pytest.mark.forked |
| 241 | +@pytest_mark_django_db_decorator(transaction=True) |
| 242 | +def test_no_query_source_if_duration_too_short(sentry_init, client, capture_events): |
| 243 | + sentry_init( |
| 244 | + integrations=[DjangoIntegration()], |
| 245 | + send_default_pii=True, |
| 246 | + traces_sample_rate=1.0, |
| 247 | + enable_db_query_source=True, |
| 248 | + db_query_source_threshold_ms=100, |
| 249 | + ) |
| 250 | + |
| 251 | + if "postgres" not in connections: |
| 252 | + pytest.skip("postgres tests disabled") |
| 253 | + |
| 254 | + # trigger Django to open a new connection by marking the existing one as None. |
| 255 | + connections["postgres"].connection = None |
| 256 | + |
| 257 | + events = capture_events() |
| 258 | + |
| 259 | + class fake_record_sql_queries: # noqa: N801 |
| 260 | + def __init__(self, *args, **kwargs): |
| 261 | + with record_sql_queries(*args, **kwargs) as span: |
| 262 | + self.span = span |
| 263 | + |
| 264 | + self.span.start_timestamp = datetime(2024, 1, 1, microsecond=0) |
| 265 | + self.span.timestamp = datetime(2024, 1, 1, microsecond=99999) |
| 266 | + |
| 267 | + def __enter__(self): |
| 268 | + return self.span |
| 269 | + |
| 270 | + def __exit__(self, type, value, traceback): |
| 271 | + pass |
| 272 | + |
| 273 | + with mock.patch( |
| 274 | + "sentry_sdk.integrations.django.record_sql_queries", |
| 275 | + fake_record_sql_queries, |
| 276 | + ): |
| 277 | + _, status, _ = unpack_werkzeug_response( |
| 278 | + client.get(reverse("postgres_select_orm")) |
| 279 | + ) |
| 280 | + |
| 281 | + assert status == "200 OK" |
| 282 | + |
| 283 | + (event,) = events |
| 284 | + for span in event["spans"]: |
| 285 | + if span.get("op") == "db" and "auth_user" in span.get("description"): |
| 286 | + data = span.get("data", {}) |
| 287 | + |
| 288 | + assert SPANDATA.CODE_LINENO not in data |
| 289 | + assert SPANDATA.CODE_NAMESPACE not in data |
| 290 | + assert SPANDATA.CODE_FILEPATH not in data |
| 291 | + assert SPANDATA.CODE_FUNCTION not in data |
| 292 | + |
| 293 | + break |
| 294 | + else: |
| 295 | + raise AssertionError("No db span found") |
| 296 | + |
| 297 | + |
| 298 | +@pytest.mark.forked |
| 299 | +@pytest_mark_django_db_decorator(transaction=True) |
| 300 | +def test_query_source_if_duration_over_threshold(sentry_init, client, capture_events): |
| 301 | + sentry_init( |
| 302 | + integrations=[DjangoIntegration()], |
| 303 | + send_default_pii=True, |
| 304 | + traces_sample_rate=1.0, |
| 305 | + enable_db_query_source=True, |
| 306 | + db_query_source_threshold_ms=100, |
| 307 | + ) |
| 308 | + |
| 309 | + if "postgres" not in connections: |
| 310 | + pytest.skip("postgres tests disabled") |
| 311 | + |
| 312 | + # trigger Django to open a new connection by marking the existing one as None. |
| 313 | + connections["postgres"].connection = None |
| 314 | + |
| 315 | + events = capture_events() |
| 316 | + |
| 317 | + class fake_record_sql_queries: # noqa: N801 |
| 318 | + def __init__(self, *args, **kwargs): |
| 319 | + with record_sql_queries(*args, **kwargs) as span: |
| 320 | + self.span = span |
| 321 | + |
| 322 | + self.span.start_timestamp = datetime(2024, 1, 1, microsecond=0) |
| 323 | + self.span.timestamp = datetime(2024, 1, 1, microsecond=101000) |
| 324 | + |
| 325 | + def __enter__(self): |
| 326 | + return self.span |
| 327 | + |
| 328 | + def __exit__(self, type, value, traceback): |
| 329 | + pass |
| 330 | + |
| 331 | + with mock.patch( |
| 332 | + "sentry_sdk.integrations.django.record_sql_queries", |
| 333 | + fake_record_sql_queries, |
| 334 | + ): |
| 335 | + _, status, _ = unpack_werkzeug_response( |
| 336 | + client.get(reverse("postgres_select_orm")) |
| 337 | + ) |
| 338 | + |
| 339 | + assert status == "200 OK" |
| 340 | + |
| 341 | + (event,) = events |
| 342 | + for span in event["spans"]: |
| 343 | + if span.get("op") == "db" and "auth_user" in span.get("description"): |
| 344 | + data = span.get("data", {}) |
| 345 | + |
| 346 | + assert SPANDATA.CODE_LINENO in data |
| 347 | + assert SPANDATA.CODE_NAMESPACE in data |
| 348 | + assert SPANDATA.CODE_FILEPATH in data |
| 349 | + assert SPANDATA.CODE_FUNCTION in data |
| 350 | + |
| 351 | + assert type(data.get(SPANDATA.CODE_LINENO)) == int |
| 352 | + assert data.get(SPANDATA.CODE_LINENO) > 0 |
| 353 | + |
| 354 | + assert ( |
| 355 | + data.get(SPANDATA.CODE_NAMESPACE) |
| 356 | + == "tests.integrations.django.myapp.views" |
| 357 | + ) |
| 358 | + assert data.get(SPANDATA.CODE_FILEPATH).endswith( |
| 359 | + "tests/integrations/django/myapp/views.py" |
| 360 | + ) |
| 361 | + |
| 362 | + is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep |
| 363 | + assert is_relative_path |
| 364 | + |
| 365 | + assert data.get(SPANDATA.CODE_FUNCTION) == "postgres_select_orm" |
| 366 | + break |
| 367 | + else: |
| 368 | + raise AssertionError("No db span found") |
0 commit comments