Skip to content

Commit 79e8970

Browse files
test(client): Add tests for dropped span client reports
1 parent c34a71e commit 79e8970

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed

tests/test_client.py

+157
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
import sys
55
import time
6+
from collections import Counter, defaultdict
67
from collections.abc import Mapping
78
from textwrap import dedent
89
from unittest import mock
@@ -1214,3 +1215,159 @@ def test_uwsgi_warnings(sentry_init, recwarn, opt, missing_flags):
12141215
assert flag in str(record.message)
12151216
else:
12161217
assert not recwarn
1218+
1219+
1220+
class TestSpanClientReports:
1221+
"""
1222+
Tests for client reports related to spans.
1223+
"""
1224+
1225+
@staticmethod
1226+
def span_dropper(spans_to_drop):
1227+
"""
1228+
Returns a function that can be used to drop spans from an event.
1229+
"""
1230+
1231+
def drop_spans(event, _):
1232+
event["spans"] = event["spans"][spans_to_drop:]
1233+
return event
1234+
1235+
return drop_spans
1236+
1237+
@staticmethod
1238+
def mock_transaction_event(span_count):
1239+
"""
1240+
Returns a mock transaction event with the given number of spans.
1241+
"""
1242+
1243+
return defaultdict(
1244+
mock.MagicMock,
1245+
type="transaction",
1246+
spans=[mock.MagicMock() for _ in range(span_count)],
1247+
)
1248+
1249+
def __init__(self, span_count):
1250+
"""Configures a test case with the number of spans dropped and whether the transaction was dropped."""
1251+
self.span_count = span_count
1252+
self.expected_record_lost_event_calls = Counter()
1253+
self.before_send = lambda event, _: event
1254+
self.event_processor = lambda event, _: event
1255+
1256+
def _update_resulting_calls(self, reason, drops_transactions=0, drops_spans=0):
1257+
"""
1258+
Updates the expected calls with the given resulting calls.
1259+
"""
1260+
if drops_transactions > 0:
1261+
self.expected_record_lost_event_calls[
1262+
(reason, "transaction", None, drops_transactions)
1263+
] += 1
1264+
1265+
if drops_spans > 0:
1266+
self.expected_record_lost_event_calls[
1267+
(reason, "span", None, drops_spans)
1268+
] += 1
1269+
1270+
def with_before_send(
1271+
self,
1272+
before_send,
1273+
*,
1274+
drops_transactions=0,
1275+
drops_spans=0,
1276+
):
1277+
self.before_send = before_send
1278+
self._update_resulting_calls(
1279+
"before_send",
1280+
drops_transactions,
1281+
drops_spans,
1282+
)
1283+
1284+
return self
1285+
1286+
def with_event_processor(
1287+
self,
1288+
event_processor,
1289+
*,
1290+
drops_transactions=0,
1291+
drops_spans=0,
1292+
):
1293+
self.event_processor = event_processor
1294+
self._update_resulting_calls(
1295+
"event_processor",
1296+
drops_transactions,
1297+
drops_spans,
1298+
)
1299+
1300+
return self
1301+
1302+
def run(self, sentry_init, capture_record_lost_event_calls):
1303+
"""Runs the test case with the configured parameters."""
1304+
sentry_init(before_send_transaction=self.before_send)
1305+
record_lost_event_calls = capture_record_lost_event_calls()
1306+
1307+
with sentry_sdk.isolation_scope() as scope:
1308+
scope.add_event_processor(self.event_processor)
1309+
event = self.mock_transaction_event(self.span_count)
1310+
sentry_sdk.get_client().capture_event(event, scope=scope)
1311+
1312+
# We use counters to ensure that the calls are made the expected number of times, disregarding order.
1313+
assert Counter(record_lost_event_calls) == self.expected_record_lost_event_calls
1314+
1315+
1316+
@pytest.mark.parametrize(
1317+
"test_config",
1318+
(
1319+
TestSpanClientReports(span_count=10), # No spans dropped
1320+
TestSpanClientReports(span_count=0).with_before_send(
1321+
lambda e, _: None,
1322+
drops_transactions=1,
1323+
drops_spans=1,
1324+
),
1325+
TestSpanClientReports(span_count=10).with_before_send(
1326+
lambda e, _: None,
1327+
drops_transactions=1,
1328+
drops_spans=11,
1329+
),
1330+
TestSpanClientReports(span_count=10).with_before_send(
1331+
TestSpanClientReports.span_dropper(3),
1332+
drops_spans=3,
1333+
),
1334+
TestSpanClientReports(span_count=10).with_before_send(
1335+
TestSpanClientReports.span_dropper(10),
1336+
drops_spans=10,
1337+
),
1338+
TestSpanClientReports(span_count=10).with_event_processor(
1339+
lambda e, _: None,
1340+
drops_transactions=1,
1341+
drops_spans=11,
1342+
),
1343+
TestSpanClientReports(span_count=10).with_event_processor(
1344+
TestSpanClientReports.span_dropper(3),
1345+
drops_spans=3,
1346+
),
1347+
TestSpanClientReports(span_count=10).with_event_processor(
1348+
TestSpanClientReports.span_dropper(10),
1349+
drops_spans=10,
1350+
),
1351+
TestSpanClientReports(span_count=10)
1352+
.with_event_processor(
1353+
TestSpanClientReports.span_dropper(3),
1354+
drops_spans=3,
1355+
)
1356+
.with_before_send(
1357+
TestSpanClientReports.span_dropper(5),
1358+
drops_spans=5,
1359+
),
1360+
TestSpanClientReports(10)
1361+
.with_event_processor(
1362+
TestSpanClientReports.span_dropper(3),
1363+
drops_spans=3,
1364+
)
1365+
.with_before_send(
1366+
lambda e, _: None,
1367+
drops_transactions=1,
1368+
drops_spans=8, # 3 of the 11 (incl. transaction) spans already dropped
1369+
),
1370+
),
1371+
)
1372+
def test_dropped_transaction(sentry_init, capture_record_lost_event_calls, test_config):
1373+
test_config.run(sentry_init, capture_record_lost_event_calls)

0 commit comments

Comments
 (0)