test_webhooks.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import
  3. import six
  4. from datetime import datetime
  5. from django.utils import timezone
  6. from sentry.models import (
  7. Commit,
  8. CommitAuthor,
  9. Integration,
  10. OrganizationOption,
  11. PullRequest,
  12. Repository,
  13. )
  14. from sentry.testutils import APITestCase
  15. from uuid import uuid4
  16. from sentry_plugins.github.testutils import (
  17. INSTALLATION_EVENT_EXAMPLE,
  18. INSTALLATION_REPO_EVENT,
  19. PUSH_EVENT_EXAMPLE,
  20. PUSH_EVENT_EXAMPLE_INSTALLATION,
  21. PULL_REQUEST_OPENED_EVENT_EXAMPLE,
  22. PULL_REQUEST_EDITED_EVENT_EXAMPLE,
  23. PULL_REQUEST_CLOSED_EVENT_EXAMPLE,
  24. )
  25. class WebhookTest(APITestCase):
  26. def test_get(self):
  27. project = self.project # force creation
  28. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  29. response = self.client.get(url)
  30. assert response.status_code == 405
  31. def test_unregistered_event(self):
  32. project = self.project # force creation
  33. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  34. secret = "b3002c3e321d4b7880360d397db2ccfd"
  35. OrganizationOption.objects.set_value(
  36. organization=project.organization, key="github:webhook_secret", value=secret
  37. )
  38. response = self.client.post(
  39. path=url,
  40. data=PUSH_EVENT_EXAMPLE,
  41. content_type="application/json",
  42. HTTP_X_GITHUB_EVENT="UnregisteredEvent",
  43. HTTP_X_HUB_SIGNATURE="sha1=98196e70369945ffa6b248cf70f7dc5e46dff241",
  44. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  45. )
  46. assert response.status_code == 204
  47. def test_invalid_signature_event(self):
  48. project = self.project # force creation
  49. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  50. secret = "2d7565c3537847b789d6995dca8d9f84"
  51. OrganizationOption.objects.set_value(
  52. organization=project.organization, key="github:webhook_secret", value=secret
  53. )
  54. response = self.client.post(
  55. path=url,
  56. data=PUSH_EVENT_EXAMPLE,
  57. content_type="application/json",
  58. HTTP_X_GITHUB_EVENT="push",
  59. HTTP_X_HUB_SIGNATURE="sha1=33521abeaaf9a57c2abf486e0ccd54d23cf36fec",
  60. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  61. )
  62. assert response.status_code == 401
  63. class PushEventWebhookTest(APITestCase):
  64. def test_simple(self):
  65. project = self.project # force creation
  66. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  67. secret = "b3002c3e321d4b7880360d397db2ccfd"
  68. OrganizationOption.objects.set_value(
  69. organization=project.organization, key="github:webhook_secret", value=secret
  70. )
  71. Repository.objects.create(
  72. organization_id=project.organization.id,
  73. external_id="35129377",
  74. provider="github",
  75. name="baxterthehacker/public-repo",
  76. )
  77. response = self.client.post(
  78. path=url,
  79. data=PUSH_EVENT_EXAMPLE,
  80. content_type="application/json",
  81. HTTP_X_GITHUB_EVENT="push",
  82. HTTP_X_HUB_SIGNATURE="sha1=98196e70369945ffa6b248cf70f7dc5e46dff241",
  83. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  84. )
  85. assert response.status_code == 204
  86. commit_list = list(
  87. Commit.objects.filter(organization_id=project.organization_id)
  88. .select_related("author")
  89. .order_by("-date_added")
  90. )
  91. assert len(commit_list) == 2
  92. commit = commit_list[0]
  93. assert commit.key == "133d60480286590a610a0eb7352ff6e02b9674c4"
  94. assert commit.message == u"Update README.md (àgain)"
  95. assert commit.author.name == u"bàxterthehacker"
  96. assert commit.author.email == "baxterthehacker@users.noreply.github.com"
  97. assert commit.author.external_id is None
  98. assert commit.date_added == datetime(2015, 5, 5, 23, 45, 15, tzinfo=timezone.utc)
  99. commit = commit_list[1]
  100. assert commit.key == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"
  101. assert commit.message == "Update README.md"
  102. assert commit.author.name == u"bàxterthehacker"
  103. assert commit.author.email == "baxterthehacker@users.noreply.github.com"
  104. assert commit.author.external_id is None
  105. assert commit.date_added == datetime(2015, 5, 5, 23, 40, 15, tzinfo=timezone.utc)
  106. def test_anonymous_lookup(self):
  107. project = self.project # force creation
  108. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  109. secret = "b3002c3e321d4b7880360d397db2ccfd"
  110. OrganizationOption.objects.set_value(
  111. organization=project.organization, key="github:webhook_secret", value=secret
  112. )
  113. Repository.objects.create(
  114. organization_id=project.organization.id,
  115. external_id="35129377",
  116. provider="github",
  117. name="baxterthehacker/public-repo",
  118. )
  119. CommitAuthor.objects.create(
  120. external_id="github:baxterthehacker",
  121. organization_id=project.organization_id,
  122. email="baxterthehacker@example.com",
  123. name=u"bàxterthehacker",
  124. )
  125. response = self.client.post(
  126. path=url,
  127. data=PUSH_EVENT_EXAMPLE,
  128. content_type="application/json",
  129. HTTP_X_GITHUB_EVENT="push",
  130. HTTP_X_HUB_SIGNATURE="sha1=98196e70369945ffa6b248cf70f7dc5e46dff241",
  131. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  132. )
  133. assert response.status_code == 204
  134. commit_list = list(
  135. Commit.objects.filter(organization_id=project.organization_id)
  136. .select_related("author")
  137. .order_by("-date_added")
  138. )
  139. # should be skipping the #skipsentry commit
  140. assert len(commit_list) == 2
  141. commit = commit_list[0]
  142. assert commit.key == "133d60480286590a610a0eb7352ff6e02b9674c4"
  143. assert commit.message == u"Update README.md (àgain)"
  144. assert commit.author.name == u"bàxterthehacker"
  145. assert commit.author.email == "baxterthehacker@example.com"
  146. assert commit.date_added == datetime(2015, 5, 5, 23, 45, 15, tzinfo=timezone.utc)
  147. commit = commit_list[1]
  148. assert commit.key == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"
  149. assert commit.message == "Update README.md"
  150. assert commit.author.name == u"bàxterthehacker"
  151. assert commit.author.email == "baxterthehacker@example.com"
  152. assert commit.date_added == datetime(2015, 5, 5, 23, 40, 15, tzinfo=timezone.utc)
  153. class InstallationPushEventWebhookTest(APITestCase):
  154. def test_simple(self):
  155. project = self.project # force creation
  156. url = "/plugins/github/installations/webhook/"
  157. inst = Integration.objects.create(
  158. provider="github_apps", external_id="12345", name="dummyorg"
  159. )
  160. inst.add_organization(self.project.organization)
  161. Repository.objects.create(
  162. organization_id=project.organization.id,
  163. external_id="35129377",
  164. provider="github_apps",
  165. name="baxterthehacker/public-repo",
  166. )
  167. response = self.client.post(
  168. path=url,
  169. data=PUSH_EVENT_EXAMPLE_INSTALLATION,
  170. content_type="application/json",
  171. HTTP_X_GITHUB_EVENT="push",
  172. HTTP_X_HUB_SIGNATURE="sha1=56a3df597e02adbc17fb617502c70e19d96a6136",
  173. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  174. )
  175. assert response.status_code == 204
  176. commit_list = list(
  177. Commit.objects.filter(organization_id=project.organization_id)
  178. .select_related("author")
  179. .order_by("-date_added")
  180. )
  181. assert len(commit_list) == 2
  182. commit = commit_list[0]
  183. assert commit.key == "133d60480286590a610a0eb7352ff6e02b9674c4"
  184. assert commit.message == u"Update README.md (àgain)"
  185. assert commit.author.name == u"bàxterthehacker"
  186. assert commit.author.email == "baxterthehacker@users.noreply.github.com"
  187. assert commit.author.external_id is None
  188. assert commit.date_added == datetime(2015, 5, 5, 23, 45, 15, tzinfo=timezone.utc)
  189. commit = commit_list[1]
  190. assert commit.key == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"
  191. assert commit.message == "Update README.md"
  192. assert commit.author.name == u"bàxterthehacker"
  193. assert commit.author.email == "baxterthehacker@users.noreply.github.com"
  194. assert commit.author.external_id is None
  195. assert commit.date_added == datetime(2015, 5, 5, 23, 40, 15, tzinfo=timezone.utc)
  196. class InstallationInstallEventWebhookTest(APITestCase):
  197. def test_simple(self):
  198. url = "/plugins/github/installations/webhook/"
  199. response = self.client.post(
  200. path=url,
  201. data=INSTALLATION_EVENT_EXAMPLE,
  202. content_type="application/json",
  203. HTTP_X_GITHUB_EVENT="installation",
  204. HTTP_X_HUB_SIGNATURE="sha1=348e46312df2901e8cb945616ee84ce30d9987c9",
  205. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  206. )
  207. assert response.status_code == 204
  208. assert Integration.objects.filter(
  209. provider="github_apps", external_id=2, name="octocat"
  210. ).exists()
  211. class InstallationRepoInstallEventWebhookTest(APITestCase):
  212. def test_simple(self):
  213. project = self.project # force creation
  214. url = "/plugins/github/installations/webhook/"
  215. integration = Integration.objects.create(
  216. provider="github_apps", external_id="2", name="octocat"
  217. )
  218. integration.add_organization(project.organization)
  219. response = self.client.post(
  220. path=url,
  221. data=INSTALLATION_REPO_EVENT,
  222. content_type="application/json",
  223. HTTP_X_GITHUB_EVENT="installation_repositories",
  224. HTTP_X_HUB_SIGNATURE="sha1=6899797a97dc5bb6aab3af927e92e881d03a3bd2",
  225. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  226. )
  227. assert response.status_code == 204
  228. assert Repository.objects.filter(
  229. provider="github",
  230. name="octocat/Hello-World",
  231. external_id=1296269,
  232. organization_id=project.organization_id,
  233. ).exists()
  234. def test_updates_existing_repo(self):
  235. project = self.project # force creation
  236. url = "/plugins/github/installations/webhook/"
  237. integration = Integration.objects.create(
  238. provider="github_apps", external_id="2", name="octocat"
  239. )
  240. integration.add_organization(project.organization)
  241. repo = Repository.objects.create(
  242. provider="github",
  243. name="octocat/Hello-World",
  244. external_id=1296269,
  245. organization_id=project.organization_id,
  246. )
  247. assert "name" not in repo.config
  248. response = self.client.post(
  249. path=url,
  250. data=INSTALLATION_REPO_EVENT,
  251. content_type="application/json",
  252. HTTP_X_GITHUB_EVENT="installation_repositories",
  253. HTTP_X_HUB_SIGNATURE="sha1=6899797a97dc5bb6aab3af927e92e881d03a3bd2",
  254. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  255. )
  256. assert response.status_code == 204
  257. repo = Repository.objects.get(id=repo.id)
  258. assert repo.integration_id == integration.id
  259. assert repo.config["name"] == repo.name
  260. class PullRequestEventWebhook(APITestCase):
  261. def test_opened(self):
  262. project = self.project # force creation
  263. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  264. secret = "b3002c3e321d4b7880360d397db2ccfd"
  265. OrganizationOption.objects.set_value(
  266. organization=project.organization, key="github:webhook_secret", value=secret
  267. )
  268. repo = Repository.objects.create(
  269. organization_id=project.organization.id,
  270. external_id="35129377",
  271. provider="github_apps",
  272. name="baxterthehacker/public-repo",
  273. )
  274. response = self.client.post(
  275. path=url,
  276. data=PULL_REQUEST_OPENED_EVENT_EXAMPLE,
  277. content_type="application/json",
  278. HTTP_X_GITHUB_EVENT="pull_request",
  279. HTTP_X_HUB_SIGNATURE="sha1=aa5b11bc52b9fac082cb59f9ee8667cb222c3aff",
  280. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  281. )
  282. assert response.status_code == 204
  283. prs = PullRequest.objects.filter(
  284. repository_id=repo.id, organization_id=project.organization.id
  285. )
  286. assert len(prs) == 1
  287. pr = prs[0]
  288. assert pr.key == "1"
  289. assert pr.message == u"This is a pretty simple change that we need to pull into master."
  290. assert pr.title == u"Update the README with new information"
  291. assert pr.author.name == u"baxterthehacker"
  292. def test_edited(self):
  293. project = self.project # force creation
  294. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  295. secret = "b3002c3e321d4b7880360d397db2ccfd"
  296. OrganizationOption.objects.set_value(
  297. organization=project.organization, key="github:webhook_secret", value=secret
  298. )
  299. repo = Repository.objects.create(
  300. organization_id=project.organization.id,
  301. external_id="35129377",
  302. provider="github_apps",
  303. name="baxterthehacker/public-repo",
  304. )
  305. pr = PullRequest.objects.create(
  306. key="1", repository_id=repo.id, organization_id=project.organization.id
  307. )
  308. response = self.client.post(
  309. path=url,
  310. data=PULL_REQUEST_EDITED_EVENT_EXAMPLE,
  311. content_type="application/json",
  312. HTTP_X_GITHUB_EVENT="pull_request",
  313. HTTP_X_HUB_SIGNATURE="sha1=b50a13afd33b514e8e62e603827ea62530f0690e",
  314. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  315. )
  316. assert response.status_code == 204
  317. pr = PullRequest.objects.get(id=pr.id)
  318. assert pr.key == "1"
  319. assert pr.message == u"new edited body"
  320. assert pr.title == u"new edited title"
  321. assert pr.author.name == u"baxterthehacker"
  322. def test_closed(self):
  323. project = self.project # force creation
  324. url = "/plugins/github/organizations/{}/webhook/".format(project.organization.id)
  325. secret = "b3002c3e321d4b7880360d397db2ccfd"
  326. OrganizationOption.objects.set_value(
  327. organization=project.organization, key="github:webhook_secret", value=secret
  328. )
  329. repo = Repository.objects.create(
  330. organization_id=project.organization.id,
  331. external_id="35129377",
  332. provider="github_apps",
  333. name="baxterthehacker/public-repo",
  334. )
  335. response = self.client.post(
  336. path=url,
  337. data=PULL_REQUEST_CLOSED_EVENT_EXAMPLE,
  338. content_type="application/json",
  339. HTTP_X_GITHUB_EVENT="pull_request",
  340. HTTP_X_HUB_SIGNATURE="sha1=dff1c803cf1e48c1b9aefe4a17952ea132758806",
  341. HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
  342. )
  343. assert response.status_code == 204
  344. prs = PullRequest.objects.filter(
  345. repository_id=repo.id, organization_id=project.organization.id
  346. )
  347. assert len(prs) == 1
  348. pr = prs[0]
  349. assert pr.key == "1"
  350. assert pr.message == u"new closed body"
  351. assert pr.title == u"new closed title"
  352. assert pr.author.name == u"baxterthehacker"
  353. assert pr.merge_commit_sha == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"