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
@@ -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)