test_integration.py 11 KB

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