import gzip import hashlib import io import json import uuid import zipfile from django.core.files.uploadedfile import SimpleUploadedFile from django.http.response import HttpResponse from django.urls import reverse from apps.event_ingest.tests.utils import generate_event from apps.files.models import File, FileBlob from apps.issue_events.models import Issue from glitchtip.test_utils.test_case import GlitchTestCase debug_id = str(uuid.uuid4()) minified_js = "function a(n,t){return n+t}" minified_js_map = f"""{{ "version": 3, "file": "minified.js", "sourceRoot": "", "sources": ["original.js"], "names": ["calculateSum", "firstNumber", "secondNumber", "a", "n", "t"], "mappings": "AAAA,QAASA,CAAT,CAAeC,CAAf,EAAkBC,CAAlB,EAAqB,OAAOD,CAAP,GAAUC,CAAV,GAAaC,CAAd", "sourcesContent": ["function calculateSum(firstNumber, secondNumber) {{\\n return firstNumber + secondNumber;\\n}}"], "debugId": "{debug_id}" }}""" original_js = """function calculateSum(firstNumber, secondNumber) { return firstNumber + secondNumber; }""" class SourceCodeTestCase(GlitchTestCase): @classmethod def setUpTestData(cls): cls.create_user() def setUp(self): self.client.force_login(self.user) def upload_chunk( self, url: str, ) -> tuple[HttpResponse, str]: # First construct zip file containing source, map, and manifest files. manifest = { "files": { f"files/_/_/{debug_id}-0.js": { "type": "minified_source", "url": f"~/{debug_id}-0.js", "headers": {"debug-id": debug_id, "sourcemap": "minified.js.map"}, }, f"files/_/_/{debug_id}-0.js.map": { "type": "source_map", "url": f"~/{debug_id}-0.js.map", "headers": {"debug-id": debug_id}, }, }, "debug_id": str(uuid.uuid4()), "org": self.organization.slug, "project": self.project.slug, } in_memory_buffer = io.BytesIO() with zipfile.ZipFile(in_memory_buffer, mode="w") as zipf: zipf.writestr("manifest.json", json.dumps(manifest)) zipf.writestr(f"files/_/_/{debug_id}-0.js", minified_js) zipf.writestr(f"files/_/_/{debug_id}-0.js.map", minified_js_map) in_memory_buffer.seek(0) # Calculate the SHA1 checksum first checksum = hashlib.sha1(in_memory_buffer.read()).hexdigest() in_memory_buffer.seek(0) file = SimpleUploadedFile( checksum, # Use checksum as filename gzip.compress(in_memory_buffer.read()), ) response = self.client.post( url, {"file_gzip": [file]}, ) return response, checksum def test_sourcemap_integrated(self): """Test full workflow of uploading sourcemaps to unminifying event code""" chunk_upload_url = reverse( "api:get_chunk_upload_info", args=[self.organization.slug] ) assemble_url = reverse( "api:artifact_bundle_assemble", args=[self.organization.slug] ) envelope_url = ( reverse("api:event_envelope", args=[self.project.id]) + f"?sentry_key={self.projectkey.public_key}" ) res = self.client.get(chunk_upload_url) self.assertContains(res, "artifact_bundles") # sentry sdk requires this set # Upload source code "chunks" res, checksum = self.upload_chunk(chunk_upload_url) self.assertEqual(res.status_code, 200) self.assertEqual(FileBlob.objects.count(), 1) # Call assemble res = self.client.post( assemble_url, { "checksum": checksum, "chunks": [checksum], "projects": [self.project.slug], }, content_type="application/json", ) self.assertContains(res, "created") self.assertEqual(FileBlob.objects.count(), 3) self.assertEqual(File.objects.count(), 2) # Submit event data = generate_event( event_type="error", platform="javascript", event={ "exception": { "values": [ { "type": "Error", "value": "err", "stacktrace": { "frames": [ { "filename": "http://127.0.0.1:8080/assets/minified.js", "function": "?", "in_app": True, "lineno": 2, "colno": 4, }, ] }, } ] }, "debug_meta": { "images": [ { "type": "sourcemap", "code_file": "http://127.0.0.1:8080/assets/minified.js", "debug_id": debug_id, } ] }, }, envelope=True, ) res = self.client.post(envelope_url, data, content_type="application/json") self.assertContains(res, data[0]["event_id"][:8]) self.assertEqual(Issue.objects.count(), 1) issue = Issue.objects.get() event = issue.issueevent_set.first() self.assertIn( "firstNumber", event.data["exception"][0]["stacktrace"]["frames"][0]["context_line"], )