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.

176 lines
6.0 KiB

  1. from datetime import timedelta as td
  2. from io import StringIO
  3. from unittest.mock import Mock, patch
  4. from django.core.management import call_command
  5. from django.utils.timezone import now
  6. from hc.api.management.commands.sendalerts import Command, notify
  7. from hc.api.models import Flip, Check
  8. from hc.test import BaseTestCase
  9. class SendAlertsTestCase(BaseTestCase):
  10. def test_it_handles_grace_period(self):
  11. check = Check(project=self.project, status="up")
  12. # 1 day 30 minutes after ping the check is in grace period:
  13. check.last_ping = now() - td(days=1, minutes=30)
  14. check.alert_after = check.last_ping + td(days=1, hours=1)
  15. check.save()
  16. Command().handle_going_down()
  17. check.refresh_from_db()
  18. self.assertEqual(check.status, "up")
  19. self.assertEqual(Flip.objects.count(), 0)
  20. def test_it_creates_a_flip_when_check_goes_down(self):
  21. check = Check(project=self.project, status="up")
  22. check.last_ping = now() - td(days=2)
  23. check.alert_after = check.last_ping + td(days=1, hours=1)
  24. check.save()
  25. result = Command().handle_going_down()
  26. # If it finds work, it should return True
  27. self.assertTrue(result)
  28. # It should create a flip object
  29. flip = Flip.objects.get()
  30. self.assertEqual(flip.owner_id, check.id)
  31. self.assertEqual(flip.created, check.alert_after)
  32. self.assertEqual(flip.new_status, "down")
  33. # It should change stored status to "down", and clear out alert_after
  34. check.refresh_from_db()
  35. self.assertEqual(check.status, "down")
  36. self.assertEqual(check.alert_after, None)
  37. @patch("hc.api.management.commands.sendalerts.notify_on_thread")
  38. def test_it_processes_flip(self, mock_notify):
  39. check = Check(project=self.project, status="up")
  40. check.last_ping = now()
  41. check.alert_after = check.last_ping + td(days=1, hours=1)
  42. check.save()
  43. flip = Flip(owner=check, created=check.last_ping)
  44. flip.old_status = "down"
  45. flip.new_status = "up"
  46. flip.save()
  47. result = Command().process_one_flip()
  48. # If it finds work, it should return True
  49. self.assertTrue(result)
  50. # It should set the processed date
  51. flip.refresh_from_db()
  52. self.assertTrue(flip.processed)
  53. # It should call `notify_on_thread`
  54. self.assertTrue(mock_notify.called)
  55. @patch("hc.api.management.commands.sendalerts.notify_on_thread")
  56. def test_it_updates_alert_after(self, mock_notify):
  57. check = Check(project=self.project, status="up")
  58. check.last_ping = now() - td(hours=1)
  59. check.alert_after = check.last_ping
  60. check.save()
  61. result = Command().handle_going_down()
  62. # If it finds work, it should return True
  63. self.assertTrue(result)
  64. # alert_after should have been increased
  65. expected_aa = check.last_ping + td(days=1, hours=1)
  66. check.refresh_from_db()
  67. self.assertEqual(check.alert_after, expected_aa)
  68. # a flip should have not been created
  69. self.assertEqual(Flip.objects.count(), 0)
  70. @patch("hc.api.management.commands.sendalerts.notify")
  71. def test_it_works_synchronously(self, mock_notify):
  72. check = Check(project=self.project, status="up")
  73. check.last_ping = now() - td(days=2)
  74. check.alert_after = check.last_ping + td(days=1, hours=1)
  75. check.save()
  76. call_command("sendalerts", loop=False, use_threads=False, stdout=StringIO())
  77. # It should call `notify` instead of `notify_on_thread`
  78. self.assertTrue(mock_notify.called)
  79. def test_it_sets_next_nag_date(self):
  80. self.profile.nag_period = td(hours=1)
  81. self.profile.save()
  82. self.bobs_profile.nag_period = td(hours=1)
  83. self.bobs_profile.save()
  84. check = Check(project=self.project, status="down")
  85. check.last_ping = now() - td(days=2)
  86. check.save()
  87. flip = Flip(owner=check, created=check.last_ping)
  88. flip.old_status = "up"
  89. flip.new_status = "down"
  90. flip.save()
  91. notify(flip.id, Mock())
  92. # next_nag_gate should now be set for the project's owner
  93. self.profile.refresh_from_db()
  94. self.assertIsNotNone(self.profile.next_nag_date)
  95. # next_nag_gate should now be set for the project's members
  96. self.bobs_profile.refresh_from_db()
  97. self.assertIsNotNone(self.bobs_profile.next_nag_date)
  98. def test_it_clears_next_nag_date(self):
  99. self.profile.nag_period = td(hours=1)
  100. self.profile.next_nag_date = now() - td(minutes=30)
  101. self.profile.save()
  102. self.bobs_profile.nag_period = td(hours=1)
  103. self.bobs_profile.next_nag_date = now() - td(minutes=30)
  104. self.bobs_profile.save()
  105. check = Check(project=self.project, status="up")
  106. check.last_ping = now()
  107. check.save()
  108. flip = Flip(owner=check, created=check.last_ping)
  109. flip.old_status = "down"
  110. flip.new_status = "up"
  111. flip.save()
  112. notify(flip.id, Mock())
  113. # next_nag_gate should now be cleared out for the project's owner
  114. self.profile.refresh_from_db()
  115. self.assertIsNone(self.profile.next_nag_date)
  116. # next_nag_gate should now be cleared out for the project's members
  117. self.bobs_profile.refresh_from_db()
  118. self.assertIsNone(self.bobs_profile.next_nag_date)
  119. def test_it_does_not_touch_already_set_next_nag_dates(self):
  120. original_nag_date = now() - td(minutes=30)
  121. self.profile.nag_period = td(hours=1)
  122. self.profile.next_nag_date = original_nag_date
  123. self.profile.save()
  124. check = Check(project=self.project, status="down")
  125. check.last_ping = now() - td(days=2)
  126. check.save()
  127. flip = Flip(owner=check, created=check.last_ping)
  128. flip.old_status = "up"
  129. flip.new_status = "down"
  130. flip.save()
  131. notify(flip.id, Mock())
  132. self.profile.refresh_from_db()
  133. self.assertEqual(self.profile.next_nag_date, original_nag_date)