|
8 | 8 |
|
9 | 9 | from sentry_sdk import capture_message, configure_scope
|
10 | 10 | from sentry_sdk.integrations.sanic import SanicIntegration
|
| 11 | +from sentry_sdk.tracing import TRANSACTION_SOURCE_COMPONENT, TRANSACTION_SOURCE_URL |
11 | 12 |
|
12 | 13 | from sanic import Sanic, request, response, __version__ as SANIC_VERSION_RAW
|
13 | 14 | from sanic.response import HTTPResponse
|
14 | 15 | from sanic.exceptions import SanicException
|
15 | 16 |
|
| 17 | +from sentry_sdk._types import TYPE_CHECKING |
| 18 | + |
| 19 | +if TYPE_CHECKING: |
| 20 | + from collections.abc import Iterable, Container |
| 21 | + from typing import Any, Optional |
| 22 | + |
16 | 23 | SANIC_VERSION = tuple(map(int, SANIC_VERSION_RAW.split(".")))
|
| 24 | +PERFORMANCE_SUPPORTED = SANIC_VERSION >= (21, 9) |
17 | 25 |
|
18 | 26 |
|
19 | 27 | @pytest.fixture
|
@@ -49,6 +57,10 @@ def hi_with_id(request, message_id):
|
49 | 57 | capture_message("hi with id")
|
50 | 58 | return response.text("ok with id")
|
51 | 59 |
|
| 60 | + @app.route("/500") |
| 61 | + def fivehundred(_): |
| 62 | + 1 / 0 |
| 63 | + |
52 | 64 | return app
|
53 | 65 |
|
54 | 66 |
|
@@ -88,7 +100,7 @@ def test_request_data(sentry_init, app, capture_events):
|
88 | 100 | ("/message/123456", "hi_with_id", "component"),
|
89 | 101 | ],
|
90 | 102 | )
|
91 |
| -def test_transaction( |
| 103 | +def test_transaction_name( |
92 | 104 | sentry_init, app, capture_events, url, expected_transaction, expected_source
|
93 | 105 | ):
|
94 | 106 | sentry_init(integrations=[SanicIntegration()])
|
@@ -284,3 +296,114 @@ async def runner():
|
284 | 296 |
|
285 | 297 | with configure_scope() as scope:
|
286 | 298 | assert not scope._tags
|
| 299 | + |
| 300 | + |
| 301 | +class TransactionTestConfig: |
| 302 | + """ |
| 303 | + Data class to store configurations for each performance transaction test run, including |
| 304 | + both the inputs and relevant expected results. |
| 305 | + """ |
| 306 | + |
| 307 | + def __init__( |
| 308 | + self, |
| 309 | + integration_args, |
| 310 | + url, |
| 311 | + expected_status, |
| 312 | + expected_transaction_name, |
| 313 | + expected_source=None, |
| 314 | + ): |
| 315 | + # type: (Iterable[Optional[Container[int]]], str, int, Optional[str], Optional[str]) -> None |
| 316 | + """ |
| 317 | + expected_transaction_name of None indicates we expect to not receive a transaction |
| 318 | + """ |
| 319 | + self.integration_args = integration_args |
| 320 | + self.url = url |
| 321 | + self.expected_status = expected_status |
| 322 | + self.expected_transaction_name = expected_transaction_name |
| 323 | + self.expected_source = expected_source |
| 324 | + |
| 325 | + |
| 326 | +@pytest.mark.skipif( |
| 327 | + not PERFORMANCE_SUPPORTED, reason="Performance not supported on this Sanic version" |
| 328 | +) |
| 329 | +@pytest.mark.parametrize( |
| 330 | + "test_config", |
| 331 | + [ |
| 332 | + TransactionTestConfig( |
| 333 | + # Transaction for successful page load |
| 334 | + integration_args=(), |
| 335 | + url="/message", |
| 336 | + expected_status=200, |
| 337 | + expected_transaction_name="hi", |
| 338 | + expected_source=TRANSACTION_SOURCE_COMPONENT, |
| 339 | + ), |
| 340 | + TransactionTestConfig( |
| 341 | + # Transaction still recorded when we have an internal server error |
| 342 | + integration_args=(), |
| 343 | + url="/500", |
| 344 | + expected_status=500, |
| 345 | + expected_transaction_name="fivehundred", |
| 346 | + expected_source=TRANSACTION_SOURCE_COMPONENT, |
| 347 | + ), |
| 348 | + TransactionTestConfig( |
| 349 | + # By default, no transaction when we have a 404 error |
| 350 | + integration_args=(), |
| 351 | + url="/404", |
| 352 | + expected_status=404, |
| 353 | + expected_transaction_name=None, |
| 354 | + ), |
| 355 | + TransactionTestConfig( |
| 356 | + # With no ignored HTTP statuses, we should get transactions for 404 errors |
| 357 | + integration_args=(None,), |
| 358 | + url="/404", |
| 359 | + expected_status=404, |
| 360 | + expected_transaction_name="/404", |
| 361 | + expected_source=TRANSACTION_SOURCE_URL, |
| 362 | + ), |
| 363 | + TransactionTestConfig( |
| 364 | + # Transaction can be suppressed for other HTTP statuses, too, by passing config to the integration |
| 365 | + integration_args=({200},), |
| 366 | + url="/message", |
| 367 | + expected_status=200, |
| 368 | + expected_transaction_name=None, |
| 369 | + ), |
| 370 | + ], |
| 371 | +) |
| 372 | +def test_transactions(test_config, sentry_init, app, capture_events): |
| 373 | + # type: (TransactionTestConfig, Any, Any, Any) -> None |
| 374 | + |
| 375 | + # Init the SanicIntegration with the desired arguments |
| 376 | + sentry_init( |
| 377 | + integrations=[SanicIntegration(*test_config.integration_args)], |
| 378 | + traces_sample_rate=1.0, |
| 379 | + ) |
| 380 | + events = capture_events() |
| 381 | + |
| 382 | + # Make request to the desired URL |
| 383 | + _, response = app.test_client.get(test_config.url) |
| 384 | + assert response.status == test_config.expected_status |
| 385 | + |
| 386 | + # Extract the transaction events by inspecting the event types. We should at most have 1 transaction event. |
| 387 | + transaction_events = [ |
| 388 | + e for e in events if "type" in e and e["type"] == "transaction" |
| 389 | + ] |
| 390 | + assert len(transaction_events) <= 1 |
| 391 | + |
| 392 | + # Get the only transaction event, or set to None if there are no transaction events. |
| 393 | + (transaction_event, *_) = [*transaction_events, None] |
| 394 | + |
| 395 | + # We should have no transaction event if and only if we expect no transactions |
| 396 | + assert (transaction_event is None) == ( |
| 397 | + test_config.expected_transaction_name is None |
| 398 | + ) |
| 399 | + |
| 400 | + # If a transaction was expected, ensure it is correct |
| 401 | + assert ( |
| 402 | + transaction_event is None |
| 403 | + or transaction_event["transaction"] == test_config.expected_transaction_name |
| 404 | + ) |
| 405 | + assert ( |
| 406 | + transaction_event is None |
| 407 | + or transaction_event["transaction_info"]["source"] |
| 408 | + == test_config.expected_source |
| 409 | + ) |
0 commit comments