test_weekly_escalating_forecast.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from datetime import datetime
  2. from typing import List
  3. from unittest.mock import MagicMock, patch
  4. import pytz
  5. from sentry.issues.escalating_group_forecast import (
  6. DEFAULT_MINIMUM_CEILING_FORECAST,
  7. EscalatingGroupForecast,
  8. )
  9. from sentry.models.group import Group, GroupStatus
  10. from sentry.models.project import Project
  11. from sentry.tasks.weekly_escalating_forecast import run_escalating_forecast
  12. from sentry.testutils.cases import APITestCase, SnubaTestCase
  13. from sentry.types.group import GroupSubStatus
  14. from tests.sentry.issues.test_utils import get_mock_groups_past_counts_response
  15. class TestWeeklyEscalatingForecast(APITestCase, SnubaTestCase): # type: ignore
  16. def create_archived_until_escalating_groups(self, num_groups: int) -> List[Group]:
  17. group_list = []
  18. project_1 = Project.objects.get(id=1)
  19. for i in range(num_groups):
  20. group = self.create_group(project=project_1)
  21. group.status = GroupStatus.IGNORED
  22. group.substatus = GroupSubStatus.UNTIL_ESCALATING
  23. group.save()
  24. group_list.append(group)
  25. return group_list
  26. @patch("sentry.analytics.record")
  27. @patch("sentry.issues.escalating.query_groups_past_counts")
  28. def test_empty_escalating_forecast(
  29. self, mock_query_groups_past_counts: MagicMock, record_mock: MagicMock
  30. ) -> None:
  31. with self.tasks():
  32. group_list = self.create_archived_until_escalating_groups(num_groups=1)
  33. mock_query_groups_past_counts.return_value = {}
  34. run_escalating_forecast()
  35. fetched_forecast = EscalatingGroupForecast.fetch(
  36. group_list[0].project.id, group_list[0].id
  37. )
  38. assert fetched_forecast is not None
  39. assert fetched_forecast.project_id == group_list[0].project.id
  40. assert fetched_forecast.group_id == group_list[0].id
  41. assert fetched_forecast.forecast == DEFAULT_MINIMUM_CEILING_FORECAST
  42. record_mock.assert_called_with("issue_forecasts.saved", num_groups=0)
  43. @patch("sentry.analytics.record")
  44. @patch("sentry.issues.forecasts.query_groups_past_counts")
  45. def test_single_group_escalating_forecast(
  46. self, mock_query_groups_past_counts: MagicMock, record_mock: MagicMock
  47. ) -> None:
  48. with self.tasks():
  49. group_list = self.create_archived_until_escalating_groups(num_groups=1)
  50. mock_query_groups_past_counts.return_value = get_mock_groups_past_counts_response(
  51. num_days=7, num_hours=1, groups=group_list
  52. )
  53. run_escalating_forecast()
  54. approximate_date_added = datetime.now(pytz.utc)
  55. fetched_forecast = EscalatingGroupForecast.fetch(
  56. group_list[0].project.id, group_list[0].id
  57. )
  58. assert fetched_forecast is not None
  59. assert fetched_forecast.project_id == group_list[0].project.id
  60. assert fetched_forecast.group_id == group_list[0].id
  61. assert fetched_forecast.forecast == DEFAULT_MINIMUM_CEILING_FORECAST
  62. assert fetched_forecast.date_added.replace(
  63. second=0, microsecond=0
  64. ) == approximate_date_added.replace(second=0, microsecond=0)
  65. assert fetched_forecast.date_added < approximate_date_added
  66. record_mock.assert_called_with("issue_forecasts.saved", num_groups=1)
  67. @patch("sentry.analytics.record")
  68. @patch("sentry.issues.forecasts.query_groups_past_counts")
  69. def test_multiple_groups_escalating_forecast(
  70. self, mock_query_groups_past_counts: MagicMock, record_mock: MagicMock
  71. ) -> None:
  72. with self.tasks():
  73. group_list = self.create_archived_until_escalating_groups(num_groups=3)
  74. mock_query_groups_past_counts.return_value = get_mock_groups_past_counts_response(
  75. num_days=7, num_hours=23, groups=group_list
  76. )
  77. run_escalating_forecast()
  78. approximate_date_added = datetime.now(pytz.utc)
  79. for i in range(len(group_list)):
  80. fetched_forecast = EscalatingGroupForecast.fetch(
  81. group_list[i].project.id, group_list[i].id
  82. )
  83. assert fetched_forecast is not None
  84. assert fetched_forecast.project_id == group_list[i].project.id
  85. assert fetched_forecast.group_id == group_list[i].id
  86. assert fetched_forecast.forecast == DEFAULT_MINIMUM_CEILING_FORECAST
  87. assert fetched_forecast.date_added.replace(
  88. second=0, microsecond=0
  89. ) == approximate_date_added.replace(second=0, microsecond=0)
  90. assert fetched_forecast.date_added < approximate_date_added
  91. record_mock.assert_called_with("issue_forecasts.saved", num_groups=3)
  92. @patch("sentry.analytics.record")
  93. @patch("sentry.issues.forecasts.query_groups_past_counts")
  94. def test_update_group_escalating_forecast(
  95. self, mock_query_groups_past_counts: MagicMock, record_mock: MagicMock
  96. ) -> None:
  97. with self.tasks():
  98. group_list = self.create_archived_until_escalating_groups(num_groups=1)
  99. mock_query_groups_past_counts.return_value = get_mock_groups_past_counts_response(
  100. num_days=7, num_hours=2, groups=group_list
  101. )
  102. run_escalating_forecast()
  103. first_fetched_forecast = EscalatingGroupForecast.fetch(
  104. group_list[0].project.id, group_list[0].id
  105. )
  106. # Assert update when this is run twice
  107. run_escalating_forecast()
  108. second_fetched_forecast = EscalatingGroupForecast.fetch(
  109. group_list[0].project.id, group_list[0].id
  110. )
  111. assert first_fetched_forecast is not None
  112. assert second_fetched_forecast is not None
  113. assert first_fetched_forecast.date_added < second_fetched_forecast.date_added
  114. record_mock.assert_called_with("issue_forecasts.saved", num_groups=1)