test_webhooks.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import json
  2. from datetime import datetime
  3. from unittest import mock
  4. from model_bakery import baker
  5. from apps.issue_events.constants import LogLevel
  6. from apps.uptime.constants import MonitorType
  7. from apps.uptime.models import Monitor, MonitorCheck
  8. from apps.uptime.webhooks import send_uptime_as_webhook
  9. from glitchtip.test_utils.test_case import GlitchTipTestCase
  10. from ..constants import RecipientType
  11. from ..models import AlertRecipient, Notification
  12. from ..tasks import process_event_alerts
  13. from ..webhooks import (
  14. send_issue_as_discord_webhook,
  15. send_issue_as_googlechat_webhook,
  16. send_issue_as_webhook,
  17. send_webhook,
  18. )
  19. TEST_URL = "https://burkesoftware.rocket.chat/hooks/Y8TttGY7RvN7Qm3gD/rqhHLiRSvYRZ8BhbhhhLYumdMksWnyj3Dqsqt8QKrmbNndXH"
  20. DISCORD_TEST_URL = "https://discord.com/api/webhooks/not_real_id/not_real_token"
  21. GOOGLE_CHAT_TEST_URL = "https://chat.googleapis.com/v1/spaces/space_id/messages?key=api_key&token=api_token"
  22. class WebhookTestCase(GlitchTipTestCase):
  23. def setUp(self):
  24. self.environment_name = "test-environment"
  25. self.release_name = "test-release"
  26. self.create_user_and_project()
  27. self.monitor = baker.make(
  28. Monitor,
  29. name="Example Monitor",
  30. url="https://example.com",
  31. monitor_type=MonitorType.GET,
  32. project=self.project,
  33. )
  34. self.monitor_check = baker.make(MonitorCheck, monitor=self.monitor)
  35. self.expected_subject = "GlitchTip Uptime Alert"
  36. self.expected_message_down = "The monitored site has gone down."
  37. self.expected_message_up = "The monitored site is back up."
  38. def generate_issue_with_tags(self):
  39. key_environment = baker.make("issue_events.TagKey", key="environment")
  40. environment_value = baker.make(
  41. "issue_events.TagValue", value=self.environment_name
  42. )
  43. key_release = baker.make("issue_events.TagKey", key="release")
  44. release_value = baker.make("issue_events.TagValue", value=self.release_name)
  45. issue = baker.make("issue_events.Issue", level=LogLevel.ERROR)
  46. baker.make(
  47. "issue_events.IssueTag",
  48. issue=issue,
  49. tag_key=key_environment,
  50. tag_value=environment_value,
  51. )
  52. baker.make(
  53. "issue_events.IssueTag",
  54. issue=issue,
  55. tag_key=key_release,
  56. tag_value=release_value,
  57. )
  58. return issue
  59. @mock.patch("requests.post")
  60. def test_send_webhook(self, mock_post):
  61. send_webhook(
  62. TEST_URL,
  63. "from unit test",
  64. )
  65. mock_post.assert_called_once()
  66. @mock.patch("requests.post")
  67. def test_send_issue_as_webhook(self, mock_post):
  68. issue = self.generate_issue_with_tags()
  69. issue2 = baker.make("issue_events.Issue", level=LogLevel.ERROR, short_id=2)
  70. issue3 = baker.make("issue_events.Issue", level=LogLevel.NOTSET)
  71. send_issue_as_webhook(TEST_URL, [issue, issue2, issue3], 3)
  72. mock_post.assert_called_once()
  73. first_issue_json_data = json.dumps(
  74. mock_post.call_args.kwargs["json"]["attachments"][0]
  75. )
  76. self.assertIn(
  77. f'"title": "Environment", "value": "{self.environment_name}"',
  78. first_issue_json_data,
  79. )
  80. self.assertIn(
  81. f'"title": "Release", "value": "{self.release_name}"', first_issue_json_data
  82. )
  83. @mock.patch("requests.post")
  84. def test_trigger_webhook(self, mock_post):
  85. project = baker.make("projects.Project")
  86. alert = baker.make(
  87. "alerts.ProjectAlert",
  88. project=project,
  89. timespan_minutes=1,
  90. quantity=2,
  91. )
  92. baker.make(
  93. "alerts.AlertRecipient",
  94. alert=alert,
  95. recipient_type=RecipientType.GENERAL_WEBHOOK,
  96. url="example.com",
  97. )
  98. issue = baker.make("issue_events.Issue", project=project)
  99. baker.make("issue_events.IssueEvent", issue=issue)
  100. process_event_alerts()
  101. self.assertEqual(Notification.objects.count(), 0)
  102. baker.make("issue_events.IssueEvent", issue=issue)
  103. process_event_alerts()
  104. self.assertEqual(
  105. Notification.objects.filter(
  106. project_alert__alertrecipient__recipient_type=RecipientType.GENERAL_WEBHOOK
  107. ).count(),
  108. 1,
  109. )
  110. mock_post.assert_called_once()
  111. self.assertIn(
  112. issue.title, mock_post.call_args[1]["json"]["sections"][0]["activityTitle"]
  113. )
  114. @mock.patch("requests.post")
  115. def test_send_issue_with_tags_as_discord_webhook(self, mock_post):
  116. issue = self.generate_issue_with_tags()
  117. send_issue_as_discord_webhook(DISCORD_TEST_URL, [issue])
  118. mock_post.assert_called_once()
  119. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  120. self.assertIn(
  121. f'"name": "Environment", "value": "{self.environment_name}"', json_data
  122. )
  123. self.assertIn(f'"name": "Release", "value": "{self.release_name}"', json_data)
  124. @mock.patch("requests.post")
  125. def test_send_issue_with_tags_as_googlechat_webhook(self, mock_post):
  126. issue = self.generate_issue_with_tags()
  127. send_issue_as_googlechat_webhook(GOOGLE_CHAT_TEST_URL, [issue])
  128. mock_post.assert_called_once()
  129. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  130. self.assertIn(
  131. f'"topLabel": "Release", "text": "{self.release_name}"', json_data
  132. )
  133. self.assertIn(
  134. f'"topLabel": "Environment", "text": "{self.environment_name}"',
  135. json_data,
  136. )
  137. @mock.patch("requests.post")
  138. def test_send_uptime_events_generic_webhook(self, mock_post):
  139. recipient = baker.make(
  140. AlertRecipient, recipient_type=RecipientType.GENERAL_WEBHOOK, url=TEST_URL
  141. )
  142. send_uptime_as_webhook(
  143. recipient,
  144. self.monitor_check.id,
  145. True,
  146. datetime.now(),
  147. )
  148. mock_post.assert_called_once()
  149. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  150. self.assertIn(f'"text": "{self.expected_subject}"', json_data)
  151. self.assertIn(f'"title": "{self.monitor.name}"', json_data)
  152. self.assertIn(f'"text": "{self.expected_message_down}"', json_data)
  153. mock_post.reset_mock()
  154. send_uptime_as_webhook(
  155. recipient,
  156. self.monitor_check.id,
  157. False,
  158. datetime.now(),
  159. )
  160. mock_post.assert_called_once()
  161. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  162. self.assertIn(f'"text": "{self.expected_subject}"', json_data)
  163. self.assertIn(f'"title": "{self.monitor.name}"', json_data)
  164. self.assertIn(f'"text": "{self.expected_message_up}"', json_data)
  165. @mock.patch("requests.post")
  166. def test_send_uptime_events_google_chat_webhook(self, mock_post):
  167. recipient = baker.make(
  168. AlertRecipient,
  169. recipient_type=RecipientType.GOOGLE_CHAT,
  170. url=GOOGLE_CHAT_TEST_URL,
  171. )
  172. send_uptime_as_webhook(
  173. recipient,
  174. self.monitor_check.id,
  175. True,
  176. datetime.now(),
  177. )
  178. mock_post.assert_called_once()
  179. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  180. self.assertIn(
  181. f'"title": "{self.expected_subject}", "subtitle": "{self.monitor.name}"',
  182. json_data,
  183. )
  184. self.assertIn(f'"text": "{self.expected_message_down}"', json_data)
  185. mock_post.reset_mock()
  186. send_uptime_as_webhook(
  187. recipient,
  188. self.monitor_check.id,
  189. False,
  190. datetime.now(),
  191. )
  192. mock_post.assert_called_once()
  193. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  194. self.assertIn(
  195. f'"title": "{self.expected_subject}", "subtitle": "{self.monitor.name}"',
  196. json_data,
  197. )
  198. self.assertIn(f'"text": "{self.expected_message_up}"', json_data)
  199. @mock.patch("requests.post")
  200. def test_send_uptime_events_discord_webhook(self, mock_post):
  201. recipient = baker.make(
  202. AlertRecipient, recipient_type=RecipientType.DISCORD, url=DISCORD_TEST_URL
  203. )
  204. send_uptime_as_webhook(
  205. recipient,
  206. self.monitor_check.id,
  207. True,
  208. datetime.now(),
  209. )
  210. mock_post.assert_called_once()
  211. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  212. self.assertIn(f'"content": "{self.expected_subject}"', json_data)
  213. self.assertIn(
  214. f'"title": "{self.monitor.name}", "description": "{self.expected_message_down}"',
  215. json_data,
  216. )
  217. mock_post.reset_mock()
  218. send_uptime_as_webhook(
  219. recipient,
  220. self.monitor_check.id,
  221. False,
  222. datetime.now(),
  223. )
  224. mock_post.assert_called_once()
  225. json_data = json.dumps(mock_post.call_args.kwargs["json"])
  226. self.assertIn(f'"content": "{self.expected_subject}"', json_data)
  227. self.assertIn(
  228. f'"title": "{self.monitor.name}", "description": "{self.expected_message_up}"',
  229. json_data,
  230. )