|
45 | 45 | from sentry.snuba.dataset import Dataset
|
46 | 46 | from sentry.snuba.models import QuerySubscription, SnubaQueryEventType
|
47 | 47 | from sentry.testutils.cases import BaseMetricsTestCase, SnubaTestCase, TestCase
|
| 48 | +from sentry.testutils.helpers import override_options |
48 | 49 | from sentry.testutils.helpers.datetime import freeze_time, iso_format
|
49 | 50 | from sentry.utils import json
|
50 | 51 | from sentry.utils.dates import to_timestamp
|
@@ -167,6 +168,12 @@ def assert_trigger_counts(self, processor, trigger, alert_triggers=0, resolve_tr
|
167 | 168 | def latest_activity(self, incident):
|
168 | 169 | return IncidentActivity.objects.filter(incident=incident).order_by("-id").first()
|
169 | 170 |
|
| 171 | + def assert_incident_is_latest_for_rule(self, incident): |
| 172 | + last_incident = ( |
| 173 | + Incident.objects.filter(alert_rule=incident.alert_rule).order_by("-date_added").first() |
| 174 | + ) |
| 175 | + assert last_incident == incident |
| 176 | + |
170 | 177 |
|
171 | 178 | @freeze_time()
|
172 | 179 | class ProcessUpdateTest(ProcessUpdateBaseClass):
|
@@ -2106,6 +2113,72 @@ def test_comparison_alert_different_aggregate(self):
|
2106 | 2113 | incident, [self.action], [(150.0, IncidentStatus.CLOSED, mock.ANY)]
|
2107 | 2114 | )
|
2108 | 2115 |
|
| 2116 | + @override_options({"metric_alerts.rate_limit": True}) |
| 2117 | + def test_no_new_incidents_within_ten_minutes(self): |
| 2118 | + # Verify that a new incident is not made for the same rule, trigger, and |
| 2119 | + # subscription if an incident was already made within the last 10 minutes. |
| 2120 | + rule = self.rule |
| 2121 | + trigger = self.trigger |
| 2122 | + processor = self.send_update( |
| 2123 | + rule, trigger.alert_threshold + 1, timedelta(minutes=-2), self.sub |
| 2124 | + ) |
| 2125 | + self.assert_trigger_counts(processor, self.trigger, 0, 0) |
| 2126 | + original_incident = self.assert_active_incident(rule) |
| 2127 | + original_incident.update(date_added=original_incident.date_added - timedelta(minutes=10)) |
| 2128 | + self.assert_trigger_exists_with_status(original_incident, trigger, TriggerStatus.ACTIVE) |
| 2129 | + |
| 2130 | + # resolve the trigger |
| 2131 | + self.send_update(rule, 6, timedelta(minutes=-1), subscription=self.sub) |
| 2132 | + self.assert_no_active_incident(rule) |
| 2133 | + self.assert_trigger_exists_with_status(original_incident, trigger, TriggerStatus.RESOLVED) |
| 2134 | + |
| 2135 | + # fire trigger again within 10 minutes; no new incident should be made |
| 2136 | + processor = self.send_update(rule, trigger.alert_threshold + 1, subscription=self.sub) |
| 2137 | + self.assert_trigger_counts(processor, self.trigger, 1, 0) |
| 2138 | + self.assert_no_active_incident(rule) |
| 2139 | + self.assert_trigger_exists_with_status(original_incident, trigger, TriggerStatus.RESOLVED) |
| 2140 | + self.assert_incident_is_latest_for_rule(original_incident) |
| 2141 | + self.metrics.incr.assert_has_calls( |
| 2142 | + [ |
| 2143 | + call( |
| 2144 | + "incidents.alert_rules.hit_rate_limit", |
| 2145 | + tags={ |
| 2146 | + "last_incident_id": original_incident.id, |
| 2147 | + "project_id": self.sub.project.id, |
| 2148 | + "trigger_id": trigger.id, |
| 2149 | + }, |
| 2150 | + ), |
| 2151 | + ], |
| 2152 | + any_order=True, |
| 2153 | + ) |
| 2154 | + |
| 2155 | + @override_options({"metric_alerts.rate_limit": True}) |
| 2156 | + def test_incident_made_after_ten_minutes(self): |
| 2157 | + # Verify that a new incident will be made for the same rule, trigger, and |
| 2158 | + # subscription if the last incident made for those was made more tha 10 minutes |
| 2159 | + # ago |
| 2160 | + rule = self.rule |
| 2161 | + trigger = self.trigger |
| 2162 | + processor = self.send_update( |
| 2163 | + rule, trigger.alert_threshold + 1, timedelta(minutes=-2), self.sub |
| 2164 | + ) |
| 2165 | + self.assert_trigger_counts(processor, self.trigger, 0, 0) |
| 2166 | + original_incident = self.assert_active_incident(rule) |
| 2167 | + original_incident.update(date_added=original_incident.date_added - timedelta(minutes=11)) |
| 2168 | + self.assert_trigger_exists_with_status(original_incident, trigger, TriggerStatus.ACTIVE) |
| 2169 | + |
| 2170 | + # resolve the trigger |
| 2171 | + self.send_update(rule, 6, timedelta(minutes=-1), self.sub) |
| 2172 | + self.assert_no_active_incident(rule) |
| 2173 | + self.assert_trigger_exists_with_status(original_incident, trigger, TriggerStatus.RESOLVED) |
| 2174 | + |
| 2175 | + # fire trigger again after more than 10 minutes have passed; a new incident should be made |
| 2176 | + processor = self.send_update(rule, trigger.alert_threshold + 1, subscription=self.sub) |
| 2177 | + self.assert_trigger_counts(processor, self.trigger, 0, 0) |
| 2178 | + new_incident = self.assert_active_incident(rule) |
| 2179 | + self.assert_trigger_exists_with_status(new_incident, trigger, TriggerStatus.ACTIVE) |
| 2180 | + self.assert_incident_is_latest_for_rule(new_incident) |
| 2181 | + |
2109 | 2182 |
|
2110 | 2183 | class MetricsCrashRateAlertProcessUpdateTest(ProcessUpdateBaseClass, BaseMetricsTestCase):
|
2111 | 2184 | @pytest.fixture(autouse=True)
|
|
0 commit comments