You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

760 lines
27 KiB

6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
6 years ago
6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
6 years ago
6 years ago
5 years ago
5 years ago
  1. # coding: utf-8
  2. from datetime import timedelta as td
  3. import json
  4. from django.core import mail
  5. from django.utils.timezone import now
  6. from hc.api.models import Channel, Check, Notification
  7. from hc.test import BaseTestCase
  8. from mock import patch, Mock
  9. from requests.exceptions import ConnectionError, Timeout
  10. from django.test.utils import override_settings
  11. class NotifyTestCase(BaseTestCase):
  12. def _setup_data(self, kind, value, status="down", email_verified=True):
  13. self.check = Check(project=self.project)
  14. self.check.status = status
  15. self.check.last_ping = now() - td(minutes=61)
  16. self.check.save()
  17. self.channel = Channel(project=self.project)
  18. self.channel.kind = kind
  19. self.channel.value = value
  20. self.channel.email_verified = email_verified
  21. self.channel.save()
  22. self.channel.checks.add(self.check)
  23. @patch("hc.api.transports.requests.request")
  24. def test_webhook(self, mock_get):
  25. self._setup_data("webhook", "http://example")
  26. mock_get.return_value.status_code = 200
  27. self.channel.notify(self.check)
  28. mock_get.assert_called_with(
  29. "get",
  30. "http://example",
  31. headers={"User-Agent": "healthchecks.io"},
  32. timeout=5,
  33. )
  34. @patch("hc.api.transports.requests.request", side_effect=Timeout)
  35. def test_webhooks_handle_timeouts(self, mock_get):
  36. self._setup_data("webhook", "http://example")
  37. self.channel.notify(self.check)
  38. n = Notification.objects.get()
  39. self.assertEqual(n.error, "Connection timed out")
  40. @patch("hc.api.transports.requests.request", side_effect=ConnectionError)
  41. def test_webhooks_handle_connection_errors(self, mock_get):
  42. self._setup_data("webhook", "http://example")
  43. self.channel.notify(self.check)
  44. n = Notification.objects.get()
  45. self.assertEqual(n.error, "Connection failed")
  46. @patch("hc.api.transports.requests.request")
  47. def test_webhooks_ignore_up_events(self, mock_get):
  48. self._setup_data("webhook", "http://example", status="up")
  49. self.channel.notify(self.check)
  50. self.assertFalse(mock_get.called)
  51. self.assertEqual(Notification.objects.count(), 0)
  52. @patch("hc.api.transports.requests.request")
  53. def test_webhooks_handle_500(self, mock_get):
  54. self._setup_data("webhook", "http://example")
  55. mock_get.return_value.status_code = 500
  56. self.channel.notify(self.check)
  57. n = Notification.objects.get()
  58. self.assertEqual(n.error, "Received status code 500")
  59. @patch("hc.api.transports.requests.request")
  60. def test_webhooks_support_variables(self, mock_get):
  61. template = "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME"
  62. self._setup_data("webhook", template)
  63. self.check.name = "Hello World"
  64. self.check.tags = "foo bar"
  65. self.check.save()
  66. self.channel.notify(self.check)
  67. url = "http://host/%s/down/foo/bar/?name=Hello%%20World" % self.check.code
  68. args, kwargs = mock_get.call_args
  69. self.assertEqual(args[0], "get")
  70. self.assertEqual(args[1], url)
  71. self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"})
  72. self.assertEqual(kwargs["timeout"], 5)
  73. @patch("hc.api.transports.requests.request")
  74. def test_webhooks_handle_variable_variables(self, mock_get):
  75. self._setup_data("webhook", "http://host/$$NAMETAG1")
  76. self.check.tags = "foo bar"
  77. self.check.save()
  78. self.channel.notify(self.check)
  79. # $$NAMETAG1 should *not* get transformed to "foo"
  80. args, kwargs = mock_get.call_args
  81. self.assertEqual(args[1], "http://host/$TAG1")
  82. @patch("hc.api.transports.requests.request")
  83. def test_webhooks_support_post(self, mock_request):
  84. template = "http://example.com\n\nThe Time Is $NOW"
  85. self._setup_data("webhook", template)
  86. self.check.save()
  87. self.channel.notify(self.check)
  88. args, kwargs = mock_request.call_args
  89. self.assertEqual(args[0], "post")
  90. self.assertEqual(args[1], "http://example.com")
  91. # spaces should not have been urlencoded:
  92. payload = kwargs["data"].decode()
  93. self.assertTrue(payload.startswith("The Time Is 2"))
  94. @patch("hc.api.transports.requests.request")
  95. def test_webhooks_dollarsign_escaping(self, mock_get):
  96. # If name or tag contains what looks like a variable reference,
  97. # that should be left alone:
  98. template = "http://host/$NAME"
  99. self._setup_data("webhook", template)
  100. self.check.name = "$TAG1"
  101. self.check.tags = "foo"
  102. self.check.save()
  103. self.channel.notify(self.check)
  104. url = "http://host/%24TAG1"
  105. mock_get.assert_called_with(
  106. "get", url, headers={"User-Agent": "healthchecks.io"}, timeout=5
  107. )
  108. @patch("hc.api.transports.requests.request")
  109. def test_webhook_fires_on_up_event(self, mock_get):
  110. self._setup_data("webhook", "http://foo\nhttp://bar", status="up")
  111. self.channel.notify(self.check)
  112. mock_get.assert_called_with(
  113. "get", "http://bar", headers={"User-Agent": "healthchecks.io"}, timeout=5
  114. )
  115. @patch("hc.api.transports.requests.request")
  116. def test_webhooks_handle_unicode_post_body(self, mock_request):
  117. template = "http://example.com\n\n(╯°□°)╯︵ ┻━┻"
  118. self._setup_data("webhook", template)
  119. self.check.save()
  120. self.channel.notify(self.check)
  121. args, kwargs = mock_request.call_args
  122. # unicode should be encoded into utf-8
  123. self.assertIsInstance(kwargs["data"], bytes)
  124. @patch("hc.api.transports.requests.request")
  125. def test_webhooks_handle_json_value(self, mock_request):
  126. definition = {
  127. "method_down": "GET",
  128. "url_down": "http://foo.com",
  129. "body_down": "",
  130. "headers_down": {},
  131. }
  132. self._setup_data("webhook", json.dumps(definition))
  133. self.channel.notify(self.check)
  134. headers = {"User-Agent": "healthchecks.io"}
  135. mock_request.assert_called_with(
  136. "get", "http://foo.com", headers=headers, timeout=5
  137. )
  138. @patch("hc.api.transports.requests.request")
  139. def test_webhooks_handle_json_up_event(self, mock_request):
  140. definition = {
  141. "method_up": "GET",
  142. "url_up": "http://bar",
  143. "body_up": "",
  144. "headers_up": {},
  145. }
  146. self._setup_data("webhook", json.dumps(definition), status="up")
  147. self.channel.notify(self.check)
  148. headers = {"User-Agent": "healthchecks.io"}
  149. mock_request.assert_called_with("get", "http://bar", headers=headers, timeout=5)
  150. @patch("hc.api.transports.requests.request")
  151. def test_webhooks_handle_post_headers(self, mock_request):
  152. definition = {
  153. "method_down": "POST",
  154. "url_down": "http://foo.com",
  155. "body_down": "data",
  156. "headers_down": {"Content-Type": "application/json"},
  157. }
  158. self._setup_data("webhook", json.dumps(definition))
  159. self.channel.notify(self.check)
  160. headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
  161. mock_request.assert_called_with(
  162. "post", "http://foo.com", data=b"data", headers=headers, timeout=5
  163. )
  164. @patch("hc.api.transports.requests.request")
  165. def test_webhooks_handle_get_headers(self, mock_request):
  166. definition = {
  167. "method_down": "GET",
  168. "url_down": "http://foo.com",
  169. "body_down": "",
  170. "headers_down": {"Content-Type": "application/json"},
  171. }
  172. self._setup_data("webhook", json.dumps(definition))
  173. self.channel.notify(self.check)
  174. headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
  175. mock_request.assert_called_with(
  176. "get", "http://foo.com", headers=headers, timeout=5
  177. )
  178. @patch("hc.api.transports.requests.request")
  179. def test_webhooks_allow_user_agent_override(self, mock_request):
  180. definition = {
  181. "method_down": "GET",
  182. "url_down": "http://foo.com",
  183. "body_down": "",
  184. "headers_down": {"User-Agent": "My-Agent"},
  185. }
  186. self._setup_data("webhook", json.dumps(definition))
  187. self.channel.notify(self.check)
  188. headers = {"User-Agent": "My-Agent"}
  189. mock_request.assert_called_with(
  190. "get", "http://foo.com", headers=headers, timeout=5
  191. )
  192. @patch("hc.api.transports.requests.request")
  193. def test_webhooks_support_variables_in_headers(self, mock_request):
  194. definition = {
  195. "method_down": "GET",
  196. "url_down": "http://foo.com",
  197. "body_down": "",
  198. "headers_down": {"X-Message": "$NAME is DOWN"},
  199. }
  200. self._setup_data("webhook", json.dumps(definition))
  201. self.check.name = "Foo"
  202. self.check.save()
  203. self.channel.notify(self.check)
  204. headers = {"User-Agent": "healthchecks.io", "X-Message": "Foo is DOWN"}
  205. mock_request.assert_called_with(
  206. "get", "http://foo.com", headers=headers, timeout=5
  207. )
  208. def test_email(self):
  209. self._setup_data("email", "[email protected]")
  210. self.channel.notify(self.check)
  211. n = Notification.objects.get()
  212. self.assertEqual(n.error, "")
  213. # And email should have been sent
  214. self.assertEqual(len(mail.outbox), 1)
  215. email = mail.outbox[0]
  216. self.assertEqual(email.to[0], "[email protected]")
  217. self.assertTrue("X-Bounce-Url" in email.extra_headers)
  218. self.assertTrue("List-Unsubscribe" in email.extra_headers)
  219. self.assertTrue("List-Unsubscribe-Post" in email.extra_headers)
  220. def test_email_transport_handles_json_value(self):
  221. payload = {"value": "[email protected]", "up": True, "down": True}
  222. self._setup_data("email", json.dumps(payload))
  223. self.channel.notify(self.check)
  224. # And email should have been sent
  225. self.assertEqual(len(mail.outbox), 1)
  226. email = mail.outbox[0]
  227. self.assertEqual(email.to[0], "[email protected]")
  228. def test_it_skips_unverified_email(self):
  229. self._setup_data("email", "[email protected]", email_verified=False)
  230. self.channel.notify(self.check)
  231. # If an email is not verified, it should be skipped over
  232. # without logging a notification:
  233. self.assertEqual(Notification.objects.count(), 0)
  234. self.assertEqual(len(mail.outbox), 0)
  235. def test_email_checks_up_down_flags(self):
  236. payload = {"value": "[email protected]", "up": True, "down": False}
  237. self._setup_data("email", json.dumps(payload))
  238. self.channel.notify(self.check)
  239. # This channel should not notify on "down" events:
  240. self.assertEqual(Notification.objects.count(), 0)
  241. self.assertEqual(len(mail.outbox), 0)
  242. @patch("hc.api.transports.requests.request")
  243. def test_pd(self, mock_post):
  244. self._setup_data("pd", "123")
  245. mock_post.return_value.status_code = 200
  246. self.channel.notify(self.check)
  247. assert Notification.objects.count() == 1
  248. args, kwargs = mock_post.call_args
  249. payload = kwargs["json"]
  250. self.assertEqual(payload["event_type"], "trigger")
  251. self.assertEqual(payload["service_key"], "123")
  252. @patch("hc.api.transports.requests.request")
  253. def test_pd_complex(self, mock_post):
  254. self._setup_data("pd", json.dumps({"service_key": "456"}))
  255. mock_post.return_value.status_code = 200
  256. self.channel.notify(self.check)
  257. assert Notification.objects.count() == 1
  258. args, kwargs = mock_post.call_args
  259. payload = kwargs["json"]
  260. self.assertEqual(payload["event_type"], "trigger")
  261. self.assertEqual(payload["service_key"], "456")
  262. @patch("hc.api.transports.requests.request")
  263. def test_pagertree(self, mock_post):
  264. self._setup_data("pagertree", "123")
  265. mock_post.return_value.status_code = 200
  266. self.channel.notify(self.check)
  267. assert Notification.objects.count() == 1
  268. args, kwargs = mock_post.call_args
  269. payload = kwargs["json"]
  270. self.assertEqual(payload["event_type"], "trigger")
  271. @patch("hc.api.transports.requests.request")
  272. def test_pagerteam(self, mock_post):
  273. self._setup_data("pagerteam", "123")
  274. mock_post.return_value.status_code = 200
  275. self.channel.notify(self.check)
  276. assert Notification.objects.count() == 1
  277. args, kwargs = mock_post.call_args
  278. payload = kwargs["json"]
  279. self.assertEqual(payload["event_type"], "trigger")
  280. @patch("hc.api.transports.requests.request")
  281. def test_slack(self, mock_post):
  282. self._setup_data("slack", "123")
  283. mock_post.return_value.status_code = 200
  284. self.channel.notify(self.check)
  285. assert Notification.objects.count() == 1
  286. args, kwargs = mock_post.call_args
  287. payload = kwargs["json"]
  288. attachment = payload["attachments"][0]
  289. fields = {f["title"]: f["value"] for f in attachment["fields"]}
  290. self.assertEqual(fields["Last Ping"], "an hour ago")
  291. @patch("hc.api.transports.requests.request")
  292. def test_slack_with_complex_value(self, mock_post):
  293. v = json.dumps({"incoming_webhook": {"url": "123"}})
  294. self._setup_data("slack", v)
  295. mock_post.return_value.status_code = 200
  296. self.channel.notify(self.check)
  297. assert Notification.objects.count() == 1
  298. args, kwargs = mock_post.call_args
  299. self.assertEqual(args[1], "123")
  300. @patch("hc.api.transports.requests.request")
  301. def test_slack_handles_500(self, mock_post):
  302. self._setup_data("slack", "123")
  303. mock_post.return_value.status_code = 500
  304. self.channel.notify(self.check)
  305. n = Notification.objects.get()
  306. self.assertEqual(n.error, "Received status code 500")
  307. @patch("hc.api.transports.requests.request", side_effect=Timeout)
  308. def test_slack_handles_timeout(self, mock_post):
  309. self._setup_data("slack", "123")
  310. self.channel.notify(self.check)
  311. n = Notification.objects.get()
  312. self.assertEqual(n.error, "Connection timed out")
  313. @patch("hc.api.transports.requests.request")
  314. def test_slack_with_tabs_in_schedule(self, mock_post):
  315. self._setup_data("slack", "123")
  316. self.check.kind = "cron"
  317. self.check.schedule = "*\t* * * *"
  318. self.check.save()
  319. mock_post.return_value.status_code = 200
  320. self.channel.notify(self.check)
  321. self.assertEqual(Notification.objects.count(), 1)
  322. self.assertTrue(mock_post.called)
  323. @patch("hc.api.transports.requests.request")
  324. def test_hipchat(self, mock_post):
  325. self._setup_data("hipchat", "123")
  326. self.channel.notify(self.check)
  327. self.assertFalse(mock_post.called)
  328. self.assertEqual(Notification.objects.count(), 0)
  329. @patch("hc.api.transports.requests.request")
  330. def test_opsgenie_with_legacy_value(self, mock_post):
  331. self._setup_data("opsgenie", "123")
  332. mock_post.return_value.status_code = 202
  333. self.channel.notify(self.check)
  334. n = Notification.objects.first()
  335. self.assertEqual(n.error, "")
  336. self.assertEqual(mock_post.call_count, 1)
  337. args, kwargs = mock_post.call_args
  338. self.assertIn("api.opsgenie.com", args[1])
  339. payload = kwargs["json"]
  340. self.assertIn("DOWN", payload["message"])
  341. @patch("hc.api.transports.requests.request")
  342. def test_opsgenie_up(self, mock_post):
  343. self._setup_data("opsgenie", "123", status="up")
  344. mock_post.return_value.status_code = 202
  345. self.channel.notify(self.check)
  346. n = Notification.objects.first()
  347. self.assertEqual(n.error, "")
  348. self.assertEqual(mock_post.call_count, 1)
  349. args, kwargs = mock_post.call_args
  350. method, url = args
  351. self.assertTrue(str(self.check.code) in url)
  352. @patch("hc.api.transports.requests.request")
  353. def test_opsgenie_with_json_value(self, mock_post):
  354. self._setup_data("opsgenie", json.dumps({"key": "456", "region": "eu"}))
  355. mock_post.return_value.status_code = 202
  356. self.channel.notify(self.check)
  357. n = Notification.objects.first()
  358. self.assertEqual(n.error, "")
  359. self.assertEqual(mock_post.call_count, 1)
  360. args, kwargs = mock_post.call_args
  361. self.assertIn("api.eu.opsgenie.com", args[1])
  362. @patch("hc.api.transports.requests.request")
  363. def test_pushover(self, mock_post):
  364. self._setup_data("po", "123|0")
  365. mock_post.return_value.status_code = 200
  366. self.channel.notify(self.check)
  367. assert Notification.objects.count() == 1
  368. args, kwargs = mock_post.call_args
  369. payload = kwargs["data"]
  370. self.assertIn("DOWN", payload["title"])
  371. @patch("hc.api.transports.requests.request")
  372. def test_pushover_up_priority(self, mock_post):
  373. self._setup_data("po", "123|0|2", status="up")
  374. mock_post.return_value.status_code = 200
  375. self.channel.notify(self.check)
  376. assert Notification.objects.count() == 1
  377. args, kwargs = mock_post.call_args
  378. payload = kwargs["data"]
  379. self.assertIn("UP", payload["title"])
  380. self.assertEqual(payload["priority"], 2)
  381. self.assertIn("retry", payload)
  382. self.assertIn("expire", payload)
  383. @patch("hc.api.transports.requests.request")
  384. def test_victorops(self, mock_post):
  385. self._setup_data("victorops", "123")
  386. mock_post.return_value.status_code = 200
  387. self.channel.notify(self.check)
  388. assert Notification.objects.count() == 1
  389. args, kwargs = mock_post.call_args
  390. payload = kwargs["json"]
  391. self.assertEqual(payload["message_type"], "CRITICAL")
  392. @patch("hc.api.transports.requests.request")
  393. def test_discord(self, mock_post):
  394. v = json.dumps({"webhook": {"url": "123"}})
  395. self._setup_data("discord", v)
  396. mock_post.return_value.status_code = 200
  397. self.channel.notify(self.check)
  398. assert Notification.objects.count() == 1
  399. args, kwargs = mock_post.call_args
  400. payload = kwargs["json"]
  401. attachment = payload["attachments"][0]
  402. fields = {f["title"]: f["value"] for f in attachment["fields"]}
  403. self.assertEqual(fields["Last Ping"], "an hour ago")
  404. @patch("hc.api.transports.requests.request")
  405. def test_pushbullet(self, mock_post):
  406. self._setup_data("pushbullet", "fake-token")
  407. mock_post.return_value.status_code = 200
  408. self.channel.notify(self.check)
  409. assert Notification.objects.count() == 1
  410. _, kwargs = mock_post.call_args
  411. self.assertEqual(kwargs["json"]["type"], "note")
  412. self.assertEqual(kwargs["headers"]["Access-Token"], "fake-token")
  413. @patch("hc.api.transports.requests.request")
  414. def test_telegram(self, mock_post):
  415. v = json.dumps({"id": 123})
  416. self._setup_data("telegram", v)
  417. mock_post.return_value.status_code = 200
  418. self.channel.notify(self.check)
  419. assert Notification.objects.count() == 1
  420. args, kwargs = mock_post.call_args
  421. payload = kwargs["json"]
  422. self.assertEqual(payload["chat_id"], 123)
  423. self.assertTrue("The check" in payload["text"])
  424. @patch("hc.api.transports.requests.request")
  425. def test_sms(self, mock_post):
  426. self._setup_data("sms", "+1234567890")
  427. self.check.last_ping = now() - td(hours=2)
  428. mock_post.return_value.status_code = 200
  429. self.channel.notify(self.check)
  430. self.assertEqual(Notification.objects.count(), 1)
  431. args, kwargs = mock_post.call_args
  432. payload = kwargs["data"]
  433. self.assertEqual(payload["To"], "+1234567890")
  434. self.assertFalse("\xa0" in payload["Body"])
  435. # sent SMS counter should go up
  436. self.profile.refresh_from_db()
  437. self.assertEqual(self.profile.sms_sent, 1)
  438. @patch("hc.api.transports.requests.request")
  439. def test_sms_handles_json_value(self, mock_post):
  440. value = {"label": "foo", "value": "+1234567890"}
  441. self._setup_data("sms", json.dumps(value))
  442. self.check.last_ping = now() - td(hours=2)
  443. mock_post.return_value.status_code = 200
  444. self.channel.notify(self.check)
  445. assert Notification.objects.count() == 1
  446. args, kwargs = mock_post.call_args
  447. payload = kwargs["data"]
  448. self.assertEqual(payload["To"], "+1234567890")
  449. @patch("hc.api.transports.requests.request")
  450. def test_sms_limit(self, mock_post):
  451. # At limit already:
  452. self.profile.last_sms_date = now()
  453. self.profile.sms_sent = 50
  454. self.profile.save()
  455. self._setup_data("sms", "+1234567890")
  456. self.channel.notify(self.check)
  457. self.assertFalse(mock_post.called)
  458. n = Notification.objects.get()
  459. self.assertTrue("Monthly SMS limit exceeded" in n.error)
  460. # And email should have been sent
  461. self.assertEqual(len(mail.outbox), 1)
  462. email = mail.outbox[0]
  463. self.assertEqual(email.to[0], "[email protected]")
  464. self.assertEqual(email.subject, "Monthly SMS Limit Reached")
  465. @patch("hc.api.transports.requests.request")
  466. def test_sms_limit_reset(self, mock_post):
  467. # At limit, but also into a new month
  468. self.profile.sms_sent = 50
  469. self.profile.last_sms_date = now() - td(days=100)
  470. self.profile.save()
  471. self._setup_data("sms", "+1234567890")
  472. mock_post.return_value.status_code = 200
  473. self.channel.notify(self.check)
  474. self.assertTrue(mock_post.called)
  475. @patch("hc.api.transports.requests.request")
  476. def test_whatsapp(self, mock_post):
  477. definition = {"value": "+1234567890", "up": True, "down": True}
  478. self._setup_data("whatsapp", json.dumps(definition))
  479. self.check.last_ping = now() - td(hours=2)
  480. mock_post.return_value.status_code = 200
  481. self.channel.notify(self.check)
  482. self.assertEqual(Notification.objects.count(), 1)
  483. args, kwargs = mock_post.call_args
  484. payload = kwargs["data"]
  485. self.assertEqual(payload["To"], "whatsapp:+1234567890")
  486. # sent SMS counter should go up
  487. self.profile.refresh_from_db()
  488. self.assertEqual(self.profile.sms_sent, 1)
  489. @patch("hc.api.transports.requests.request")
  490. def test_whatsapp_obeys_up_down_flags(self, mock_post):
  491. definition = {"value": "+1234567890", "up": True, "down": False}
  492. self._setup_data("whatsapp", json.dumps(definition))
  493. self.check.last_ping = now() - td(hours=2)
  494. self.channel.notify(self.check)
  495. self.assertEqual(Notification.objects.count(), 0)
  496. self.assertFalse(mock_post.called)
  497. @patch("hc.api.transports.requests.request")
  498. def test_whatsapp_limit(self, mock_post):
  499. # At limit already:
  500. self.profile.last_sms_date = now()
  501. self.profile.sms_sent = 50
  502. self.profile.save()
  503. definition = {"value": "+1234567890", "up": True, "down": True}
  504. self._setup_data("whatsapp", json.dumps(definition))
  505. self.channel.notify(self.check)
  506. self.assertFalse(mock_post.called)
  507. n = Notification.objects.get()
  508. self.assertTrue("Monthly message limit exceeded" in n.error)
  509. # And email should have been sent
  510. self.assertEqual(len(mail.outbox), 1)
  511. email = mail.outbox[0]
  512. self.assertEqual(email.to[0], "[email protected]")
  513. self.assertEqual(email.subject, "Monthly WhatsApp Limit Reached")
  514. @patch("apprise.Apprise")
  515. @override_settings(APPRISE_ENABLED=True)
  516. def test_apprise_enabled(self, mock_apprise):
  517. self._setup_data("apprise", "123")
  518. mock_aobj = Mock()
  519. mock_aobj.add.return_value = True
  520. mock_aobj.notify.return_value = True
  521. mock_apprise.return_value = mock_aobj
  522. self.channel.notify(self.check)
  523. self.assertEqual(Notification.objects.count(), 1)
  524. self.check.status = "up"
  525. self.assertEqual(Notification.objects.count(), 1)
  526. @patch("apprise.Apprise")
  527. @override_settings(APPRISE_ENABLED=False)
  528. def test_apprise_disabled(self, mock_apprise):
  529. self._setup_data("apprise", "123")
  530. mock_aobj = Mock()
  531. mock_aobj.add.return_value = True
  532. mock_aobj.notify.return_value = True
  533. mock_apprise.return_value = mock_aobj
  534. self.channel.notify(self.check)
  535. self.assertEqual(Notification.objects.count(), 1)
  536. def test_not_implimented(self):
  537. self._setup_data("webhook", "http://example")
  538. self.channel.kind = "invalid"
  539. with self.assertRaises(NotImplementedError):
  540. self.channel.notify(self.check)
  541. @patch("hc.api.transports.requests.request")
  542. def test_msteams(self, mock_post):
  543. self._setup_data("msteams", "http://example.com/webhook")
  544. mock_post.return_value.status_code = 200
  545. self.channel.notify(self.check)
  546. assert Notification.objects.count() == 1
  547. args, kwargs = mock_post.call_args
  548. payload = kwargs["json"]
  549. self.assertEqual(payload["@type"], "MessageCard")
  550. @patch("hc.api.transports.os.system")
  551. @override_settings(SHELL_ENABLED=True)
  552. def test_shell(self, mock_system):
  553. definition = {"cmd_down": "logger hello", "cmd_up": ""}
  554. self._setup_data("shell", json.dumps(definition))
  555. mock_system.return_value = 0
  556. self.channel.notify(self.check)
  557. mock_system.assert_called_with("logger hello")
  558. @patch("hc.api.transports.os.system")
  559. @override_settings(SHELL_ENABLED=True)
  560. def test_shell_handles_nonzero_exit_code(self, mock_system):
  561. definition = {"cmd_down": "logger hello", "cmd_up": ""}
  562. self._setup_data("shell", json.dumps(definition))
  563. mock_system.return_value = 123
  564. self.channel.notify(self.check)
  565. n = Notification.objects.get()
  566. self.assertEqual(n.error, "Command returned exit code 123")
  567. @patch("hc.api.transports.os.system")
  568. @override_settings(SHELL_ENABLED=True)
  569. def test_shell_supports_variables(self, mock_system):
  570. definition = {"cmd_down": "logger $NAME is $STATUS ($TAG1)", "cmd_up": ""}
  571. self._setup_data("shell", json.dumps(definition))
  572. mock_system.return_value = 0
  573. self.check.name = "Database"
  574. self.check.tags = "foo bar"
  575. self.check.save()
  576. self.channel.notify(self.check)
  577. mock_system.assert_called_with("logger Database is down (foo)")
  578. @patch("hc.api.transports.os.system")
  579. @override_settings(SHELL_ENABLED=False)
  580. def test_shell_disabled(self, mock_system):
  581. definition = {"cmd_down": "logger hello", "cmd_up": ""}
  582. self._setup_data("shell", json.dumps(definition))
  583. self.channel.notify(self.check)
  584. self.assertFalse(mock_system.called)
  585. n = Notification.objects.get()
  586. self.assertEqual(n.error, "Shell commands are not enabled")