test_integration.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. from io import BytesIO
  2. from unittest import mock
  3. from uuid import uuid4
  4. import pytest
  5. from sentry.models.eventattachment import EventAttachment
  6. from sentry.tasks.relay import invalidate_project_config
  7. from sentry.testutils.cases import TransactionTestCase
  8. from sentry.testutils.helpers.datetime import before_now, iso_format, timestamp_format
  9. from sentry.testutils.relay import RelayStoreHelper
  10. from sentry.testutils.silo import region_silo_test
  11. from sentry.testutils.skips import requires_kafka
  12. pytestmark = [requires_kafka]
  13. @region_silo_test
  14. class SentryRemoteTest(RelayStoreHelper, TransactionTestCase):
  15. # used to be test_ungzipped_data
  16. def test_simple_data(self):
  17. event_data = {"message": "hello", "timestamp": iso_format(before_now(seconds=1))}
  18. event = self.post_and_retrieve_event(event_data)
  19. assert event.message == "hello"
  20. def test_csp(self):
  21. event_data = {
  22. "csp-report": {
  23. "document-uri": "https://example.com/foo/bar",
  24. "referrer": "https://www.google.com/",
  25. "violated-directive": "default-src self",
  26. "original-policy": "default-src self; report-uri /csp-hotline.php",
  27. "blocked-uri": "http://evilhackerscripts.com",
  28. }
  29. }
  30. event = self.post_and_retrieve_security_report(event_data)
  31. assert event.message == "Blocked 'default-src' from 'evilhackerscripts.com'"
  32. def test_hpkp(self):
  33. event_data = {
  34. "date-time": "2014-04-06T13:00:50Z",
  35. "hostname": "www.example.com",
  36. "port": 443,
  37. "effective-expiration-date": "2014-05-01T12:40:50Z",
  38. "include-subdomains": False,
  39. "served-certificate-chain": [
  40. "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygBQUAMEIxCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----"
  41. ],
  42. "validated-certificate-chain": [
  43. "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygAwIBAgIDCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----"
  44. ],
  45. "known-pins": [
  46. 'pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="',
  47. 'pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="',
  48. ],
  49. }
  50. event = self.post_and_retrieve_security_report(event_data)
  51. assert event.message == "Public key pinning validation failed for 'www.example.com'"
  52. assert event.group.title == "Public key pinning validation failed for 'www.example.com'"
  53. def test_expect_ct(self):
  54. event_data = {
  55. "expect-ct-report": {
  56. "date-time": "2014-04-06T13:00:50Z",
  57. "hostname": "www.example.com",
  58. "port": 443,
  59. "effective-expiration-date": "2014-05-01T12:40:50Z",
  60. "served-certificate-chain": [
  61. "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----"
  62. ],
  63. "validated-certificate-chain": [
  64. "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----"
  65. ],
  66. "scts": [
  67. {
  68. "version": 1,
  69. "status": "invalid",
  70. "source": "embedded",
  71. "serialized_sct": "ABCD==",
  72. }
  73. ],
  74. }
  75. }
  76. event = self.post_and_retrieve_security_report(event_data)
  77. assert event.message == "Expect-CT failed for 'www.example.com'"
  78. assert event.group.title == "Expect-CT failed for 'www.example.com'"
  79. def test_expect_staple(self):
  80. event_data = {
  81. "expect-staple-report": {
  82. "date-time": "2014-04-06T13:00:50Z",
  83. "hostname": "www.example.com",
  84. "port": 443,
  85. "response-status": "ERROR_RESPONSE",
  86. "cert-status": "REVOKED",
  87. "effective-expiration-date": "2014-05-01T12:40:50Z",
  88. "served-certificate-chain": [
  89. "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----"
  90. ],
  91. "validated-certificate-chain": [
  92. "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----"
  93. ],
  94. }
  95. }
  96. event = self.post_and_retrieve_security_report(event_data)
  97. assert event.message == "Expect-Staple failed for 'www.example.com'"
  98. assert event.group.title == "Expect-Staple failed for 'www.example.com'"
  99. def test_standalone_attachment(self):
  100. event_id = uuid4().hex
  101. # First, ingest the attachment and ensure it is saved
  102. files = {"some_file": ("hello.txt", BytesIO(b"Hello World!"))}
  103. self.post_and_retrieve_attachment(event_id, files)
  104. # Next, ingest an error event
  105. event = self.post_and_retrieve_event({"event_id": event_id, "message": "my error"})
  106. assert event.event_id == event_id
  107. assert event.group_id
  108. # Finally, fetch the updated attachment and compare the group id
  109. attachment = EventAttachment.objects.get(project_id=self.project.id, event_id=event_id)
  110. assert attachment.group_id == event.group_id
  111. def test_blob_only_attachment(self):
  112. event_id = uuid4().hex
  113. files = {"some_file": ("hello.txt", BytesIO(b"Hello World! default"))}
  114. self.post_and_retrieve_attachment(event_id, files)
  115. attachments = EventAttachment.objects.filter(project_id=self.project.id)
  116. assert len(attachments) == 1
  117. attachment = EventAttachment.objects.get(event_id=event_id)
  118. with attachment.getfile() as blob:
  119. assert blob.read() == b"Hello World! default"
  120. assert attachment.blob_path is not None
  121. def test_transaction(self):
  122. event_data = {
  123. "event_id": "d2132d31b39445f1938d7e21b6bf0ec4",
  124. "type": "transaction",
  125. "transaction": "/organizations/:orgId/performance/:eventSlug/",
  126. "start_timestamp": iso_format(before_now(minutes=1, milliseconds=500)),
  127. "timestamp": iso_format(before_now(minutes=1)),
  128. "contexts": {
  129. "trace": {
  130. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  131. "span_id": "8f5a2b8768cafb4e",
  132. "type": "trace",
  133. }
  134. },
  135. "spans": [
  136. {
  137. "description": "<OrganizationContext>",
  138. "op": "react.mount",
  139. "parent_span_id": "8f5a2b8768cafb4e",
  140. "span_id": "bd429c44b67a3eb4",
  141. "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=250)),
  142. "timestamp": timestamp_format(before_now(minutes=1)),
  143. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  144. },
  145. {
  146. "description": "browser span",
  147. "op": "browser",
  148. "parent_span_id": "bd429c44b67a3eb4",
  149. "span_id": "a99fd04e79e17631",
  150. "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)),
  151. "timestamp": timestamp_format(before_now(minutes=1)),
  152. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  153. },
  154. {
  155. "description": "resource span",
  156. "op": "resource",
  157. "parent_span_id": "bd429c44b67a3eb4",
  158. "span_id": "a71a5e67db5ce938",
  159. "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)),
  160. "timestamp": timestamp_format(before_now(minutes=1)),
  161. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  162. },
  163. {
  164. "description": "http span",
  165. "op": "http",
  166. "parent_span_id": "a99fd04e79e17631",
  167. "span_id": "abe79ad9292b90a9",
  168. "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)),
  169. "timestamp": timestamp_format(before_now(minutes=1)),
  170. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  171. },
  172. {
  173. "description": "db span",
  174. "op": "db",
  175. "parent_span_id": "abe79ad9292b90a9",
  176. "span_id": "9c045ea336297177",
  177. "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)),
  178. "timestamp": timestamp_format(before_now(minutes=1)),
  179. "trace_id": "ff62a8b040f340bda5d830223def1d81",
  180. },
  181. ],
  182. }
  183. event = self.post_and_retrieve_event(event_data)
  184. raw_event = event.get_raw_data()
  185. assert raw_event["breakdowns"] == {
  186. "span_ops": {
  187. "ops.browser": {"unit": "millisecond", "value": pytest.approx(200, abs=2)},
  188. "ops.resource": {"unit": "millisecond", "value": pytest.approx(200, abs=2)},
  189. "ops.http": {"unit": "millisecond", "value": pytest.approx(200, abs=2)},
  190. "ops.db": {"unit": "millisecond", "value": pytest.approx(200, abs=2)},
  191. "total.time": {"unit": "millisecond", "value": pytest.approx(1050, abs=2)},
  192. }
  193. }
  194. @pytest.mark.skip(
  195. "Fails when test suite is run in region mode, possibly due to cache pollution"
  196. )
  197. # Background: This test used to pass reliably when the undecorated test cases were
  198. # run in monolith mode by default, but began failing during CI for unclear reasons
  199. # when the global default was switched to region mode. (See
  200. # get_default_silo_mode_for_test_cases in sentry/testutils/pytest/sentry.py.) Note
  201. # that this case was marked as @region_silo_test when it was passing, so there was
  202. # no change in the silo mode in which *this* case is run. The probable explanation
  203. # is that the test is sensitive to side effects (possibly in Redis?) of other test
  204. # cases, which *did* have their silo mode changed.
  205. def test_project_config_compression(self):
  206. # Populate redis cache with compressed config:
  207. invalidate_project_config(public_key=self.projectkey, trigger="test")
  208. # Disable project config endpoint, to make sure Relay gets its data
  209. # from redis:
  210. with mock.patch(
  211. "sentry.api.endpoints.relay.project_configs.RelayProjectConfigsEndpoint.post"
  212. ):
  213. event_data = {"message": "hello", "timestamp": iso_format(before_now(seconds=1))}
  214. event = self.post_and_retrieve_event(event_data)
  215. assert event.message == "hello"