test_minidump_full.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import zipfile
  2. from io import BytesIO
  3. from unittest.mock import patch
  4. import pytest
  5. from django.core.files.uploadedfile import SimpleUploadedFile
  6. from django.urls import reverse
  7. from sentry import eventstore
  8. from sentry.lang.native.utils import STORE_CRASH_REPORTS_ALL
  9. from sentry.models import EventAttachment, File
  10. from sentry.testutils import RelayStoreHelper, TransactionTestCase
  11. from sentry.testutils.helpers.task_runner import BurstTaskRunner
  12. from tests.symbolicator import get_fixture_path, insta_snapshot_stacktrace_data
  13. # IMPORTANT:
  14. # For these tests to run, write `symbolicator.enabled: true` into your
  15. # `~/.sentry/config.yml` and run `sentry devservices up`
  16. @pytest.mark.snuba
  17. class SymbolicatorMinidumpIntegrationTest(RelayStoreHelper, TransactionTestCase):
  18. @pytest.fixture(autouse=True)
  19. def initialize(self, live_server, reset_snuba):
  20. self.project.update_option("sentry:builtin_symbol_sources", [])
  21. new_prefix = live_server.url
  22. with patch("sentry.auth.system.is_internal_ip", return_value=True), self.options(
  23. {"system.url-prefix": new_prefix}
  24. ):
  25. # Run test case:
  26. yield
  27. def upload_symbols(self):
  28. url = reverse(
  29. "sentry-api-0-dsym-files",
  30. kwargs={
  31. "organization_slug": self.project.organization.slug,
  32. "project_slug": self.project.slug,
  33. },
  34. )
  35. self.login_as(user=self.user)
  36. out = BytesIO()
  37. f = zipfile.ZipFile(out, "w")
  38. f.write(get_fixture_path("windows.sym"), "crash.sym")
  39. f.close()
  40. response = self.client.post(
  41. url,
  42. {
  43. "file": SimpleUploadedFile(
  44. "symbols.zip", out.getvalue(), content_type="application/zip"
  45. )
  46. },
  47. format="multipart",
  48. )
  49. assert response.status_code == 201, response.content
  50. assert len(response.data) == 1
  51. _FEATURES = {
  52. "organizations:event-attachments": True,
  53. "organizations:symbol-sources": False,
  54. "organizations:custom-symbol-sources": False,
  55. "organizations:images-loaded-v2": False,
  56. }
  57. def test_full_minidump(self):
  58. self.project.update_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_ALL)
  59. self.upload_symbols()
  60. with self.feature(self._FEATURES):
  61. with open(get_fixture_path("windows.dmp"), "rb") as f:
  62. event = self.post_and_retrieve_minidump(
  63. {
  64. "upload_file_minidump": f,
  65. "some_file": ("hello.txt", BytesIO(b"Hello World!")),
  66. },
  67. {"sentry[logger]": "test-logger"},
  68. )
  69. insta_snapshot_stacktrace_data(self, event.data)
  70. assert event.data.get("logger") == "test-logger"
  71. # assert event.data.get("extra") == {"foo": "bar"}
  72. attachments = sorted(
  73. EventAttachment.objects.filter(event_id=event.event_id), key=lambda x: x.name
  74. )
  75. hello, minidump = attachments
  76. assert hello.name == "hello.txt"
  77. hello_file = File.objects.get(id=hello.file_id)
  78. assert hello_file.type == "event.attachment"
  79. assert hello_file.checksum == "2ef7bde608ce5404e97d5f042f95f89f1c232871"
  80. assert minidump.name == "windows.dmp"
  81. minidump_file = File.objects.get(id=minidump.file_id)
  82. assert minidump_file.type == "event.minidump"
  83. assert minidump_file.checksum == "74bb01c850e8d65d3ffbc5bad5cabc4668fce247"
  84. def test_full_minidump_json_extra(self):
  85. self.project.update_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_ALL)
  86. self.upload_symbols()
  87. with self.feature("organizations:event-attachments"):
  88. with open(get_fixture_path("windows.dmp"), "rb") as f:
  89. event = self.post_and_retrieve_minidump(
  90. {"upload_file_minidump": f},
  91. {"sentry": '{"logger":"test-logger"}', "foo": "bar"},
  92. )
  93. assert event.data.get("logger") == "test-logger"
  94. assert event.data.get("extra") == {"foo": "bar"}
  95. # Other assertions are performed by `test_full_minidump`
  96. def test_full_minidump_invalid_extra(self):
  97. self.project.update_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_ALL)
  98. self.upload_symbols()
  99. with self.feature("organizations:event-attachments"):
  100. with open(get_fixture_path("windows.dmp"), "rb") as f:
  101. event = self.post_and_retrieve_minidump(
  102. {"upload_file_minidump": f},
  103. {"sentry": "{{{{", "foo": "bar"}, # invalid sentry JSON
  104. )
  105. assert not event.data.get("logger")
  106. assert event.data.get("extra") == {"foo": "bar"}
  107. # Other assertions are performed by `test_full_minidump`
  108. def test_missing_dsym(self):
  109. with self.feature(self._FEATURES):
  110. with open(get_fixture_path("windows.dmp"), "rb") as f:
  111. event = self.post_and_retrieve_minidump(
  112. {"upload_file_minidump": f}, {"sentry[logger]": "test-logger"}
  113. )
  114. insta_snapshot_stacktrace_data(self, event.data)
  115. assert not EventAttachment.objects.filter(event_id=event.event_id)
  116. def test_reprocessing(self):
  117. self.project.update_option("sentry:store_crash_reports", STORE_CRASH_REPORTS_ALL)
  118. features = dict(self._FEATURES)
  119. features["organizations:reprocessing-v2"] = True
  120. with self.feature(features):
  121. with open(get_fixture_path("windows.dmp"), "rb") as f:
  122. event = self.post_and_retrieve_minidump(
  123. {"upload_file_minidump": f}, {"sentry[logger]": "test-logger"}
  124. )
  125. insta_snapshot_stacktrace_data(self, event.data, subname="initial")
  126. self.upload_symbols()
  127. from sentry.tasks.reprocessing2 import reprocess_group
  128. with BurstTaskRunner() as burst:
  129. reprocess_group.delay(project_id=self.project.id, group_id=event.group_id)
  130. burst(max_jobs=100)
  131. new_event = eventstore.get_event_by_id(self.project.id, event.event_id)
  132. assert new_event is not None
  133. assert new_event.event_id == event.event_id
  134. insta_snapshot_stacktrace_data(self, new_event.data, subname="reprocessed")
  135. for event_id in (event.event_id, new_event.event_id):
  136. (minidump,) = sorted(
  137. EventAttachment.objects.filter(event_id=new_event.event_id), key=lambda x: x.name
  138. )
  139. assert minidump.name == "windows.dmp"
  140. minidump_file = File.objects.get(id=minidump.file_id)
  141. assert minidump_file.type == "event.minidump"
  142. assert minidump_file.checksum == "74bb01c850e8d65d3ffbc5bad5cabc4668fce247"