|
7 | 7 | from unittest import mock |
8 | 8 | from unittest.mock import patch |
9 | 9 |
|
| 10 | +import msgpack |
10 | 11 | import pytest |
11 | 12 | from django.core.files.uploadedfile import SimpleUploadedFile |
12 | 13 | from django.urls import reverse |
|
32 | 33 | ) |
33 | 34 | from sentry.profiles.utils import Profile |
34 | 35 | from sentry.signals import first_profile_received |
35 | | -from sentry.testutils.cases import TransactionTestCase |
| 36 | +from sentry.testutils.cases import TestCase, TransactionTestCase |
36 | 37 | from sentry.testutils.factories import Factories, get_fixture_path |
37 | 38 | from sentry.testutils.helpers import Feature, override_options |
38 | 39 | from sentry.testutils.pytest.fixtures import django_db_all |
@@ -1171,3 +1172,103 @@ def test_process_profile_task_should_flip_project_flag( |
1171 | 1172 | ) |
1172 | 1173 | project.refresh_from_db() |
1173 | 1174 | assert project.flags.has_profiles |
| 1175 | + |
| 1176 | + |
| 1177 | +class TestProcessProfileTaskDoubleCompression(TestCase): |
| 1178 | + """ |
| 1179 | + TODO(taskworker): Remove this test once we have deleted zlib compression. |
| 1180 | + Test class for validating the double compression flow: |
| 1181 | + 1. Consumer does zlib compression and calls process_profile_task.delay() |
| 1182 | + 2. Taskworker does zstd compression on the task parameters |
| 1183 | + 3. Task worker decompresses zstd and task decompresses zlib |
| 1184 | + """ |
| 1185 | + |
| 1186 | + @patch("sentry.profiles.task._track_outcome") |
| 1187 | + @patch("sentry.profiles.task._track_duration_outcome") |
| 1188 | + @patch("sentry.profiles.task._symbolicate_profile") |
| 1189 | + @patch("sentry.profiles.task._deobfuscate_profile") |
| 1190 | + @patch("sentry.profiles.task._push_profile_to_vroom") |
| 1191 | + def test_consumer_to_task_double_compression_flow( |
| 1192 | + self, |
| 1193 | + _push_profile_to_vroom, |
| 1194 | + _deobfuscate_profile, |
| 1195 | + _symbolicate_profile, |
| 1196 | + _track_duration_outcome, |
| 1197 | + _track_outcome, |
| 1198 | + ): |
| 1199 | + """ |
| 1200 | + Test that the full consumer -> task flow works with double compression. |
| 1201 | +
|
| 1202 | + This test validates: |
| 1203 | + 1. process_message in factory.py does zlib compression |
| 1204 | + 2. taskworker layer does zstd compression |
| 1205 | + 3. Both decompressions work correctly in the task execution |
| 1206 | + """ |
| 1207 | + from datetime import datetime |
| 1208 | + |
| 1209 | + from arroyo.backends.kafka import KafkaPayload |
| 1210 | + from arroyo.types import BrokerValue, Message, Partition, Topic |
| 1211 | + from django.utils import timezone |
| 1212 | + |
| 1213 | + from sentry.profiles.consumers.process.factory import ProcessProfileStrategyFactory |
| 1214 | + |
| 1215 | + # Mock the task functions |
| 1216 | + _push_profile_to_vroom.return_value = True |
| 1217 | + _deobfuscate_profile.return_value = True |
| 1218 | + _symbolicate_profile.return_value = True |
| 1219 | + |
| 1220 | + # Get the profile fixture data |
| 1221 | + profile = generate_sample_v2_profile() |
| 1222 | + |
| 1223 | + # Create a message dict like the consumer would receive from Kafka |
| 1224 | + message_dict = { |
| 1225 | + "organization_id": self.organization.id, |
| 1226 | + "project_id": self.project.id, |
| 1227 | + "key_id": 1, |
| 1228 | + "received": int(timezone.now().timestamp()), |
| 1229 | + "payload": json.dumps(profile), |
| 1230 | + } |
| 1231 | + |
| 1232 | + # Pack the message with msgpack (like the consumer receives from Kafka) |
| 1233 | + payload = msgpack.packb(message_dict) |
| 1234 | + |
| 1235 | + # Create the processing strategy (this will call process_message) |
| 1236 | + processing_strategy = ProcessProfileStrategyFactory().create_with_partitions( |
| 1237 | + commit=mock.Mock(), partitions={} |
| 1238 | + ) |
| 1239 | + |
| 1240 | + # Use self.tasks() to run the actual task with both compression layers |
| 1241 | + with self.tasks(): |
| 1242 | + # Submit the message to the processing strategy |
| 1243 | + # This calls process_message which does: |
| 1244 | + # 1. zlib compression of the msgpack data |
| 1245 | + # 2. process_profile_task.delay() which adds zstd compression |
| 1246 | + processing_strategy.submit( |
| 1247 | + Message( |
| 1248 | + BrokerValue( |
| 1249 | + KafkaPayload( |
| 1250 | + b"key", |
| 1251 | + payload, |
| 1252 | + [], |
| 1253 | + ), |
| 1254 | + Partition(Topic("profiles"), 1), |
| 1255 | + 1, |
| 1256 | + datetime.now(), |
| 1257 | + ) |
| 1258 | + ) |
| 1259 | + ) |
| 1260 | + processing_strategy.poll() |
| 1261 | + processing_strategy.join(1) |
| 1262 | + processing_strategy.terminate() |
| 1263 | + |
| 1264 | + # Verify the task was executed successfully |
| 1265 | + assert _push_profile_to_vroom.call_count == 1 |
| 1266 | + assert _deobfuscate_profile.call_count == 1 |
| 1267 | + assert _symbolicate_profile.call_count == 1 |
| 1268 | + assert _track_duration_outcome.call_count == 1 |
| 1269 | + |
| 1270 | + # Verify the profile was processed with correct data |
| 1271 | + processed_profile = _push_profile_to_vroom.call_args[0][0] |
| 1272 | + assert processed_profile["organization_id"] == self.organization.id |
| 1273 | + assert processed_profile["project_id"] == self.project.id |
| 1274 | + assert processed_profile["platform"] == profile["platform"] |
0 commit comments