firefighter-incident 0.0.14__py3-none-any.whl → 0.0.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. firefighter/_version.py +2 -2
  2. firefighter/api/serializers.py +9 -0
  3. firefighter/confluence/signals/incident_updated.py +2 -2
  4. firefighter/incidents/enums.py +22 -2
  5. firefighter/incidents/forms/closure_reason.py +45 -0
  6. firefighter/incidents/forms/unified_incident.py +406 -0
  7. firefighter/incidents/forms/update_status.py +87 -1
  8. firefighter/incidents/migrations/0027_add_closure_fields.py +40 -0
  9. firefighter/incidents/migrations/0028_add_closure_reason_constraint.py +33 -0
  10. firefighter/incidents/migrations/0029_add_custom_fields_to_incident.py +22 -0
  11. firefighter/incidents/models/incident.py +32 -5
  12. firefighter/incidents/static/css/main.min.css +1 -1
  13. firefighter/incidents/templates/layouts/partials/status_pill.html +1 -1
  14. firefighter/incidents/views/reports.py +3 -3
  15. firefighter/raid/apps.py +9 -26
  16. firefighter/raid/client.py +2 -2
  17. firefighter/raid/forms.py +75 -238
  18. firefighter/raid/signals/incident_created.py +38 -13
  19. firefighter/raid/signals/incident_updated.py +3 -2
  20. firefighter/slack/messages/slack_messages.py +19 -4
  21. firefighter/slack/rules.py +1 -1
  22. firefighter/slack/signals/create_incident_conversation.py +6 -0
  23. firefighter/slack/signals/incident_updated.py +7 -1
  24. firefighter/slack/views/modals/__init__.py +4 -0
  25. firefighter/slack/views/modals/base_modal/form_utils.py +63 -0
  26. firefighter/slack/views/modals/close.py +15 -2
  27. firefighter/slack/views/modals/closure_reason.py +193 -0
  28. firefighter/slack/views/modals/open.py +60 -13
  29. firefighter/slack/views/modals/opening/details/unified.py +203 -0
  30. firefighter/slack/views/modals/opening/select_impact.py +1 -1
  31. firefighter/slack/views/modals/opening/set_details.py +3 -2
  32. firefighter/slack/views/modals/postmortem.py +10 -2
  33. firefighter/slack/views/modals/update_status.py +28 -2
  34. firefighter/slack/views/modals/utils.py +51 -0
  35. {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.16.dist-info}/METADATA +1 -1
  36. {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.16.dist-info}/RECORD +62 -38
  37. firefighter_tests/test_incidents/test_enums.py +100 -0
  38. firefighter_tests/test_incidents/test_forms/conftest.py +179 -0
  39. firefighter_tests/test_incidents/test_forms/test_closure_reason.py +91 -0
  40. firefighter_tests/test_incidents/test_forms/test_unified_incident_form.py +570 -0
  41. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +581 -0
  42. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +410 -0
  43. firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +343 -0
  44. firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +167 -0
  45. firefighter_tests/test_incidents/test_models/test_incident_model.py +68 -0
  46. firefighter_tests/test_raid/conftest.py +154 -0
  47. firefighter_tests/test_raid/test_p1_p3_jira_fields.py +372 -0
  48. firefighter_tests/test_raid/test_raid_forms.py +10 -253
  49. firefighter_tests/test_raid/test_raid_signals.py +187 -0
  50. firefighter_tests/test_slack/messages/__init__.py +0 -0
  51. firefighter_tests/test_slack/messages/test_slack_messages.py +367 -0
  52. firefighter_tests/test_slack/views/modals/conftest.py +140 -0
  53. firefighter_tests/test_slack/views/modals/test_close.py +65 -3
  54. firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +138 -0
  55. firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py +249 -0
  56. firefighter_tests/test_slack/views/modals/test_open.py +146 -2
  57. firefighter_tests/test_slack/views/modals/test_opening_unified.py +421 -0
  58. firefighter_tests/test_slack/views/modals/test_update_status.py +327 -3
  59. firefighter_tests/test_slack/views/modals/test_utils.py +135 -0
  60. firefighter/raid/views/open_normal.py +0 -139
  61. firefighter/slack/views/modals/opening/details/critical.py +0 -88
  62. {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.16.dist-info}/WHEEL +0 -0
  63. {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.16.dist-info}/entry_points.txt +0 -0
  64. {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.16.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,6 @@ from django.test import TestCase
7
7
  from slack_sdk.errors import SlackApiError
8
8
 
9
9
  from firefighter.incidents.factories import (
10
- IncidentCategoryFactory,
11
10
  IncidentFactory,
12
11
  PriorityFactory,
13
12
  UserFactory,
@@ -16,12 +15,7 @@ from firefighter.incidents.models.priority import Priority
16
15
  from firefighter.jira_app.client import JiraAPIError, JiraUserNotFoundError
17
16
  from firefighter.jira_app.models import JiraUser
18
17
  from firefighter.raid.forms import (
19
- CreateNormalCustomerIncidentForm,
20
- CreateRaidDocumentationRequestIncidentForm,
21
- CreateRaidFeatureRequestIncidentForm,
22
- CreateRaidInternalIncidentForm,
23
18
  PlatformChoices,
24
- RaidCreateIncidentSellerForm,
25
19
  alert_slack_comment_ticket,
26
20
  alert_slack_new_jira_ticket,
27
21
  alert_slack_update_ticket,
@@ -33,7 +27,7 @@ from firefighter.raid.forms import (
33
27
  send_message_to_watchers,
34
28
  set_jira_ticket_watchers_raid,
35
29
  )
36
- from firefighter.raid.models import FeatureTeam, JiraTicket
30
+ from firefighter.raid.models import JiraTicket
37
31
  from firefighter.slack.models.conversation import Conversation
38
32
  from firefighter.slack.models.user import SlackUser
39
33
 
@@ -70,252 +64,15 @@ class TestInitialPriority:
70
64
  assert result == default_priority
71
65
 
72
66
 
73
- @pytest.mark.django_db
74
- class TestCreateNormalCustomerIncidentForm:
75
- """Test CreateNormalCustomerIncidentForm functionality."""
76
-
77
- def setup_method(self):
78
- """Set up test data."""
79
- Priority.objects.all().delete() # Clear existing priorities
80
- self.user = UserFactory()
81
- self.jira_user = JiraUser.objects.create(id="jira-123", user=self.user)
82
- self.priority = PriorityFactory(value=1)
83
- self.incident_category = IncidentCategoryFactory()
84
- self.feature_team = FeatureTeam.objects.create(
85
- name="Test Team", jira_project_key="TEST"
86
- )
87
-
88
- @patch("firefighter.raid.forms.process_jira_issue")
89
- @patch("firefighter.raid.forms.create_issue_customer")
90
- @patch("firefighter.raid.forms.get_jira_user_from_user")
91
- @patch("firefighter.raid.forms.get_business_impact")
92
- def test_trigger_incident_workflow(
93
- self, mock_get_business_impact, mock_get_jira_user, mock_create_issue, mock_process_jira_issue
94
- ):
95
- """Test trigger_incident_workflow method."""
96
- # Given
97
- mock_get_jira_user.return_value = self.jira_user
98
- mock_create_issue.return_value = {"id": "10001", "key": "TEST-123"}
99
- mock_get_business_impact.return_value = "High"
100
- mock_process_jira_issue.return_value = None
101
-
102
- form_data = {
103
- "incident_category": self.incident_category,
104
- "platform": "platform-FR",
105
- "title": "Test incident title",
106
- "description": "Test incident description",
107
- "priority": self.priority,
108
- "suggested_team_routing": self.feature_team,
109
- "zendesk_ticket_id": "12345",
110
- }
111
-
112
- form = CreateNormalCustomerIncidentForm()
113
- form.cleaned_data = form_data
114
-
115
- # When
116
- form.trigger_incident_workflow(self.user, {})
117
-
118
- # Then
119
- mock_get_jira_user.assert_called_once_with(self.user)
120
- mock_create_issue.assert_called_once()
121
- mock_process_jira_issue.assert_called_once()
122
-
123
-
124
- @pytest.mark.django_db
125
- class TestCreateRaidDocumentationRequestIncidentForm:
126
- """Test CreateRaidDocumentationRequestIncidentForm functionality."""
127
-
128
- def setup_method(self):
129
- """Set up test data."""
130
- Priority.objects.all().delete() # Clear existing priorities
131
- self.user = UserFactory()
132
- self.jira_user = JiraUser.objects.create(id="jira-doc-123", user=self.user)
133
- self.priority = PriorityFactory(value=20)
134
- self.incident_category = IncidentCategoryFactory()
135
- self.feature_team = FeatureTeam.objects.create(
136
- name="Documentation Team", jira_project_key="DOC"
137
- )
138
-
139
- @patch("firefighter.raid.forms.process_jira_issue")
140
- @patch("firefighter.raid.forms.create_issue_documentation_request")
141
- @patch("firefighter.raid.forms.get_jira_user_from_user")
142
- def test_trigger_incident_workflow(
143
- self, mock_get_jira_user, mock_create_issue, mock_process_jira_issue
144
- ):
145
- """Test trigger_incident_workflow method."""
146
- # Given
147
- mock_get_jira_user.return_value = self.jira_user
148
- mock_create_issue.return_value = {"id": "10002", "key": "TEST-124"}
149
- mock_process_jira_issue.return_value = None
150
-
151
- form_data = {
152
- "incident_category": self.incident_category,
153
- "platform": "platform-DE",
154
- "title": "Documentation request title",
155
- "description": "Documentation request description",
156
- "priority": self.priority,
157
- "suggested_team_routing": self.feature_team,
158
- }
159
-
160
- form = CreateRaidDocumentationRequestIncidentForm()
161
- form.cleaned_data = form_data
162
-
163
- # When
164
- form.trigger_incident_workflow(self.user, {})
165
-
166
- # Then
167
- mock_get_jira_user.assert_called_once_with(self.user)
168
- mock_create_issue.assert_called_once()
169
- mock_process_jira_issue.assert_called_once()
170
-
171
-
172
- @pytest.mark.django_db
173
- class TestCreateRaidFeatureRequestIncidentForm:
174
- """Test CreateRaidFeatureRequestIncidentForm functionality."""
175
-
176
- def setup_method(self):
177
- """Set up test data."""
178
- self.user = UserFactory()
179
- self.jira_user = JiraUser.objects.create(id="jira-feat-123", user=self.user)
180
- self.priority = PriorityFactory(value=30)
181
- self.incident_category = IncidentCategoryFactory()
182
- self.feature_team = FeatureTeam.objects.create(
183
- name="Feature Team", jira_project_key="FEAT"
184
- )
185
-
186
- @patch("firefighter.raid.forms.process_jira_issue")
187
- @patch("firefighter.raid.forms.create_issue_feature_request")
188
- @patch("firefighter.raid.forms.get_jira_user_from_user")
189
- def test_trigger_incident_workflow(
190
- self, mock_get_jira_user, mock_create_issue, mock_process_jira_issue
191
- ):
192
- """Test trigger_incident_workflow method."""
193
- # Given
194
- mock_get_jira_user.return_value = self.jira_user
195
- mock_create_issue.return_value = {"id": "10003", "key": "TEST-125"}
196
- mock_process_jira_issue.return_value = None
197
-
198
- form_data = {
199
- "incident_category": self.incident_category,
200
- "platform": "platform-IT",
201
- "title": "Feature request title",
202
- "description": "Feature request description",
203
- "priority": self.priority,
204
- "suggested_team_routing": self.feature_team,
205
- }
206
-
207
- form = CreateRaidFeatureRequestIncidentForm()
208
- form.cleaned_data = form_data
209
-
210
- # When
211
- form.trigger_incident_workflow(self.user, {})
212
-
213
- # Then
214
- mock_get_jira_user.assert_called_once_with(self.user)
215
- mock_create_issue.assert_called_once()
216
- mock_process_jira_issue.assert_called_once()
217
-
218
-
219
- @pytest.mark.django_db
220
- class TestCreateRaidInternalIncidentForm:
221
- """Test CreateRaidInternalIncidentForm functionality."""
222
-
223
- def setup_method(self):
224
- """Set up test data."""
225
- self.user = UserFactory()
226
- self.jira_user = JiraUser.objects.create(id="jira-int-123", user=self.user)
227
- self.priority = PriorityFactory(value=40)
228
- self.incident_category = IncidentCategoryFactory()
229
- self.feature_team = FeatureTeam.objects.create(
230
- name="Internal Team", jira_project_key="INT"
231
- )
232
-
233
- @patch("firefighter.raid.forms.process_jira_issue")
234
- @patch("firefighter.raid.forms.create_issue_internal")
235
- @patch("firefighter.raid.forms.get_jira_user_from_user")
236
- @patch("firefighter.raid.forms.get_business_impact")
237
- def test_trigger_incident_workflow(
238
- self, mock_get_business_impact, mock_get_jira_user, mock_create_issue, mock_process_jira_issue
239
- ):
240
- """Test trigger_incident_workflow method."""
241
- # Given
242
- mock_get_jira_user.return_value = self.jira_user
243
- mock_create_issue.return_value = {"id": "10004", "key": "TEST-126"}
244
- mock_get_business_impact.return_value = "Medium"
245
- mock_process_jira_issue.return_value = None
246
-
247
- form_data = {
248
- "incident_category": self.incident_category,
249
- "platform": "platform-ES",
250
- "title": "Internal incident title",
251
- "description": "Internal incident description",
252
- "priority": self.priority,
253
- "suggested_team_routing": self.feature_team,
254
- }
255
-
256
- form = CreateRaidInternalIncidentForm()
257
- form.cleaned_data = form_data
258
-
259
- # When
260
- form.trigger_incident_workflow(self.user, {})
261
-
262
- # Then
263
- mock_get_jira_user.assert_called_once_with(self.user)
264
- mock_create_issue.assert_called_once()
265
- mock_process_jira_issue.assert_called_once()
266
-
267
-
268
- @pytest.mark.django_db
269
- class TestRaidCreateIncidentSellerForm:
270
- """Test RaidCreateIncidentSellerForm functionality."""
271
-
272
- def setup_method(self):
273
- """Set up test data."""
274
- self.user = UserFactory()
275
- self.jira_user = JiraUser.objects.create(id="jira-sell-123", user=self.user)
276
- self.priority = PriorityFactory(value=50)
277
- self.incident_category = IncidentCategoryFactory()
278
- self.feature_team = FeatureTeam.objects.create(
279
- name="Seller Team", jira_project_key="SELL"
280
- )
281
-
282
- @patch("firefighter.raid.forms.process_jira_issue")
283
- @patch("firefighter.raid.forms.create_issue_seller")
284
- @patch("firefighter.raid.forms.get_jira_user_from_user")
285
- @patch("firefighter.raid.forms.get_business_impact")
286
- def test_trigger_incident_workflow(
287
- self, mock_get_business_impact, mock_get_jira_user, mock_create_issue, mock_process_jira_issue
288
- ):
289
- """Test trigger_incident_workflow method."""
290
- # Given
291
- mock_get_jira_user.return_value = self.jira_user
292
- mock_create_issue.return_value = {"id": "10005", "key": "TEST-127"}
293
- mock_get_business_impact.return_value = "Low"
294
- mock_process_jira_issue.return_value = None
295
-
296
- form_data = {
297
- "incident_category": self.incident_category,
298
- "platform": "platform-UK",
299
- "title": "Seller incident title",
300
- "description": "Seller incident description",
301
- "priority": self.priority,
302
- "suggested_team_routing": self.feature_team,
303
- "seller_contract_id": "SELLER123",
304
- "is_key_account": True,
305
- "is_seller_in_golden_list": False,
306
- "zoho_desk_ticket_id": "ZOHO456",
307
- }
308
-
309
- form = RaidCreateIncidentSellerForm()
310
- form.cleaned_data = form_data
311
-
312
- # When
313
- form.trigger_incident_workflow(self.user, {})
314
-
315
- # Then
316
- mock_get_jira_user.assert_called_once_with(self.user)
317
- mock_create_issue.assert_called_once()
318
- mock_process_jira_issue.assert_called_once()
67
+ # NOTE: Form class tests (TestCreateNormalCustomerIncidentForm,
68
+ # TestCreateRaidDocumentationRequestIncidentForm, TestCreateRaidFeatureRequestIncidentForm,
69
+ # TestCreateRaidInternalIncidentForm, TestRaidCreateIncidentSellerForm) have been removed.
70
+ #
71
+ # These forms have been replaced by the unified incident form:
72
+ # firefighter.incidents.forms.unified_incident.UnifiedIncidentForm
73
+ #
74
+ # Tests for the unified form should be added in a new file:
75
+ # tests/test_incidents/test_forms/test_unified_incident.py
319
76
 
320
77
 
321
78
  @pytest.mark.django_db
@@ -0,0 +1,187 @@
1
+ """Tests for RAID signal handlers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import Mock, patch
6
+
7
+ import pytest
8
+
9
+ from firefighter.incidents.enums import IncidentStatus
10
+ from firefighter.incidents.models.incident_update import IncidentUpdate
11
+ from firefighter.raid.signals.incident_updated import (
12
+ incident_updated_close_ticket_when_mitigated_or_postmortem,
13
+ )
14
+
15
+
16
+ @pytest.mark.django_db
17
+ class TestIncidentUpdatedCloseJiraTicket:
18
+ """Test that Jira tickets are closed when incidents reach terminal statuses."""
19
+
20
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
21
+ def test_close_jira_ticket_when_status_changes_to_mitigated(
22
+ self, mock_close_issue: Mock, incident_factory, user_factory, jira_ticket_factory
23
+ ) -> None:
24
+ """Test that Jira ticket is closed when incident status changes to MITIGATED."""
25
+ user = user_factory()
26
+ incident = incident_factory(created_by=user)
27
+ jira_ticket = jira_ticket_factory(incident=incident)
28
+ incident.jira_ticket = jira_ticket
29
+
30
+ incident_update = IncidentUpdate(
31
+ incident=incident,
32
+ status=IncidentStatus.MITIGATED,
33
+ created_by=user,
34
+ )
35
+
36
+ # Call the signal handler
37
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
38
+ sender="update_status",
39
+ incident=incident,
40
+ incident_update=incident_update,
41
+ updated_fields=["_status"],
42
+ )
43
+
44
+ # Verify close_issue was called
45
+ mock_close_issue.assert_called_once_with(issue_id=jira_ticket.id)
46
+
47
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
48
+ def test_close_jira_ticket_when_status_changes_to_postmortem(
49
+ self, mock_close_issue: Mock, incident_factory, user_factory, jira_ticket_factory
50
+ ) -> None:
51
+ """Test that Jira ticket is closed when incident status changes to POST_MORTEM."""
52
+ user = user_factory()
53
+ incident = incident_factory(created_by=user)
54
+ jira_ticket = jira_ticket_factory(incident=incident)
55
+ incident.jira_ticket = jira_ticket
56
+
57
+ incident_update = IncidentUpdate(
58
+ incident=incident,
59
+ status=IncidentStatus.POST_MORTEM,
60
+ created_by=user,
61
+ )
62
+
63
+ # Call the signal handler
64
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
65
+ sender="update_status",
66
+ incident=incident,
67
+ incident_update=incident_update,
68
+ updated_fields=["_status"],
69
+ )
70
+
71
+ # Verify close_issue was called
72
+ mock_close_issue.assert_called_once_with(issue_id=jira_ticket.id)
73
+
74
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
75
+ def test_close_jira_ticket_when_status_changes_to_closed(
76
+ self, mock_close_issue: Mock, incident_factory, user_factory, jira_ticket_factory
77
+ ) -> None:
78
+ """Test that Jira ticket is closed when incident status changes to CLOSED (direct close)."""
79
+ user = user_factory()
80
+ incident = incident_factory(created_by=user)
81
+ jira_ticket = jira_ticket_factory(incident=incident)
82
+ incident.jira_ticket = jira_ticket
83
+
84
+ incident_update = IncidentUpdate(
85
+ incident=incident,
86
+ status=IncidentStatus.CLOSED,
87
+ created_by=user,
88
+ )
89
+
90
+ # Call the signal handler
91
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
92
+ sender="update_status",
93
+ incident=incident,
94
+ incident_update=incident_update,
95
+ updated_fields=["_status"],
96
+ )
97
+
98
+ # Verify close_issue was called
99
+ mock_close_issue.assert_called_once_with(issue_id=jira_ticket.id)
100
+
101
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
102
+ def test_do_not_close_jira_ticket_when_status_not_terminal(
103
+ self, mock_close_issue: Mock, incident_factory, user_factory, jira_ticket_factory
104
+ ) -> None:
105
+ """Test that Jira ticket is NOT closed for non-terminal statuses."""
106
+ user = user_factory()
107
+ incident = incident_factory(created_by=user)
108
+ jira_ticket = jira_ticket_factory(incident=incident)
109
+ incident.jira_ticket = jira_ticket
110
+
111
+ incident_update = IncidentUpdate(
112
+ incident=incident,
113
+ status=IncidentStatus.INVESTIGATING,
114
+ created_by=user,
115
+ )
116
+
117
+ # Call the signal handler
118
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
119
+ sender="update_status",
120
+ incident=incident,
121
+ incident_update=incident_update,
122
+ updated_fields=["_status"],
123
+ )
124
+
125
+ # Verify close_issue was NOT called
126
+ mock_close_issue.assert_not_called()
127
+
128
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
129
+ def test_do_not_close_jira_ticket_when_status_not_updated(
130
+ self, mock_close_issue: Mock, incident_factory, user_factory, jira_ticket_factory
131
+ ) -> None:
132
+ """Test that Jira ticket is NOT closed when _status is not in updated_fields."""
133
+ user = user_factory()
134
+ incident = incident_factory(created_by=user)
135
+ jira_ticket = jira_ticket_factory(incident=incident)
136
+ incident.jira_ticket = jira_ticket
137
+
138
+ incident_update = IncidentUpdate(
139
+ incident=incident,
140
+ status=IncidentStatus.CLOSED,
141
+ created_by=user,
142
+ )
143
+
144
+ # Call the signal handler with updated_fields that don't include _status
145
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
146
+ sender="update_status",
147
+ incident=incident,
148
+ incident_update=incident_update,
149
+ updated_fields=["priority_id"], # Not _status
150
+ )
151
+
152
+ # Verify close_issue was NOT called
153
+ mock_close_issue.assert_not_called()
154
+
155
+ @patch("firefighter.raid.signals.incident_updated.client.close_issue")
156
+ @patch("firefighter.raid.signals.incident_updated.logger")
157
+ def test_do_not_crash_when_jira_ticket_missing(
158
+ self,
159
+ mock_logger: Mock,
160
+ mock_close_issue: Mock,
161
+ incident_factory,
162
+ user_factory,
163
+ ) -> None:
164
+ """Test that signal handler handles gracefully when Jira ticket is missing."""
165
+ user = user_factory()
166
+ incident = incident_factory(created_by=user)
167
+ # No jira_ticket attached to incident
168
+
169
+ incident_update = IncidentUpdate(
170
+ incident=incident,
171
+ status=IncidentStatus.CLOSED,
172
+ created_by=user,
173
+ )
174
+
175
+ # Call the signal handler - should not crash
176
+ incident_updated_close_ticket_when_mitigated_or_postmortem(
177
+ sender="update_status",
178
+ incident=incident,
179
+ incident_update=incident_update,
180
+ updated_fields=["_status"],
181
+ )
182
+
183
+ # Verify close_issue was NOT called
184
+ mock_close_issue.assert_not_called()
185
+
186
+ # Verify a warning was logged
187
+ mock_logger.warning.assert_called_once()
File without changes