test_debug_files.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. from __future__ import absolute_import
  2. import zipfile
  3. from uuid import uuid4
  4. from six import BytesIO, text_type
  5. from django.core.files.uploadedfile import SimpleUploadedFile
  6. from django.core.urlresolvers import reverse
  7. from sentry.testutils import APITestCase
  8. from sentry.models import ProjectDebugFile, VersionDSymFile
  9. # This is obviously a freely generated UUID and not the checksum UUID.
  10. # This is permissible if users want to send different UUIDs
  11. PROGUARD_UUID = '6dc7fdb0-d2fb-4c8e-9d6b-bb1aa98929b1'
  12. PROGUARD_SOURCE = b'''\
  13. org.slf4j.helpers.Util$ClassContextSecurityManager -> org.a.b.g$a:
  14. 65:65:void <init>() -> <init>
  15. 67:67:java.lang.Class[] getClassContext() -> getClassContext
  16. 65:65:void <init>(org.slf4j.helpers.Util$1) -> <init>
  17. '''
  18. class DebugFilesUploadTest(APITestCase):
  19. def _upload_proguard(self, url, uuid):
  20. out = BytesIO()
  21. f = zipfile.ZipFile(out, 'w')
  22. f.writestr('proguard/%s.txt' % uuid, PROGUARD_SOURCE)
  23. f.close()
  24. return self.client.post(
  25. url, {
  26. 'file':
  27. SimpleUploadedFile('symbols.zip', out.getvalue(),
  28. content_type='application/zip'),
  29. },
  30. format='multipart'
  31. )
  32. def test_simple_proguard_upload(self):
  33. project = self.create_project(name='foo')
  34. url = reverse(
  35. 'sentry-api-0-dsym-files',
  36. kwargs={
  37. 'organization_slug': project.organization.slug,
  38. 'project_slug': project.slug,
  39. }
  40. )
  41. self.login_as(user=self.user)
  42. response = self._upload_proguard(url, PROGUARD_UUID)
  43. assert response.status_code == 201, response.content
  44. assert len(response.data) == 1
  45. assert response.data[0]['headers'] == {
  46. 'Content-Type': 'text/x-proguard+plain'}
  47. assert response.data[0]['sha1'] == 'e6d3c5185dac63eddfdc1a5edfffa32d46103b44'
  48. assert response.data[0]['uuid'] == PROGUARD_UUID
  49. assert response.data[0]['objectName'] == 'proguard-mapping'
  50. assert response.data[0]['cpuName'] == 'any'
  51. assert response.data[0]['symbolType'] == 'proguard'
  52. def test_associate_proguard_dsym(self):
  53. project = self.create_project(name='foo')
  54. url = reverse(
  55. 'sentry-api-0-dsym-files',
  56. kwargs={
  57. 'organization_slug': project.organization.slug,
  58. 'project_slug': project.slug,
  59. }
  60. )
  61. self.login_as(user=self.user)
  62. response = self._upload_proguard(url, PROGUARD_UUID)
  63. assert response.status_code == 201, response.content
  64. assert len(response.data) == 1
  65. assert response.data[0]['headers'] == {
  66. 'Content-Type': 'text/x-proguard+plain'}
  67. assert response.data[0]['sha1'] == 'e6d3c5185dac63eddfdc1a5edfffa32d46103b44'
  68. assert response.data[0]['uuid'] == PROGUARD_UUID
  69. assert response.data[0]['objectName'] == 'proguard-mapping'
  70. assert response.data[0]['cpuName'] == 'any'
  71. assert response.data[0]['symbolType'] == 'proguard'
  72. url = reverse(
  73. 'sentry-api-0-associate-dsym-files',
  74. kwargs={
  75. 'organization_slug': project.organization.slug,
  76. 'project_slug': project.slug,
  77. }
  78. )
  79. response = self.client.post(
  80. url, {
  81. 'checksums': ['e6d3c5185dac63eddfdc1a5edfffa32d46103b44'],
  82. 'platform': 'android',
  83. 'name': 'MyApp',
  84. 'appId': 'com.example.myapp',
  85. 'version': '1.0',
  86. 'build': '1',
  87. },
  88. format='json'
  89. )
  90. assert response.status_code == 200, response.content
  91. assert len(response.data) == 1
  92. assert response.data['associatedDsymFiles'][0]['uuid'] == PROGUARD_UUID
  93. vdf = VersionDSymFile.objects.get()
  94. assert vdf.version == '1.0'
  95. assert vdf.build == '1'
  96. assert vdf.dsym_app.app_id == 'com.example.myapp'
  97. assert vdf.dsym_file.debug_id == PROGUARD_UUID
  98. def test_associate_proguard_dsym_no_build(self):
  99. project = self.create_project(name='foo')
  100. url = reverse(
  101. 'sentry-api-0-dsym-files',
  102. kwargs={
  103. 'organization_slug': project.organization.slug,
  104. 'project_slug': project.slug,
  105. }
  106. )
  107. self.login_as(user=self.user)
  108. response = self._upload_proguard(url, PROGUARD_UUID)
  109. assert response.status_code == 201, response.content
  110. assert len(response.data) == 1
  111. assert response.data[0]['headers'] == {
  112. 'Content-Type': 'text/x-proguard+plain'}
  113. assert response.data[0]['sha1'] == 'e6d3c5185dac63eddfdc1a5edfffa32d46103b44'
  114. assert response.data[0]['uuid'] == PROGUARD_UUID
  115. assert response.data[0]['objectName'] == 'proguard-mapping'
  116. assert response.data[0]['cpuName'] == 'any'
  117. assert response.data[0]['symbolType'] == 'proguard'
  118. url = reverse(
  119. 'sentry-api-0-associate-dsym-files',
  120. kwargs={
  121. 'organization_slug': project.organization.slug,
  122. 'project_slug': project.slug,
  123. }
  124. )
  125. response = self.client.post(
  126. url, {
  127. 'checksums': ['e6d3c5185dac63eddfdc1a5edfffa32d46103b44'],
  128. 'platform': 'android',
  129. 'name': 'MyApp',
  130. 'appId': 'com.example.myapp',
  131. 'version': '1.0',
  132. },
  133. format='json'
  134. )
  135. assert response.status_code == 200, response.content
  136. assert len(response.data) == 1
  137. assert response.data['associatedDsymFiles'][0]['uuid'] == PROGUARD_UUID
  138. vdf = VersionDSymFile.objects.get()
  139. assert vdf.version == '1.0'
  140. assert vdf.build is None
  141. assert vdf.dsym_app.app_id == 'com.example.myapp'
  142. assert vdf.dsym_file.debug_id == PROGUARD_UUID
  143. def test_dsyms_requests(self):
  144. project = self.create_project(name='foo')
  145. url = reverse(
  146. 'sentry-api-0-dsym-files',
  147. kwargs={
  148. 'organization_slug': project.organization.slug,
  149. 'project_slug': project.slug,
  150. }
  151. )
  152. self.login_as(user=self.user)
  153. response = self._upload_proguard(url, PROGUARD_UUID)
  154. assert response.status_code == 201, response.content
  155. assert len(response.data) == 1
  156. url = reverse(
  157. 'sentry-api-0-associate-dsym-files',
  158. kwargs={
  159. 'organization_slug': project.organization.slug,
  160. 'project_slug': project.slug,
  161. }
  162. )
  163. response = self.client.post(
  164. url, {
  165. 'checksums': ['e6d3c5185dac63eddfdc1a5edfffa32d46103b44'],
  166. 'platform': 'android',
  167. 'name': 'MyApp',
  168. 'appId': 'com.example.myapp',
  169. 'version': '1.0',
  170. 'build': '1',
  171. },
  172. format='json'
  173. )
  174. assert response.status_code == 200, response.content
  175. assert len(response.data) == 1
  176. assert response.data['associatedDsymFiles'][0]['uuid'] == PROGUARD_UUID
  177. download_id = response.data['associatedDsymFiles'][0]['id']
  178. url = reverse(
  179. 'sentry-api-0-dsym-files',
  180. kwargs={
  181. 'organization_slug': project.organization.slug,
  182. 'project_slug': project.slug,
  183. }
  184. )
  185. response = self.client.get(url)
  186. assert response.status_code == 200, response.content
  187. dsym, = response.data
  188. assert dsym['cpuName'] == 'any'
  189. assert dsym['headers'] == {
  190. 'Content-Type': 'text/x-proguard+plain'}
  191. assert dsym['objectName'] == 'proguard-mapping'
  192. assert dsym['sha1'] == 'e6d3c5185dac63eddfdc1a5edfffa32d46103b44'
  193. assert dsym['symbolType'] == 'proguard'
  194. assert dsym['uuid'] == '6dc7fdb0-d2fb-4c8e-9d6b-bb1aa98929b1'
  195. # Test download
  196. response = self.client.get(url + "?id=" + download_id)
  197. assert response.status_code == 200, response.content
  198. assert response.get(
  199. 'Content-Disposition') == 'attachment; filename="' + PROGUARD_UUID + '.txt"'
  200. assert response.get(
  201. 'Content-Length') == text_type(len(PROGUARD_SOURCE))
  202. assert response.get('Content-Type') == 'application/octet-stream'
  203. assert PROGUARD_SOURCE == BytesIO(
  204. b"".join(response.streaming_content)).getvalue()
  205. # Login user with no permissions
  206. user_no_permission = self.create_user('baz@localhost', username='baz')
  207. self.login_as(user=user_no_permission)
  208. response = self.client.get(url + "?id=" + download_id)
  209. assert response.status_code == 403, response.content
  210. # Try to delete with no permissions
  211. response = self.client.delete(url + "?id=" + download_id)
  212. assert response.status_code == 403, response.content
  213. # Login again with permissions
  214. self.login_as(user=self.user)
  215. response = self.client.delete(url + "?id=888")
  216. assert response.status_code == 404, response.content
  217. assert ProjectDebugFile.objects.count() == 1
  218. response = self.client.delete(url + "?id=" + download_id)
  219. assert response.status_code == 204, response.content
  220. assert ProjectDebugFile.objects.count() == 0
  221. def test_dsyms_search(self):
  222. project = self.create_project(name='foo')
  223. url = reverse(
  224. 'sentry-api-0-dsym-files',
  225. kwargs={
  226. 'organization_slug': project.organization.slug,
  227. 'project_slug': project.slug,
  228. }
  229. )
  230. self.login_as(user=self.user)
  231. first_uuid = None
  232. last_uuid = None
  233. for i in range(25):
  234. last_uuid = text_type(uuid4())
  235. if first_uuid is None:
  236. first_uuid = last_uuid
  237. self._upload_proguard(url, last_uuid)
  238. # Test max 20 per page
  239. response = self.client.get(url)
  240. assert response.status_code == 200, response.content
  241. dsyms = response.data
  242. assert len(dsyms) == 20
  243. # Test should return last
  244. response = self.client.get(url + "?query=" + last_uuid)
  245. assert response.status_code == 200, response.content
  246. dsyms = response.data
  247. assert len(dsyms) == 1
  248. response = self.client.get(url + "?query=proguard")
  249. assert response.status_code == 200, response.content
  250. dsyms = response.data
  251. assert len(dsyms) == 20