test_plugin.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from __future__ import annotations
  2. from datetime import timedelta
  3. from typing import Any
  4. from unittest.mock import Mock, patch
  5. import pytest
  6. from django.utils import timezone
  7. from sentry.exceptions import HookValidationError
  8. from sentry.models.commit import Commit
  9. from sentry.models.deploy import Deploy
  10. from sentry.models.environment import Environment
  11. from sentry.models.options.project_option import ProjectOption
  12. from sentry.models.release import Release
  13. from sentry.models.releasecommit import ReleaseCommit
  14. from sentry.models.releaseheadcommit import ReleaseHeadCommit
  15. from sentry.models.repository import Repository
  16. from sentry.testutils.cases import TestCase
  17. from sentry.testutils.silo import region_silo_test
  18. from sentry.utils import json
  19. from sentry_plugins.heroku.plugin import HerokuReleaseHook
  20. @region_silo_test
  21. class SetRefsTest(TestCase):
  22. """
  23. tests that when finish_release is called on a release hook,
  24. we try to get the previous commits based on the version ref
  25. and that we create `ReleaseHeadCommit`s for the version
  26. """
  27. @patch("sentry.tasks.commits.fetch_commits")
  28. def test_minimal(self, mock_fetch_commits):
  29. project = self.create_project()
  30. version = "bbee5b51f84611e4b14834363b8514c2"
  31. data_list = [
  32. {
  33. "id": "c7155651831549cf8a5e47889fce17eb",
  34. "message": "foo",
  35. "author_email": "jane@example.com",
  36. },
  37. {
  38. "id": "62de626b7c7cfb8e77efb4273b1a3df4123e6216",
  39. "message": "hello",
  40. "author_name": "Jess",
  41. },
  42. {
  43. "id": "58de626b7c7cfb8e77efb4273b1a3df4123e6345",
  44. "message": "bar",
  45. "author_name": "Joe^^",
  46. },
  47. {
  48. "id": "bbee5b51f84611e4b14834363b8514c2",
  49. "message": "blah",
  50. "author_email": "katie@example.com",
  51. },
  52. ]
  53. user = self.create_user(email="stebe@sentry.io")
  54. repo = Repository.objects.create(
  55. organization_id=project.organization_id, name=project.name, provider="dummy"
  56. )
  57. ProjectOption.objects.set_value(key="heroku:repository", project=project, value=repo.name)
  58. for data in data_list:
  59. Commit.objects.create(
  60. key=data["id"], organization_id=self.project.organization_id, repository_id=repo.id
  61. )
  62. old_release = Release.objects.create(
  63. version="a" * 40,
  64. organization_id=project.organization_id,
  65. date_added=timezone.now() - timedelta(minutes=30),
  66. )
  67. old_release.add_project(project)
  68. ReleaseCommit.objects.create(
  69. organization_id=project.organization_id,
  70. project_id=project.id,
  71. release=old_release,
  72. commit=Commit.objects.get(key="c7155651831549cf8a5e47889fce17eb"),
  73. order=0,
  74. )
  75. ReleaseHeadCommit.objects.create(
  76. organization_id=project.organization_id,
  77. repository_id=repo.id,
  78. release=old_release,
  79. commit=Commit.objects.get(key="c7155651831549cf8a5e47889fce17eb"),
  80. )
  81. release_heads = ReleaseHeadCommit.objects.filter(
  82. organization_id=project.organization_id,
  83. repository_id=repo.id,
  84. commit=Commit.objects.get(key="bbee5b51f84611e4b14834363b8514c2"),
  85. )
  86. assert len(release_heads) == 0
  87. hook = HerokuReleaseHook(project)
  88. hook.finish_release(version=version, owner_id=user.id)
  89. release = Release.objects.get(projects=project, version=version)
  90. new_release_heads = ReleaseHeadCommit.objects.filter(
  91. organization_id=project.organization_id,
  92. repository_id=repo.id,
  93. release=release,
  94. commit=Commit.objects.get(key="bbee5b51f84611e4b14834363b8514c2"),
  95. )
  96. assert len(new_release_heads) == 1
  97. assert release.version == "bbee5b51f84611e4b14834363b8514c2"
  98. deploy = Deploy.objects.filter(
  99. organization_id=project.organization_id,
  100. release=release,
  101. environment_id=Environment.objects.get(
  102. organization_id=project.organization_id, name="production"
  103. ).id,
  104. )
  105. assert len(deploy) == 1
  106. mock_fetch_commits.apply_async.assert_called_with(
  107. kwargs={
  108. "release_id": release.id,
  109. "user_id": user.id,
  110. "refs": [{"commit": "bbee5b51f84611e4b14834363b8514c2", "repository": repo.name}],
  111. "prev_release_id": old_release.id,
  112. }
  113. )
  114. @region_silo_test
  115. class HookHandleTest(TestCase):
  116. @pytest.fixture(autouse=True)
  117. def patch_is_valid_signature(self):
  118. with patch.object(HerokuReleaseHook, "is_valid_signature"):
  119. yield
  120. @patch.object(HerokuReleaseHook, "set_refs")
  121. def test_user_success(self, set_refs_mock):
  122. user = self.create_user()
  123. organization = self.create_organization(owner=user)
  124. project = self.create_project(organization=organization)
  125. hook = HerokuReleaseHook(project)
  126. req = Mock()
  127. body: dict[str, Any] = {
  128. "data": {
  129. "user": {"email": user.email},
  130. "slug": {"commit": "abcd123"},
  131. "app": {"name": "example"},
  132. },
  133. "action": "update",
  134. }
  135. req.body = bytes(json.dumps(body), "utf-8")
  136. hook.handle(req)
  137. assert Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
  138. assert set_refs_mock.call_count == 1
  139. @patch.object(HerokuReleaseHook, "set_refs")
  140. def test_only_run_on_update(self, set_refs_mock):
  141. user = self.create_user()
  142. organization = self.create_organization(owner=user)
  143. project = self.create_project(organization=organization)
  144. hook = HerokuReleaseHook(project)
  145. req = Mock()
  146. body: dict[str, Any] = {
  147. "data": {
  148. "user": {"email": user.email},
  149. "slug": {"commit": "abcd123"},
  150. "app": {"name": "example"},
  151. },
  152. "action": "create",
  153. }
  154. req.body = bytes(json.dumps(body), "utf-8")
  155. hook.handle(req)
  156. assert not Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
  157. assert set_refs_mock.call_count == 0
  158. @patch.object(HerokuReleaseHook, "set_refs")
  159. def test_actor_email_success(self, set_refs_mock):
  160. user = self.create_user()
  161. organization = self.create_organization(owner=user)
  162. project = self.create_project(organization=organization)
  163. hook = HerokuReleaseHook(project)
  164. req = Mock()
  165. body: dict[str, Any] = {
  166. "data": {
  167. "actor": {"email": user.email},
  168. "slug": {"commit": "abcd123"},
  169. "app": {"name": "example"},
  170. },
  171. "action": "update",
  172. }
  173. req.body = bytes(json.dumps(body), "utf-8")
  174. hook.handle(req)
  175. assert Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
  176. assert set_refs_mock.call_count == 1
  177. def test_email_mismatch(self):
  178. user = self.create_user()
  179. organization = self.create_organization(owner=user)
  180. project = self.create_project(organization=organization)
  181. hook = HerokuReleaseHook(project)
  182. req = Mock()
  183. body: dict[str, Any] = {
  184. "data": {
  185. "user": {"email": "wrong@example.com"},
  186. "slug": {"commit": "v999"},
  187. "app": {"name": "example"},
  188. },
  189. "action": "update",
  190. }
  191. req.body = bytes(json.dumps(body), "utf-8")
  192. hook.handle(req)
  193. assert Release.objects.filter(version=body["data"]["slug"]["commit"]).exists()
  194. def test_bad_version(self):
  195. project = self.create_project()
  196. user = self.create_user()
  197. hook = HerokuReleaseHook(project)
  198. req = Mock()
  199. body = {
  200. "data": {
  201. "actor": {"email": user.email},
  202. "slug": {"commit": ""},
  203. "app": {"name": "example"},
  204. },
  205. "action": "update",
  206. }
  207. req.body = bytes(json.dumps(body), "utf-8")
  208. with pytest.raises(HookValidationError):
  209. hook.handle(req)