firefighter-incident 0.0.14__py3-none-any.whl → 0.0.15__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.
- firefighter/_version.py +2 -2
- firefighter/api/serializers.py +9 -0
- firefighter/confluence/signals/incident_updated.py +2 -2
- firefighter/incidents/enums.py +22 -2
- firefighter/incidents/forms/closure_reason.py +45 -0
- firefighter/incidents/forms/unified_incident.py +406 -0
- firefighter/incidents/forms/update_status.py +87 -1
- firefighter/incidents/migrations/0027_add_closure_fields.py +40 -0
- firefighter/incidents/migrations/0028_add_closure_reason_constraint.py +33 -0
- firefighter/incidents/migrations/0029_add_custom_fields_to_incident.py +22 -0
- firefighter/incidents/models/incident.py +32 -5
- firefighter/incidents/static/css/main.min.css +1 -1
- firefighter/incidents/templates/layouts/partials/status_pill.html +1 -1
- firefighter/incidents/views/reports.py +3 -3
- firefighter/raid/apps.py +9 -26
- firefighter/raid/client.py +2 -2
- firefighter/raid/forms.py +75 -238
- firefighter/raid/signals/incident_created.py +38 -13
- firefighter/raid/signals/incident_updated.py +3 -2
- firefighter/slack/messages/slack_messages.py +19 -4
- firefighter/slack/rules.py +1 -1
- firefighter/slack/signals/create_incident_conversation.py +6 -0
- firefighter/slack/signals/incident_updated.py +7 -1
- firefighter/slack/views/modals/__init__.py +4 -0
- firefighter/slack/views/modals/base_modal/form_utils.py +63 -0
- firefighter/slack/views/modals/close.py +15 -2
- firefighter/slack/views/modals/closure_reason.py +193 -0
- firefighter/slack/views/modals/open.py +59 -12
- firefighter/slack/views/modals/opening/details/unified.py +203 -0
- firefighter/slack/views/modals/opening/set_details.py +3 -2
- firefighter/slack/views/modals/postmortem.py +10 -2
- firefighter/slack/views/modals/update_status.py +28 -2
- firefighter/slack/views/modals/utils.py +51 -0
- {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.15.dist-info}/METADATA +1 -1
- {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.15.dist-info}/RECORD +61 -37
- firefighter_tests/test_incidents/test_enums.py +100 -0
- firefighter_tests/test_incidents/test_forms/conftest.py +179 -0
- firefighter_tests/test_incidents/test_forms/test_closure_reason.py +91 -0
- firefighter_tests/test_incidents/test_forms/test_unified_incident_form.py +570 -0
- firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +581 -0
- firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +410 -0
- firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +343 -0
- firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +167 -0
- firefighter_tests/test_incidents/test_models/test_incident_model.py +68 -0
- firefighter_tests/test_raid/conftest.py +154 -0
- firefighter_tests/test_raid/test_p1_p3_jira_fields.py +372 -0
- firefighter_tests/test_raid/test_raid_forms.py +10 -253
- firefighter_tests/test_raid/test_raid_signals.py +187 -0
- firefighter_tests/test_slack/messages/__init__.py +0 -0
- firefighter_tests/test_slack/messages/test_slack_messages.py +367 -0
- firefighter_tests/test_slack/views/modals/conftest.py +140 -0
- firefighter_tests/test_slack/views/modals/test_close.py +65 -3
- firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +138 -0
- firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py +249 -0
- firefighter_tests/test_slack/views/modals/test_open.py +146 -2
- firefighter_tests/test_slack/views/modals/test_opening_unified.py +421 -0
- firefighter_tests/test_slack/views/modals/test_update_status.py +327 -3
- firefighter_tests/test_slack/views/modals/test_utils.py +135 -0
- firefighter/raid/views/open_normal.py +0 -139
- firefighter/slack/views/modals/opening/details/critical.py +0 -88
- {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.15.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.15.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.14.dist-info → firefighter_incident-0.0.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
"""End-to-end integration tests for unified incident form custom fields propagation.
|
|
2
|
+
|
|
3
|
+
This test file verifies that custom fields (zendesk_ticket_id, seller_contract_id, etc.)
|
|
4
|
+
are properly transmitted from the form submission all the way to:
|
|
5
|
+
1. Form cleaned_data
|
|
6
|
+
2. Jira extra fields in signals
|
|
7
|
+
3. Incident custom_fields
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import inspect
|
|
12
|
+
from typing import Any
|
|
13
|
+
from unittest.mock import MagicMock, patch
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from firefighter.incidents.forms.unified_incident import UnifiedIncidentForm
|
|
18
|
+
from firefighter.incidents.models.impact import ImpactLevel, ImpactType, LevelChoices
|
|
19
|
+
from firefighter.incidents.signals import create_incident_conversation
|
|
20
|
+
from firefighter.slack.views.modals.open import OpenModal
|
|
21
|
+
from firefighter.slack.views.modals.opening.details.unified import (
|
|
22
|
+
OpeningUnifiedModal,
|
|
23
|
+
UnifiedIncidentFormSlack,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.django_db
|
|
28
|
+
class TestUnifiedIncidentFormCustomFieldsPropagation:
|
|
29
|
+
"""Test end-to-end propagation of custom fields from form to signal."""
|
|
30
|
+
|
|
31
|
+
def test_customer_impact_zendesk_field_propagates_to_signal(
|
|
32
|
+
self,
|
|
33
|
+
priority_factory,
|
|
34
|
+
environment_factory,
|
|
35
|
+
incident_category_factory,
|
|
36
|
+
user_factory,
|
|
37
|
+
):
|
|
38
|
+
"""Zendesk ticket ID should propagate from form to signal with jira_extra_fields."""
|
|
39
|
+
# Setup
|
|
40
|
+
priority = priority_factory(value=1, default=True)
|
|
41
|
+
environment = environment_factory(value="PRD", default=True)
|
|
42
|
+
category = incident_category_factory()
|
|
43
|
+
|
|
44
|
+
# Get customer impact from fixtures (impact_type=3, value=HI)
|
|
45
|
+
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
46
|
+
customer_impact = ImpactLevel.objects.get(
|
|
47
|
+
impact_type=customers_impact_type,
|
|
48
|
+
value=LevelChoices.HIGH.value
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Form data with customer impact and zendesk ticket
|
|
52
|
+
form_data = {
|
|
53
|
+
"title": "Customer issue with Zendesk ticket",
|
|
54
|
+
"description": "Test incident with customer impact and Zendesk tracking",
|
|
55
|
+
"incident_category": category.id,
|
|
56
|
+
"environment": [environment.id],
|
|
57
|
+
"platform": ["platform-FR"],
|
|
58
|
+
"priority": priority.id,
|
|
59
|
+
"zendesk_ticket_id": "12345",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impacts_data = {
|
|
63
|
+
"customers_impact": customer_impact,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Create form with impacts context
|
|
67
|
+
form = UnifiedIncidentFormSlack(
|
|
68
|
+
data=form_data,
|
|
69
|
+
impacts_data=impacts_data,
|
|
70
|
+
response_type="critical",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Verify form is valid
|
|
74
|
+
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
75
|
+
|
|
76
|
+
# Verify zendesk_ticket_id is in cleaned_data
|
|
77
|
+
assert "zendesk_ticket_id" in form.cleaned_data
|
|
78
|
+
assert form.cleaned_data["zendesk_ticket_id"] == "12345"
|
|
79
|
+
|
|
80
|
+
# Mock signal receiver to capture jira_extra_fields
|
|
81
|
+
signal_received = False
|
|
82
|
+
signal_data: dict[str, Any] = {}
|
|
83
|
+
|
|
84
|
+
def capture_signal(sender: Any, incident: Any, **kwargs: Any) -> None:
|
|
85
|
+
nonlocal signal_received, signal_data
|
|
86
|
+
signal_received = True
|
|
87
|
+
signal_data = {"incident": incident, **kwargs}
|
|
88
|
+
|
|
89
|
+
# Connect signal receiver
|
|
90
|
+
create_incident_conversation.connect(capture_signal, weak=False)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Mock SelectImpactForm.save since it's not what we're testing here
|
|
94
|
+
with patch("firefighter.incidents.forms.unified_incident.SelectImpactForm") as mock_select_impact:
|
|
95
|
+
mock_form_instance = MagicMock()
|
|
96
|
+
mock_form_instance.save.return_value = None
|
|
97
|
+
mock_select_impact.return_value = mock_form_instance
|
|
98
|
+
|
|
99
|
+
# Trigger workflow
|
|
100
|
+
user = user_factory()
|
|
101
|
+
form.trigger_incident_workflow(
|
|
102
|
+
creator=user,
|
|
103
|
+
impacts_data=impacts_data,
|
|
104
|
+
response_type="critical",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Verify signal was sent
|
|
108
|
+
assert signal_received, "Signal create_incident_conversation should have been sent"
|
|
109
|
+
|
|
110
|
+
# Verify jira_extra_fields was passed to signal
|
|
111
|
+
assert "jira_extra_fields" in signal_data, "jira_extra_fields should be in signal kwargs"
|
|
112
|
+
|
|
113
|
+
# Verify zendesk_ticket_id is in jira_extra_fields
|
|
114
|
+
jira_extra_fields = signal_data["jira_extra_fields"]
|
|
115
|
+
assert "zendesk_ticket_id" in jira_extra_fields
|
|
116
|
+
assert jira_extra_fields["zendesk_ticket_id"] == "12345"
|
|
117
|
+
|
|
118
|
+
# Verify incident was created with custom_fields
|
|
119
|
+
incident = signal_data["incident"]
|
|
120
|
+
assert incident is not None
|
|
121
|
+
assert hasattr(incident, "custom_fields")
|
|
122
|
+
assert "zendesk_ticket_id" in incident.custom_fields
|
|
123
|
+
assert incident.custom_fields["zendesk_ticket_id"] == "12345"
|
|
124
|
+
|
|
125
|
+
finally:
|
|
126
|
+
# Disconnect signal
|
|
127
|
+
create_incident_conversation.disconnect(capture_signal)
|
|
128
|
+
|
|
129
|
+
def test_seller_impact_fields_propagate_to_signal(
|
|
130
|
+
self,
|
|
131
|
+
priority_factory,
|
|
132
|
+
environment_factory,
|
|
133
|
+
incident_category_factory,
|
|
134
|
+
user_factory,
|
|
135
|
+
):
|
|
136
|
+
"""Seller-specific fields should propagate from form to signal with jira_extra_fields."""
|
|
137
|
+
# Setup
|
|
138
|
+
priority = priority_factory(value=1, default=True)
|
|
139
|
+
environment = environment_factory(value="PRD", default=True)
|
|
140
|
+
category = incident_category_factory()
|
|
141
|
+
|
|
142
|
+
# Get seller impact from fixtures (impact_type=2, value=HI)
|
|
143
|
+
sellers_impact_type = ImpactType.objects.get(value="sellers_impact")
|
|
144
|
+
seller_impact = ImpactLevel.objects.get(
|
|
145
|
+
impact_type=sellers_impact_type,
|
|
146
|
+
value=LevelChoices.HIGH.value
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Form data with seller impact and seller-specific fields
|
|
150
|
+
form_data = {
|
|
151
|
+
"title": "Seller issue with contract tracking",
|
|
152
|
+
"description": "Test incident with seller impact and contract ID",
|
|
153
|
+
"incident_category": category.id,
|
|
154
|
+
"environment": [environment.id],
|
|
155
|
+
"platform": ["platform-FR"],
|
|
156
|
+
"priority": priority.id,
|
|
157
|
+
"seller_contract_id": "CONTRACT-789",
|
|
158
|
+
"zoho_desk_ticket_id": "ZOHO-456",
|
|
159
|
+
"is_key_account": True,
|
|
160
|
+
"is_seller_in_golden_list": False,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
impacts_data = {
|
|
164
|
+
"sellers_impact": seller_impact,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Create form with impacts context
|
|
168
|
+
form = UnifiedIncidentFormSlack(
|
|
169
|
+
data=form_data,
|
|
170
|
+
impacts_data=impacts_data,
|
|
171
|
+
response_type="critical",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Verify form is valid
|
|
175
|
+
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
176
|
+
|
|
177
|
+
# Verify seller fields are in cleaned_data
|
|
178
|
+
assert form.cleaned_data["seller_contract_id"] == "CONTRACT-789"
|
|
179
|
+
assert form.cleaned_data["zoho_desk_ticket_id"] == "ZOHO-456"
|
|
180
|
+
assert form.cleaned_data["is_key_account"] is True
|
|
181
|
+
assert form.cleaned_data["is_seller_in_golden_list"] is False
|
|
182
|
+
|
|
183
|
+
# Mock signal receiver to capture jira_extra_fields
|
|
184
|
+
signal_received = False
|
|
185
|
+
signal_data: dict[str, Any] = {}
|
|
186
|
+
|
|
187
|
+
def capture_signal(sender: Any, incident: Any, **kwargs: Any) -> None:
|
|
188
|
+
nonlocal signal_received, signal_data
|
|
189
|
+
signal_received = True
|
|
190
|
+
signal_data = {"incident": incident, **kwargs}
|
|
191
|
+
|
|
192
|
+
# Connect signal receiver
|
|
193
|
+
create_incident_conversation.connect(capture_signal, weak=False)
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
# Mock SelectImpactForm.save since it's not what we're testing here
|
|
197
|
+
with patch("firefighter.incidents.forms.unified_incident.SelectImpactForm") as mock_select_impact:
|
|
198
|
+
mock_form_instance = MagicMock()
|
|
199
|
+
mock_form_instance.save.return_value = None
|
|
200
|
+
mock_select_impact.return_value = mock_form_instance
|
|
201
|
+
|
|
202
|
+
# Trigger workflow
|
|
203
|
+
user = user_factory()
|
|
204
|
+
form.trigger_incident_workflow(
|
|
205
|
+
creator=user,
|
|
206
|
+
impacts_data=impacts_data,
|
|
207
|
+
response_type="critical",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Verify signal was sent
|
|
211
|
+
assert signal_received, "Signal create_incident_conversation should have been sent"
|
|
212
|
+
|
|
213
|
+
# Verify jira_extra_fields was passed to signal
|
|
214
|
+
assert "jira_extra_fields" in signal_data
|
|
215
|
+
|
|
216
|
+
# Verify all seller fields are in jira_extra_fields
|
|
217
|
+
jira_extra_fields = signal_data["jira_extra_fields"]
|
|
218
|
+
assert jira_extra_fields["seller_contract_id"] == "CONTRACT-789"
|
|
219
|
+
assert jira_extra_fields["zoho_desk_ticket_id"] == "ZOHO-456"
|
|
220
|
+
assert jira_extra_fields["is_key_account"] is True
|
|
221
|
+
assert jira_extra_fields["is_seller_in_golden_list"] is False
|
|
222
|
+
|
|
223
|
+
# Verify incident was created with all seller fields in custom_fields
|
|
224
|
+
incident = signal_data["incident"]
|
|
225
|
+
assert "seller_contract_id" in incident.custom_fields
|
|
226
|
+
assert incident.custom_fields["seller_contract_id"] == "CONTRACT-789"
|
|
227
|
+
assert "zoho_desk_ticket_id" in incident.custom_fields
|
|
228
|
+
assert incident.custom_fields["zoho_desk_ticket_id"] == "ZOHO-456"
|
|
229
|
+
assert "is_key_account" in incident.custom_fields
|
|
230
|
+
assert incident.custom_fields["is_key_account"] is True
|
|
231
|
+
assert "is_seller_in_golden_list" in incident.custom_fields
|
|
232
|
+
assert incident.custom_fields["is_seller_in_golden_list"] is False
|
|
233
|
+
|
|
234
|
+
finally:
|
|
235
|
+
# Disconnect signal
|
|
236
|
+
create_incident_conversation.disconnect(capture_signal)
|
|
237
|
+
|
|
238
|
+
def test_both_customer_and_seller_fields_propagate_together(
|
|
239
|
+
self,
|
|
240
|
+
priority_factory,
|
|
241
|
+
environment_factory,
|
|
242
|
+
incident_category_factory,
|
|
243
|
+
user_factory,
|
|
244
|
+
):
|
|
245
|
+
"""Both customer and seller fields should propagate when both impacts are selected."""
|
|
246
|
+
# Setup
|
|
247
|
+
priority = priority_factory(value=1, default=True)
|
|
248
|
+
environment = environment_factory(value="PRD", default=True)
|
|
249
|
+
category = incident_category_factory()
|
|
250
|
+
|
|
251
|
+
# Get impacts from fixtures
|
|
252
|
+
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
253
|
+
customer_impact = ImpactLevel.objects.get(
|
|
254
|
+
impact_type=customers_impact_type,
|
|
255
|
+
value=LevelChoices.HIGH.value
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
sellers_impact_type = ImpactType.objects.get(value="sellers_impact")
|
|
259
|
+
seller_impact = ImpactLevel.objects.get(
|
|
260
|
+
impact_type=sellers_impact_type,
|
|
261
|
+
value=LevelChoices.LOW.value
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Form data with both impacts and all custom fields
|
|
265
|
+
form_data = {
|
|
266
|
+
"title": "Combined customer and seller issue",
|
|
267
|
+
"description": "Test incident affecting both customers and sellers",
|
|
268
|
+
"incident_category": category.id,
|
|
269
|
+
"environment": [environment.id],
|
|
270
|
+
"platform": ["platform-All"],
|
|
271
|
+
"priority": priority.id,
|
|
272
|
+
"zendesk_ticket_id": "ZD-999",
|
|
273
|
+
"seller_contract_id": "SC-888",
|
|
274
|
+
"zoho_desk_ticket_id": "ZOHO-777",
|
|
275
|
+
"is_key_account": False,
|
|
276
|
+
"is_seller_in_golden_list": True,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
impacts_data = {
|
|
280
|
+
"customers_impact": customer_impact,
|
|
281
|
+
"sellers_impact": seller_impact,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# Create form with impacts context
|
|
285
|
+
form = UnifiedIncidentFormSlack(
|
|
286
|
+
data=form_data,
|
|
287
|
+
impacts_data=impacts_data,
|
|
288
|
+
response_type="critical",
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Verify form is valid
|
|
292
|
+
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
293
|
+
|
|
294
|
+
# Mock signal receiver
|
|
295
|
+
signal_received = False
|
|
296
|
+
signal_data: dict[str, Any] = {}
|
|
297
|
+
|
|
298
|
+
def capture_signal(sender: Any, incident: Any, **kwargs: Any) -> None:
|
|
299
|
+
nonlocal signal_received, signal_data
|
|
300
|
+
signal_received = True
|
|
301
|
+
signal_data = {"incident": incident, **kwargs}
|
|
302
|
+
|
|
303
|
+
create_incident_conversation.connect(capture_signal, weak=False)
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
# Mock SelectImpactForm.save since it's not what we're testing here
|
|
307
|
+
with patch("firefighter.incidents.forms.unified_incident.SelectImpactForm") as mock_select_impact:
|
|
308
|
+
mock_form_instance = MagicMock()
|
|
309
|
+
mock_form_instance.save.return_value = None
|
|
310
|
+
mock_select_impact.return_value = mock_form_instance
|
|
311
|
+
|
|
312
|
+
# Trigger workflow
|
|
313
|
+
user = user_factory()
|
|
314
|
+
form.trigger_incident_workflow(
|
|
315
|
+
creator=user,
|
|
316
|
+
impacts_data=impacts_data,
|
|
317
|
+
response_type="critical",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Verify signal was sent with all fields
|
|
321
|
+
assert signal_received
|
|
322
|
+
jira_extra_fields = signal_data["jira_extra_fields"]
|
|
323
|
+
|
|
324
|
+
# Verify both customer and seller fields are present
|
|
325
|
+
assert jira_extra_fields["zendesk_ticket_id"] == "ZD-999"
|
|
326
|
+
assert jira_extra_fields["seller_contract_id"] == "SC-888"
|
|
327
|
+
assert jira_extra_fields["zoho_desk_ticket_id"] == "ZOHO-777"
|
|
328
|
+
assert jira_extra_fields["is_key_account"] is False
|
|
329
|
+
assert jira_extra_fields["is_seller_in_golden_list"] is True
|
|
330
|
+
|
|
331
|
+
# Verify new fields: environments and platforms
|
|
332
|
+
assert "environments" in jira_extra_fields
|
|
333
|
+
assert jira_extra_fields["environments"] == ["PRD"]
|
|
334
|
+
assert "platforms" in jira_extra_fields
|
|
335
|
+
assert jira_extra_fields["platforms"] == ["platform-All"]
|
|
336
|
+
|
|
337
|
+
# Verify incident custom_fields has all fields (5 original + 2 new = 7)
|
|
338
|
+
incident = signal_data["incident"]
|
|
339
|
+
assert len(incident.custom_fields) == 7
|
|
340
|
+
assert incident.custom_fields["zendesk_ticket_id"] == "ZD-999"
|
|
341
|
+
assert incident.custom_fields["seller_contract_id"] == "SC-888"
|
|
342
|
+
assert incident.custom_fields["environments"] == ["PRD"]
|
|
343
|
+
assert incident.custom_fields["platforms"] == ["platform-All"]
|
|
344
|
+
|
|
345
|
+
finally:
|
|
346
|
+
create_incident_conversation.disconnect(capture_signal)
|
|
347
|
+
|
|
348
|
+
def test_no_impact_sends_empty_jira_extra_fields(
|
|
349
|
+
self,
|
|
350
|
+
priority_factory,
|
|
351
|
+
environment_factory,
|
|
352
|
+
incident_category_factory,
|
|
353
|
+
user_factory,
|
|
354
|
+
):
|
|
355
|
+
"""When no customer/seller impact is selected, jira_extra_fields should only contain None values."""
|
|
356
|
+
# Setup
|
|
357
|
+
priority = priority_factory(value=1, default=True)
|
|
358
|
+
environment = environment_factory(value="PRD", default=True)
|
|
359
|
+
category = incident_category_factory()
|
|
360
|
+
|
|
361
|
+
# Form data without any custom fields
|
|
362
|
+
form_data = {
|
|
363
|
+
"title": "Basic incident without impacts",
|
|
364
|
+
"description": "Test incident with no customer or seller impact",
|
|
365
|
+
"incident_category": category.id,
|
|
366
|
+
"environment": [environment.id],
|
|
367
|
+
"platform": ["platform-FR"],
|
|
368
|
+
"priority": priority.id,
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
impacts_data: dict[str, Any] = {}
|
|
372
|
+
|
|
373
|
+
# Create form without impacts context
|
|
374
|
+
form = UnifiedIncidentFormSlack(
|
|
375
|
+
data=form_data,
|
|
376
|
+
impacts_data=impacts_data,
|
|
377
|
+
response_type="critical",
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Verify form is valid
|
|
381
|
+
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
382
|
+
|
|
383
|
+
# Verify custom fields are NOT in form fields (removed by _configure_field_visibility)
|
|
384
|
+
assert "zendesk_ticket_id" not in form.fields
|
|
385
|
+
assert "seller_contract_id" not in form.fields
|
|
386
|
+
|
|
387
|
+
# Mock signal receiver
|
|
388
|
+
signal_received = False
|
|
389
|
+
signal_data: dict[str, Any] = {}
|
|
390
|
+
|
|
391
|
+
def capture_signal(sender: Any, incident: Any, **kwargs: Any) -> None:
|
|
392
|
+
nonlocal signal_received, signal_data
|
|
393
|
+
signal_received = True
|
|
394
|
+
signal_data = {"incident": incident, **kwargs}
|
|
395
|
+
|
|
396
|
+
create_incident_conversation.connect(capture_signal, weak=False)
|
|
397
|
+
|
|
398
|
+
try:
|
|
399
|
+
# Mock SelectImpactForm.save since it's not what we're testing here
|
|
400
|
+
with patch("firefighter.incidents.forms.unified_incident.SelectImpactForm") as mock_select_impact:
|
|
401
|
+
mock_form_instance = MagicMock()
|
|
402
|
+
mock_form_instance.save.return_value = None
|
|
403
|
+
mock_select_impact.return_value = mock_form_instance
|
|
404
|
+
|
|
405
|
+
# Trigger workflow
|
|
406
|
+
user = user_factory()
|
|
407
|
+
form.trigger_incident_workflow(
|
|
408
|
+
creator=user,
|
|
409
|
+
impacts_data=impacts_data,
|
|
410
|
+
response_type="critical",
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Verify signal was sent
|
|
414
|
+
assert signal_received
|
|
415
|
+
|
|
416
|
+
# Verify jira_extra_fields exists - custom fields are None, but environments/platforms have defaults
|
|
417
|
+
jira_extra_fields = signal_data["jira_extra_fields"]
|
|
418
|
+
assert jira_extra_fields["zendesk_ticket_id"] is None
|
|
419
|
+
assert jira_extra_fields["seller_contract_id"] is None
|
|
420
|
+
assert jira_extra_fields["zoho_desk_ticket_id"] is None
|
|
421
|
+
assert jira_extra_fields["is_key_account"] is None
|
|
422
|
+
assert jira_extra_fields["is_seller_in_golden_list"] is None
|
|
423
|
+
|
|
424
|
+
# New fields: environments and platforms have default values (not None)
|
|
425
|
+
assert "environments" in jira_extra_fields
|
|
426
|
+
assert jira_extra_fields["environments"] == ["PRD"] # Default from form
|
|
427
|
+
assert "platforms" in jira_extra_fields
|
|
428
|
+
assert jira_extra_fields["platforms"] == ["platform-FR"] # Default from form
|
|
429
|
+
|
|
430
|
+
# Verify incident custom_fields only has environments and platforms (None values filtered out)
|
|
431
|
+
incident = signal_data["incident"]
|
|
432
|
+
assert len(incident.custom_fields) == 2
|
|
433
|
+
assert incident.custom_fields["environments"] == ["PRD"]
|
|
434
|
+
assert incident.custom_fields["platforms"] == ["platform-FR"]
|
|
435
|
+
|
|
436
|
+
finally:
|
|
437
|
+
create_incident_conversation.disconnect(capture_signal)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@pytest.mark.django_db
|
|
441
|
+
class TestOpenModalPreservesCustomFieldsContext:
|
|
442
|
+
"""Test that open.py properly passes context to form during validation and submission."""
|
|
443
|
+
|
|
444
|
+
def test_open_modal_passes_impacts_data_to_form_validation(
|
|
445
|
+
self,
|
|
446
|
+
priority_factory,
|
|
447
|
+
environment_factory,
|
|
448
|
+
incident_category_factory,
|
|
449
|
+
):
|
|
450
|
+
"""OpenModal._validate_details_form should pass open_incident_context to form initialization."""
|
|
451
|
+
# Setup
|
|
452
|
+
priority = priority_factory(value=1, default=True)
|
|
453
|
+
environment_factory(value="PRD", default=True)
|
|
454
|
+
incident_category_factory()
|
|
455
|
+
|
|
456
|
+
# Get customer impact from fixtures
|
|
457
|
+
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
458
|
+
customer_impact = ImpactLevel.objects.get(
|
|
459
|
+
impact_type=customers_impact_type,
|
|
460
|
+
value=LevelChoices.HIGH.value
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# Simulate the context as it exists in open.py
|
|
464
|
+
open_incident_context = {
|
|
465
|
+
"response_type": "critical",
|
|
466
|
+
"impact_form_data": {
|
|
467
|
+
"customers_impact": customer_impact,
|
|
468
|
+
},
|
|
469
|
+
"details_form_data": {
|
|
470
|
+
"priority": str(priority.id),
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
# Mock form data with zendesk field
|
|
475
|
+
details_form_data = {
|
|
476
|
+
"title": "Test incident",
|
|
477
|
+
"description": "Test description for validation",
|
|
478
|
+
"zendesk_ticket_id": "VALIDATION-123",
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
# Call _validate_details_form with context (this is the fix we applied)
|
|
482
|
+
_is_valid, _form_class, form = OpenModal._validate_details_form(
|
|
483
|
+
details_form_modal_class=OpeningUnifiedModal,
|
|
484
|
+
details_form_data=details_form_data,
|
|
485
|
+
open_incident_context=open_incident_context,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# Verify that the form has zendesk_ticket_id field
|
|
489
|
+
# This proves that impacts_data was passed correctly from open_incident_context
|
|
490
|
+
assert "zendesk_ticket_id" in form.fields, (
|
|
491
|
+
"zendesk_ticket_id should be in form fields - "
|
|
492
|
+
"this proves open_incident_context was passed correctly"
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Form will be invalid due to missing required fields, but that's expected
|
|
496
|
+
# We're only testing that the context was passed and fields were configured correctly
|
|
497
|
+
|
|
498
|
+
def test_open_modal_handle_modal_fn_passes_context_to_form_submission(
|
|
499
|
+
self,
|
|
500
|
+
priority_factory,
|
|
501
|
+
environment_factory,
|
|
502
|
+
incident_category_factory,
|
|
503
|
+
user_factory,
|
|
504
|
+
):
|
|
505
|
+
"""OpenModal.handle_modal_fn should pass impacts_data and response_type to form during submission."""
|
|
506
|
+
# This test verifies that the fix in open.py lines 612-657 works correctly
|
|
507
|
+
# Setup
|
|
508
|
+
priority = priority_factory(value=1, default=True)
|
|
509
|
+
environment = environment_factory(value="PRD", default=True)
|
|
510
|
+
category = incident_category_factory()
|
|
511
|
+
|
|
512
|
+
# Get customer impact from fixtures
|
|
513
|
+
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
514
|
+
customer_impact = ImpactLevel.objects.get(
|
|
515
|
+
impact_type=customers_impact_type,
|
|
516
|
+
value=LevelChoices.HIGH.value
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
# Simulate the context as OpenModal.handle_modal_fn would have it
|
|
520
|
+
impacts_data_for_context = {
|
|
521
|
+
"customers_impact": customer_impact,
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
form_data = {
|
|
525
|
+
"title": "Submission test with Zendesk",
|
|
526
|
+
"description": "Test that form receives context during submission",
|
|
527
|
+
"incident_category": category.id,
|
|
528
|
+
"environment": [environment.id],
|
|
529
|
+
"platform": ["platform-FR"],
|
|
530
|
+
"priority": priority.id,
|
|
531
|
+
"zendesk_ticket_id": "SUBMIT-456",
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
# This simulates what happens in open.py after our fix (using inspect.signature)
|
|
535
|
+
init_params = inspect.signature(UnifiedIncidentForm.__init__).parameters
|
|
536
|
+
|
|
537
|
+
form_kwargs: dict[str, Any] = {}
|
|
538
|
+
if "impacts_data" in init_params:
|
|
539
|
+
form_kwargs["impacts_data"] = impacts_data_for_context
|
|
540
|
+
if "response_type" in init_params:
|
|
541
|
+
form_kwargs["response_type"] = "critical"
|
|
542
|
+
|
|
543
|
+
# Create form with the kwargs (this is what open.py does now)
|
|
544
|
+
form = UnifiedIncidentForm(form_data, **form_kwargs)
|
|
545
|
+
|
|
546
|
+
# Verify form is valid
|
|
547
|
+
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
548
|
+
|
|
549
|
+
# Verify zendesk_ticket_id is in cleaned_data
|
|
550
|
+
assert form.cleaned_data["zendesk_ticket_id"] == "SUBMIT-456"
|
|
551
|
+
|
|
552
|
+
# Verify that trigger_incident_workflow will send the field to signal
|
|
553
|
+
signal_received = False
|
|
554
|
+
signal_data: dict[str, Any] = {}
|
|
555
|
+
|
|
556
|
+
def capture_signal(sender: Any, incident: Any, **kwargs: Any) -> None:
|
|
557
|
+
nonlocal signal_received, signal_data
|
|
558
|
+
signal_received = True
|
|
559
|
+
signal_data = {"incident": incident, **kwargs}
|
|
560
|
+
|
|
561
|
+
create_incident_conversation.connect(capture_signal, weak=False)
|
|
562
|
+
|
|
563
|
+
try:
|
|
564
|
+
# Mock SelectImpactForm.save since it's not what we're testing here
|
|
565
|
+
with patch("firefighter.incidents.forms.unified_incident.SelectImpactForm") as mock_select_impact:
|
|
566
|
+
mock_form_instance = MagicMock()
|
|
567
|
+
mock_form_instance.save.return_value = None
|
|
568
|
+
mock_select_impact.return_value = mock_form_instance
|
|
569
|
+
|
|
570
|
+
user = user_factory()
|
|
571
|
+
form.trigger_incident_workflow(
|
|
572
|
+
creator=user,
|
|
573
|
+
impacts_data=impacts_data_for_context,
|
|
574
|
+
response_type="critical",
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
assert signal_received
|
|
578
|
+
assert signal_data["jira_extra_fields"]["zendesk_ticket_id"] == "SUBMIT-456"
|
|
579
|
+
|
|
580
|
+
finally:
|
|
581
|
+
create_incident_conversation.disconnect(capture_signal)
|