test_process_issue_event.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. import os
  2. import shutil
  3. import uuid
  4. from django.urls import reverse
  5. from model_bakery import baker
  6. from apps.issue_events.constants import EventStatus, LogLevel
  7. from apps.issue_events.models import Issue, IssueEvent, IssueHash
  8. from apps.projects.models import IssueEventProjectHourlyStatistic
  9. from apps.releases.models import Release
  10. from ..process_event import process_issue_events
  11. from ..schema import (
  12. ErrorIssueEventSchema,
  13. InterchangeIssueEvent,
  14. IssueEventSchema,
  15. )
  16. from .utils import EventIngestTestCase
  17. COMPAT_TEST_DATA_DIR = "events/test_data"
  18. def is_exception(v):
  19. return v.get("type") == "exception"
  20. class IssueEventIngestTestCase(EventIngestTestCase):
  21. """
  22. These tests bypass the API and celery. They test the event ingest logic itself.
  23. This file should be large are test the following use cases
  24. - Multiple event saved at the same time
  25. - Sentry API compatibility
  26. - Default, Error, and CSP types
  27. - Graceful failure such as duplicate event ids or invalid data
  28. """
  29. def test_two_events(self):
  30. with self.assertNumQueries(7):
  31. self.process_events([{}, {}])
  32. self.assertEqual(Issue.objects.count(), 1)
  33. self.assertEqual(IssueHash.objects.count(), 1)
  34. self.assertEqual(IssueEvent.objects.count(), 2)
  35. self.assertTrue(
  36. IssueEventProjectHourlyStatistic.objects.filter(
  37. count=2, project=self.project
  38. ).exists()
  39. )
  40. def test_two_issues(self):
  41. self.process_events(
  42. [
  43. {
  44. "message": "a",
  45. },
  46. {
  47. "message": "b",
  48. },
  49. ]
  50. )
  51. self.assertEqual(Issue.objects.count(), 2)
  52. self.assertEqual(IssueHash.objects.count(), 2)
  53. self.assertEqual(IssueEvent.objects.count(), 2)
  54. self.assertTrue(
  55. IssueEventProjectHourlyStatistic.objects.filter(
  56. count=2, project=self.project
  57. ).exists()
  58. )
  59. def test_transaction_truncation(self):
  60. data = self.get_json_data("events/test_data/py_hi_event.json")
  61. data["culprit"] = "x" * 201
  62. self.process_events(data)
  63. self.assertTrue(IssueEvent.objects.first())
  64. def test_message_empty_param_list(self):
  65. self.process_events(
  66. [
  67. {"logentry": {"message": "This is a warning: %s", "params": []}},
  68. ]
  69. )
  70. self.assertEqual(
  71. IssueEvent.objects.first().data["logentry"]["message"],
  72. "This is a warning: %s",
  73. )
  74. def test_query_release_environment_difs(self):
  75. """Test efficiency of existing release/environment/dif"""
  76. project2 = baker.make("projects.Project", organization=self.organization)
  77. release = baker.make("releases.Release", version="r", projects=[self.project])
  78. environment = baker.make(
  79. "environments.Environment", name="e", projects=[self.project]
  80. )
  81. baker.make("difs.DebugInformationFile", project=self.project)
  82. baker.make("releases.Release", projects=[self.project, project2])
  83. baker.make("releases.Release", version="r", projects=[project2])
  84. baker.make("releases.Release", version="r")
  85. baker.make("environments.Environment", projects=[self.project])
  86. baker.make("difs.DebugInformationFile", project=self.project)
  87. event1 = {
  88. "release": release.version,
  89. "environment": environment.name,
  90. }
  91. event2 = {
  92. "release": "newr",
  93. "environment": "newe",
  94. }
  95. with self.assertNumQueries(13):
  96. self.process_events([event1, {}])
  97. self.process_events([event1, event2, {}])
  98. self.assertEqual(self.project.releases.count(), 3)
  99. self.assertEqual(self.project.environment_set.count(), 3)
  100. def test_reopen_resolved_issue(self):
  101. event = self.process_events({})[0]
  102. issue = Issue.objects.first()
  103. issue.status = EventStatus.RESOLVED
  104. issue.save()
  105. event.event_id = uuid.uuid4()
  106. self.process_events(event.dict())
  107. issue.refresh_from_db()
  108. self.assertEqual(issue.status, EventStatus.UNRESOLVED)
  109. def test_fingerprint(self):
  110. data = {
  111. "exception": [
  112. {
  113. "type": "a",
  114. "value": "a",
  115. }
  116. ],
  117. "event_id": uuid.uuid4(),
  118. "fingerprint": ["foo"],
  119. }
  120. self.process_events(data)
  121. data["exception"][0]["type"] = "lol"
  122. data["event_id"] = uuid.uuid4()
  123. self.process_events(data)
  124. self.assertEqual(Issue.objects.count(), 1)
  125. self.assertEqual(IssueEvent.objects.count(), 2)
  126. def test_event_release(self):
  127. data = self.get_json_data("events/test_data/py_hi_event.json")
  128. baker.make("releases.Release", version=data.get("release"))
  129. self.process_events(data)
  130. event = IssueEvent.objects.first()
  131. self.assertTrue(event.release)
  132. self.assertTrue(
  133. Release.objects.filter(
  134. version=data.get("release"), projects=self.project
  135. ).exists()
  136. )
  137. def test_event_release_blank(self):
  138. """In the SDK, it's possible to set a release to a blank string"""
  139. data = self.get_json_data("events/test_data/py_hi_event.json")
  140. data["release"] = ""
  141. self.process_events(data)
  142. self.assertTrue(IssueEvent.objects.first())
  143. def test_event_environment(self):
  144. # Some noise to test queries
  145. baker.make("environments.Environment", organization=self.organization)
  146. baker.make("environments.EnvironmentProject", project=self.project)
  147. data = self.get_json_data("events/test_data/py_hi_event.json")
  148. data["environment"] = "dev"
  149. self.process_events(data)
  150. event = IssueEvent.objects.first()
  151. self.assertTrue(event.issue.project.environment_set.filter(name="dev").exists())
  152. self.assertEqual(event.issue.project.environment_set.count(), 2)
  153. data["event_id"] = uuid.uuid4().hex
  154. self.process_events(data)
  155. self.assertEqual(event.issue.project.environment_set.count(), 2)
  156. def test_multi_org_event_environment_processing(self):
  157. environment = baker.make(
  158. "environments.Environment", organization=self.organization, name="prod"
  159. )
  160. baker.make(
  161. "environments.EnvironmentProject",
  162. environment=environment,
  163. project=self.project,
  164. )
  165. event_list = []
  166. data = self.get_json_data("events/test_data/py_hi_event.json")
  167. data["environment"] = "dev"
  168. event_list.append(
  169. InterchangeIssueEvent(
  170. project_id=self.project.id,
  171. organization_id=self.organization.id,
  172. payload=IssueEventSchema(**data),
  173. )
  174. )
  175. org_b = baker.make("organizations_ext.organization")
  176. org_b_project = baker.make("projects.Project", organization=org_b)
  177. data = self.get_json_data("events/test_data/py_hi_event.json")
  178. data["environment"] = "prod"
  179. event_list.append(
  180. InterchangeIssueEvent(
  181. project_id=org_b_project.id,
  182. organization_id=org_b.id,
  183. payload=IssueEventSchema(**data),
  184. )
  185. )
  186. process_issue_events(event_list)
  187. self.assertTrue(self.project.environment_set.filter(name="dev").exists())
  188. self.assertEqual(self.project.environment_set.count(), 2)
  189. self.assertTrue(org_b_project.environment_set.filter(name="prod").exists())
  190. self.assertEqual(org_b_project.environment_set.count(), 1)
  191. def test_multi_org_event_release_processing(self):
  192. release = baker.make(
  193. "releases.Release", organization=self.organization, version="v1.0"
  194. )
  195. baker.make(
  196. "releases.ReleaseProject",
  197. release=release,
  198. project=self.project,
  199. )
  200. event_list = []
  201. data = self.get_json_data("events/test_data/py_hi_event.json")
  202. data["release"] = "v2.0"
  203. event_list.append(
  204. InterchangeIssueEvent(
  205. project_id=self.project.id,
  206. organization_id=self.organization.id,
  207. payload=IssueEventSchema(**data),
  208. )
  209. )
  210. org_b = baker.make("organizations_ext.organization")
  211. org_b_project = baker.make("projects.Project", organization=org_b)
  212. data = self.get_json_data("events/test_data/py_hi_event.json")
  213. data["release"] = "v1.0"
  214. event_list.append(
  215. InterchangeIssueEvent(
  216. project_id=org_b_project.id,
  217. organization_id=org_b.id,
  218. payload=IssueEventSchema(**data),
  219. )
  220. )
  221. process_issue_events(event_list)
  222. self.assertTrue(self.organization.release_set.filter(version="v2.0").exists())
  223. self.assertEqual(self.organization.release_set.count(), 2)
  224. self.assertTrue(org_b.release_set.filter(version="v1.0").exists())
  225. self.assertEqual(org_b.release_set.count(), 1)
  226. def test_process_sourcemap(self):
  227. sample_event = {
  228. "exception": {
  229. "values": [
  230. {
  231. "type": "Error",
  232. "value": "The error",
  233. "stacktrace": {
  234. "frames": [
  235. {
  236. "filename": "http://localhost:8080/dist/bundle.js",
  237. "function": "?",
  238. "in_app": True,
  239. "lineno": 2,
  240. "colno": 74016,
  241. },
  242. {
  243. "filename": "http://localhost:8080/dist/bundle.js",
  244. "function": "?",
  245. "in_app": True,
  246. "lineno": 2,
  247. "colno": 74012,
  248. },
  249. {
  250. "filename": "http://localhost:8080/dist/bundle.js",
  251. "function": "?",
  252. "in_app": True,
  253. "lineno": 2,
  254. "colno": 73992,
  255. },
  256. ]
  257. },
  258. "mechanism": {"type": "onerror", "handled": False},
  259. }
  260. ]
  261. },
  262. "level": "error",
  263. "platform": "javascript",
  264. "event_id": "0691751a89db419994efac8ac9b00a5d",
  265. "timestamp": 1648414309.82,
  266. "environment": "production",
  267. "request": {
  268. "url": "http://localhost:8080/",
  269. "headers": {
  270. "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0"
  271. },
  272. },
  273. }
  274. release = baker.make("releases.Release", organization=self.organization)
  275. release.projects.add(self.project)
  276. blob_bundle = baker.make("files.FileBlob", blob="uploads/file_blobs/bundle.js")
  277. blob_bundle_map = baker.make(
  278. "files.FileBlob", blob="uploads/file_blobs/bundle.js.map"
  279. )
  280. baker.make(
  281. "releases.ReleaseFile",
  282. release=release,
  283. file__name="bundle.js",
  284. file__blob=blob_bundle,
  285. )
  286. baker.make(
  287. "releases.ReleaseFile",
  288. release=release,
  289. file__name="bundle.js.map",
  290. file__blob=blob_bundle_map,
  291. )
  292. try:
  293. os.mkdir("./uploads/file_blobs")
  294. except FileExistsError:
  295. pass
  296. shutil.copyfile(
  297. "./apps/event_ingest/tests/test_data/bundle.js",
  298. "./uploads/file_blobs/bundle.js",
  299. )
  300. shutil.copyfile(
  301. "./apps/event_ingest/tests/test_data/bundle.js.map",
  302. "./uploads/file_blobs/bundle.js.map",
  303. )
  304. data = sample_event | {"release": release.version}
  305. self.process_events(data)
  306. # Show that colno changes
  307. self.assertEqual(
  308. IssueEvent.objects.first().data["exception"][0]["stacktrace"]["frames"][0][
  309. "colno"
  310. ],
  311. 13,
  312. )
  313. self.assertTrue(IssueEvent.objects.filter(release=release).exists())
  314. def test_search_vector(self):
  315. word = "orange"
  316. for _ in range(2):
  317. self.process_events([{"message": word}])
  318. issue = Issue.objects.filter(search_vector=word).first()
  319. self.assertTrue(issue)
  320. self.assertEqual(len(issue.search_vector.split(" ")), 1)
  321. class SentryCompatTestCase(EventIngestTestCase):
  322. """
  323. These tests specifically test former open source sentry compatibility
  324. But otherwise are part of issue event ingest testing
  325. """
  326. def setUp(self):
  327. super().setUp()
  328. self.create_logged_in_user()
  329. def get_json_test_data(self, name: str):
  330. """Get incoming event, sentry json, sentry api event"""
  331. event = self.get_json_data(
  332. f"{COMPAT_TEST_DATA_DIR}/incoming_events/{name}.json"
  333. )
  334. sentry_json = self.get_json_data(
  335. f"{COMPAT_TEST_DATA_DIR}/oss_sentry_json/{name}.json"
  336. )
  337. # Force captured test data to match test generated data
  338. sentry_json["project"] = self.project.id
  339. api_sentry_event = self.get_json_data(
  340. f"{COMPAT_TEST_DATA_DIR}/oss_sentry_events/{name}.json"
  341. )
  342. return event, sentry_json, api_sentry_event
  343. def get_event_json(self, event: IssueEvent):
  344. return self.client.get(
  345. reverse(
  346. "api:get_event_json",
  347. kwargs={
  348. "organization_slug": self.organization.slug,
  349. "issue_id": event.issue_id,
  350. "event_id": event.id,
  351. },
  352. )
  353. ).json()
  354. # Upgrade functions handle intentional differences between GlitchTip and Sentry OSS
  355. def upgrade_title(self, value: str):
  356. """Sentry OSS uses ... while GlitchTip uses unicode …"""
  357. if value[-1] == "…":
  358. return value[:-4]
  359. return value.strip("...")
  360. def upgrade_metadata(self, value: dict):
  361. value["title"] = self.upgrade_title(value["title"])
  362. return value
  363. def assertCompareData(self, data1: dict, data2: dict, fields: list[str]):
  364. """Compare data of two dict objects. Compare only provided fields list"""
  365. for field in fields:
  366. field_value1 = data1.get(field)
  367. field_value2 = data2.get(field)
  368. if field == "datetime":
  369. # Check that it's close enough
  370. field_value1 = field_value1[:23]
  371. field_value2 = field_value2[:23]
  372. if field == "title" and isinstance(field_value1, str):
  373. field_value1 = self.upgrade_title(field_value1)
  374. if field_value2:
  375. field_value2 = self.upgrade_title(field_value2)
  376. if (
  377. field == "metadata"
  378. and isinstance(field_value1, dict)
  379. and field_value1.get("title")
  380. ):
  381. field_value1 = self.upgrade_metadata(field_value1)
  382. if field_value2:
  383. field_value2 = self.upgrade_metadata(field_value2)
  384. self.assertEqual(
  385. field_value1,
  386. field_value2,
  387. f"Failed for field '{field}'",
  388. )
  389. def get_project_events_detail(self, event_id: str):
  390. return reverse(
  391. "api:get_project_issue_event",
  392. kwargs={
  393. "organization_slug": self.project.organization.slug,
  394. "project_slug": self.project.slug,
  395. "event_id": event_id,
  396. },
  397. )
  398. def submit_event(self, event_data: dict, event_type="error") -> IssueEvent:
  399. event_class = ErrorIssueEventSchema
  400. if event_type == "default":
  401. event_class = IssueEventSchema
  402. event = InterchangeIssueEvent(
  403. event_id=event_data["event_id"],
  404. organization_id=self.organization.id if self.organization else None,
  405. project_id=self.project.id,
  406. payload=event_class(**event_data),
  407. )
  408. process_issue_events([event])
  409. return IssueEvent.objects.get(pk=event.event_id)
  410. def upgrade_data(self, data):
  411. """A recursive replace function"""
  412. if isinstance(data, dict):
  413. return {k: self.upgrade_data(v) for k, v in data.items()}
  414. elif isinstance(data, list):
  415. return [self.upgrade_data(i) for i in data]
  416. return data
  417. def test_template_error(self):
  418. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  419. "django_template_error"
  420. )
  421. event = self.submit_event(sdk_error)
  422. url = self.get_project_events_detail(event.id.hex)
  423. res = self.client.get(url)
  424. res_data = res.json()
  425. self.assertEqual(res.status_code, 200)
  426. self.assertCompareData(res_data, sentry_data, ["culprit", "title", "metadata"])
  427. res_frames = res_data["entries"][0]["data"]["values"][0]["stacktrace"]["frames"]
  428. frames = sentry_data["entries"][0]["data"]["values"][0]["stacktrace"]["frames"]
  429. for i in range(6):
  430. # absPath don't always match - needs fixed
  431. self.assertCompareData(res_frames[i], frames[i], ["absPath"])
  432. for res_frame, frame in zip(res_frames, frames):
  433. self.assertCompareData(
  434. res_frame,
  435. frame,
  436. ["lineNo", "function", "filename", "module", "context"],
  437. )
  438. if frame.get("vars"):
  439. self.assertCompareData(
  440. res_frame["vars"], frame["vars"], ["exc", "request"]
  441. )
  442. if frame["vars"].get("get_response"):
  443. # Memory address is different, truncate it
  444. self.assertEqual(
  445. res_frame["vars"]["get_response"][:-16],
  446. frame["vars"]["get_response"][:-16],
  447. )
  448. self.assertCompareData(
  449. res_data["entries"][0]["data"],
  450. sentry_data["entries"][0]["data"],
  451. ["env", "headers", "url", "method", "inferredContentType"],
  452. )
  453. url = reverse("api:get_issue", kwargs={"issue_id": event.issue.pk})
  454. res = self.client.get(url)
  455. self.assertEqual(res.status_code, 200)
  456. res_data = res.json()
  457. data = self.get_json_data("events/test_data/django_template_error_issue.json")
  458. self.assertCompareData(res_data, data, ["title", "metadata"])
  459. def test_js_sdk_with_unix_timestamp(self):
  460. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  461. "js_event_with_unix_timestamp"
  462. )
  463. event = self.submit_event(sdk_error)
  464. self.assertNotEqual(event.timestamp, sdk_error["timestamp"])
  465. self.assertEqual(event.timestamp.year, 2020)
  466. event_json = self.get_event_json(event)
  467. self.assertCompareData(event_json, sentry_json, ["datetime"])
  468. res = self.client.get(self.get_project_events_detail(event.pk))
  469. res_data = res.json()
  470. self.assertCompareData(res_data, sentry_data, ["timestamp"])
  471. self.assertEqual(res_data["entries"][1].get("type"), "breadcrumbs")
  472. self.maxDiff = None
  473. self.assertEqual(
  474. res_data["entries"][1],
  475. self.upgrade_data(sentry_data["entries"][1]),
  476. )
  477. def test_dotnet_error(self):
  478. sdk_error = self.get_json_data(
  479. "events/test_data/incoming_events/dotnet_error.json"
  480. )
  481. event = self.submit_event(sdk_error)
  482. self.assertEqual(IssueEvent.objects.count(), 1)
  483. sentry_data = self.get_json_data(
  484. "events/test_data/oss_sentry_events/dotnet_error.json"
  485. )
  486. res = self.client.get(self.get_project_events_detail(event.pk))
  487. res_data = res.json()
  488. self.assertCompareData(
  489. res_data,
  490. sentry_data,
  491. ["eventID", "title", "culprit", "platform", "type", "metadata"],
  492. )
  493. res_exception = next(filter(is_exception, res_data["entries"]), None)
  494. sentry_exception = next(filter(is_exception, sentry_data["entries"]), None)
  495. self.assertEqual(
  496. res_exception["data"].get("hasSystemFrames"),
  497. sentry_exception["data"].get("hasSystemFrames"),
  498. )
  499. def test_php_message_event(self):
  500. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  501. "php_message_event"
  502. )
  503. event = self.submit_event(sdk_error)
  504. res = self.client.get(self.get_project_events_detail(event.pk))
  505. res_data = res.json()
  506. self.assertCompareData(
  507. res_data,
  508. sentry_data,
  509. [
  510. "message",
  511. "title",
  512. ],
  513. )
  514. self.assertEqual(
  515. res_data["entries"][0]["data"]["params"],
  516. sentry_data["entries"][0]["data"]["params"],
  517. )
  518. def test_django_message_params(self):
  519. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  520. "django_message_params"
  521. )
  522. event = self.submit_event(sdk_error)
  523. res = self.client.get(self.get_project_events_detail(event.pk))
  524. res_data = res.json()
  525. self.assertCompareData(
  526. res_data,
  527. sentry_data,
  528. [
  529. "message",
  530. "title",
  531. ],
  532. )
  533. self.assertEqual(res_data["entries"][0], sentry_data["entries"][0])
  534. def test_message_event(self):
  535. """A generic message made with the Sentry SDK. Generally has less data than exceptions."""
  536. from events.test_data.django_error_factory import message
  537. event = self.submit_event(message, event_type="default")
  538. res = self.client.get(self.get_project_events_detail(event.pk))
  539. res_data = res.json()
  540. data = self.get_json_data("events/test_data/django_message_event.json")
  541. self.assertCompareData(
  542. res_data,
  543. data,
  544. ["title", "culprit", "type", "metadata", "platform", "packages"],
  545. )
  546. def test_python_logging(self):
  547. """Test Sentry SDK logging integration based event"""
  548. sdk_error, sentry_json, sentry_data = self.get_json_test_data("python_logging")
  549. event = self.submit_event(sdk_error, event_type="default")
  550. res = self.client.get(self.get_project_events_detail(event.pk))
  551. res_data = res.json()
  552. self.assertEqual(res.status_code, 200)
  553. self.assertCompareData(
  554. res_data,
  555. sentry_data,
  556. [
  557. "title",
  558. "logentry",
  559. "culprit",
  560. "type",
  561. "metadata",
  562. "platform",
  563. "packages",
  564. ],
  565. )
  566. def test_go_file_not_found(self):
  567. sdk_error = self.get_json_data(
  568. "events/test_data/incoming_events/go_file_not_found.json"
  569. )
  570. event = self.submit_event(sdk_error)
  571. sentry_data = self.get_json_data(
  572. "events/test_data/oss_sentry_events/go_file_not_found.json"
  573. )
  574. res = self.client.get(self.get_project_events_detail(event.pk))
  575. res_data = res.json()
  576. self.assertEqual(res.status_code, 200)
  577. self.assertCompareData(
  578. res_data,
  579. sentry_data,
  580. ["title", "culprit", "type", "metadata", "platform"],
  581. )
  582. def test_very_small_event(self):
  583. """
  584. Shows a very minimalist event example. Good for seeing what data is null
  585. """
  586. sdk_error = self.get_json_data(
  587. "events/test_data/incoming_events/very_small_event.json"
  588. )
  589. event = self.submit_event(sdk_error, event_type="default")
  590. sentry_data = self.get_json_data(
  591. "events/test_data/oss_sentry_events/very_small_event.json"
  592. )
  593. res = self.client.get(self.get_project_events_detail(event.pk))
  594. res_data = res.json()
  595. self.assertEqual(res.status_code, 200)
  596. self.assertCompareData(
  597. res_data,
  598. sentry_data,
  599. ["culprit", "type", "platform", "entries"],
  600. )
  601. def test_python_zero_division(self):
  602. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  603. "python_zero_division"
  604. )
  605. event = self.submit_event(sdk_error)
  606. event_json = self.get_event_json(event)
  607. self.assertCompareData(
  608. event_json,
  609. sentry_json,
  610. [
  611. "event_id",
  612. "project",
  613. "release",
  614. "dist",
  615. "platform",
  616. "level",
  617. "modules",
  618. "time_spent",
  619. "sdk",
  620. "type",
  621. "title",
  622. "breadcrumbs",
  623. ],
  624. )
  625. self.assertCompareData(
  626. event_json["request"],
  627. sentry_json["request"],
  628. [
  629. "url",
  630. "headers",
  631. "method",
  632. "env",
  633. "query_string",
  634. ],
  635. )
  636. self.assertEqual(
  637. event_json["datetime"][:22],
  638. sentry_json["datetime"][:22],
  639. "Compare if datetime is almost the same",
  640. )
  641. res = self.client.get(self.get_project_events_detail(event.pk))
  642. res_data = res.json()
  643. self.assertEqual(res.status_code, 200)
  644. self.assertCompareData(
  645. res_data,
  646. sentry_data,
  647. ["title", "culprit", "type", "metadata", "platform", "packages"],
  648. )
  649. self.assertCompareData(
  650. res_data["entries"][1]["data"],
  651. sentry_data["entries"][1]["data"],
  652. [
  653. "inferredContentType",
  654. "env",
  655. "headers",
  656. "url",
  657. "query",
  658. "data",
  659. "method",
  660. ],
  661. )
  662. issue = event.issue
  663. issue.refresh_from_db()
  664. self.assertEqual(issue.level, LogLevel.ERROR)
  665. def test_dotnet_zero_division(self):
  666. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  667. "dotnet_divide_zero"
  668. )
  669. event = self.submit_event(sdk_error)
  670. event_json = self.get_event_json(event)
  671. res = self.client.get(self.get_project_events_detail(event.pk))
  672. res_data = res.json()
  673. self.assertCompareData(event_json, sentry_json, ["environment"])
  674. self.assertCompareData(
  675. res_data,
  676. sentry_data,
  677. [
  678. "eventID",
  679. "title",
  680. "culprit",
  681. "platform",
  682. "type",
  683. "metadata",
  684. ],
  685. )
  686. res_exception = next(filter(is_exception, res_data["entries"]), None)
  687. sentry_exception = next(filter(is_exception, sentry_data["entries"]), None)
  688. self.assertEqual(
  689. res_exception["data"]["values"][0]["stacktrace"]["frames"][4]["context"],
  690. sentry_exception["data"]["values"][0]["stacktrace"]["frames"][4]["context"],
  691. )
  692. tags = res_data.get("tags")
  693. browser_tag = next(filter(lambda tag: tag["key"] == "browser", tags), None)
  694. self.assertEqual(browser_tag["value"], "Firefox 76.0")
  695. environment_tag = next(
  696. filter(lambda tag: tag["key"] == "environment", tags), None
  697. )
  698. self.assertEqual(environment_tag["value"], "Development")
  699. def test_sentry_cli_send_event_no_level(self):
  700. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  701. "sentry_cli_send_event_no_level"
  702. )
  703. event = self.submit_event(sdk_error, event_type="default")
  704. event_json = self.get_event_json(event)
  705. self.assertCompareData(event_json, sentry_json, ["title"])
  706. self.assertEqual(event_json["project"], event.issue.project_id)
  707. res = self.client.get(self.get_project_events_detail(event.pk))
  708. res_data = res.json()
  709. self.assertCompareData(
  710. res_data,
  711. sentry_data,
  712. [
  713. "userReport",
  714. "title",
  715. "culprit",
  716. "type",
  717. "metadata",
  718. "message",
  719. "platform",
  720. "previousEventID",
  721. ],
  722. )
  723. self.assertEqual(res_data["projectID"], event.issue.project_id)
  724. def test_js_error_with_context(self):
  725. self.project.scrub_ip_addresses = False
  726. self.project.save()
  727. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  728. "js_error_with_context"
  729. )
  730. event_store_url = (
  731. reverse("api:event_store", args=[self.project.id])
  732. + "?sentry_key="
  733. + self.project.projectkey_set.first().public_key.hex
  734. )
  735. res = self.client.post(
  736. event_store_url,
  737. sdk_error,
  738. content_type="application/json",
  739. REMOTE_ADDR="142.255.29.14",
  740. )
  741. res_data = res.json()
  742. event = IssueEvent.objects.get(pk=res_data["event_id"])
  743. event_json = self.get_event_json(event)
  744. self.assertCompareData(event_json, sentry_json, ["title", "extra", "user"])
  745. url = self.get_project_events_detail(event.pk)
  746. res = self.client.get(url)
  747. res_json = res.json()
  748. self.assertCompareData(res_json, sentry_data, ["context"])
  749. self.assertCompareData(
  750. res_json["user"], sentry_data["user"], ["id", "email", "ip_address"]
  751. )
  752. def test_small_js_error(self):
  753. """A small example to test stacktraces"""
  754. sdk_error, sentry_json, sentry_data = self.get_json_test_data("small_js_error")
  755. event = self.submit_event(sdk_error, event_type="default")
  756. event_json = self.get_event_json(event)
  757. self.assertCompareData(
  758. event_json["exception"][0],
  759. sentry_json["exception"]["values"][0],
  760. ["type", "values", "exception", "abs_path"],
  761. )