firefighter-incident 0.0.13__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.
Files changed (136) hide show
  1. firefighter/_version.py +16 -3
  2. firefighter/api/serializers.py +17 -8
  3. firefighter/api/urls.py +8 -1
  4. firefighter/api/views/_base.py +1 -1
  5. firefighter/api/views/components.py +5 -5
  6. firefighter/api/views/incidents.py +9 -9
  7. firefighter/confluence/signals/incident_updated.py +2 -2
  8. firefighter/firefighter/settings/components/raid.py +3 -0
  9. firefighter/incidents/admin.py +24 -24
  10. firefighter/incidents/enums.py +22 -2
  11. firefighter/incidents/factories.py +14 -5
  12. firefighter/incidents/forms/close_incident.py +4 -4
  13. firefighter/incidents/forms/closure_reason.py +45 -0
  14. firefighter/incidents/forms/create_incident.py +4 -4
  15. firefighter/incidents/forms/unified_incident.py +406 -0
  16. firefighter/incidents/forms/update_status.py +91 -5
  17. firefighter/incidents/menus.py +2 -2
  18. firefighter/incidents/migrations/0005_enable_from_p1_to_p5_priority.py +7 -5
  19. firefighter/incidents/migrations/0009_update_sla.py +7 -5
  20. firefighter/incidents/migrations/0020_create_incident_category_model.py +64 -0
  21. firefighter/incidents/migrations/0021_copy_component_data_to_incident_category.py +57 -0
  22. firefighter/incidents/migrations/0022_add_incident_category_fields.py +34 -0
  23. firefighter/incidents/migrations/0023_populate_incident_category_references.py +57 -0
  24. firefighter/incidents/migrations/0024_remove_component_fields_and_model.py +26 -0
  25. firefighter/incidents/migrations/0025_make_incident_category_required.py +24 -0
  26. firefighter/incidents/migrations/0026_alter_incidentcategory_options_and_more.py +39 -0
  27. firefighter/incidents/migrations/0027_add_closure_fields.py +40 -0
  28. firefighter/incidents/migrations/0028_add_closure_reason_constraint.py +33 -0
  29. firefighter/incidents/migrations/0029_add_custom_fields_to_incident.py +22 -0
  30. firefighter/incidents/models/__init__.py +1 -1
  31. firefighter/incidents/models/group.py +1 -1
  32. firefighter/incidents/models/incident.py +47 -20
  33. firefighter/incidents/models/{component.py → incident_category.py} +30 -29
  34. firefighter/incidents/models/incident_update.py +3 -3
  35. firefighter/incidents/static/css/main.min.css +1 -1
  36. firefighter/incidents/tables.py +9 -9
  37. firefighter/incidents/templates/layouts/partials/incident_card.html +1 -1
  38. firefighter/incidents/templates/layouts/partials/incident_timeline.html +2 -2
  39. firefighter/incidents/templates/layouts/partials/status_pill.html +1 -1
  40. firefighter/incidents/templates/pages/{component_detail.html → incident_category_detail.html} +13 -13
  41. firefighter/incidents/templates/pages/{component_list.html → incident_category_list.html} +2 -2
  42. firefighter/incidents/templates/pages/incident_detail.html +3 -3
  43. firefighter/incidents/urls.py +6 -6
  44. firefighter/incidents/views/components/details.py +9 -9
  45. firefighter/incidents/views/components/list.py +9 -9
  46. firefighter/incidents/views/reports.py +5 -5
  47. firefighter/incidents/views/users/details.py +2 -2
  48. firefighter/incidents/views/views.py +7 -7
  49. firefighter/jira_app/client.py +1 -1
  50. firefighter/logging/custom_json_formatter.py +2 -1
  51. firefighter/pagerduty/tasks/trigger_oncall.py +1 -1
  52. firefighter/raid/admin.py +0 -11
  53. firefighter/raid/apps.py +9 -26
  54. firefighter/raid/client.py +5 -5
  55. firefighter/raid/forms.py +84 -213
  56. firefighter/raid/migrations/0003_delete_raidarea.py +16 -0
  57. firefighter/raid/models.py +2 -21
  58. firefighter/raid/serializers.py +5 -4
  59. firefighter/raid/service.py +29 -27
  60. firefighter/raid/signals/incident_created.py +42 -15
  61. firefighter/raid/signals/incident_updated.py +3 -2
  62. firefighter/raid/utils.py +1 -1
  63. firefighter/raid/views/__init__.py +1 -1
  64. firefighter/slack/admin.py +8 -8
  65. firefighter/slack/management/commands/switch_test_users.py +272 -0
  66. firefighter/slack/messages/slack_messages.py +24 -9
  67. firefighter/slack/migrations/0005_add_incident_categories_fields.py +33 -0
  68. firefighter/slack/migrations/0006_copy_components_to_incident_categories.py +57 -0
  69. firefighter/slack/migrations/0007_remove_components_fields.py +22 -0
  70. firefighter/slack/migrations/0008_alter_conversation_incident_categories_and_more.py +33 -0
  71. firefighter/slack/models/conversation.py +3 -3
  72. firefighter/slack/models/incident_channel.py +1 -1
  73. firefighter/slack/models/user.py +1 -1
  74. firefighter/slack/models/user_group.py +3 -3
  75. firefighter/slack/rules.py +2 -2
  76. firefighter/slack/signals/create_incident_conversation.py +6 -0
  77. firefighter/slack/signals/get_users.py +2 -2
  78. firefighter/slack/signals/incident_updated.py +8 -2
  79. firefighter/slack/utils.py +2 -2
  80. firefighter/slack/views/events/home.py +2 -2
  81. firefighter/slack/views/modals/__init__.py +4 -0
  82. firefighter/slack/views/modals/base_modal/form_utils.py +78 -0
  83. firefighter/slack/views/modals/close.py +18 -5
  84. firefighter/slack/views/modals/closure_reason.py +193 -0
  85. firefighter/slack/views/modals/open.py +83 -12
  86. firefighter/slack/views/modals/opening/check_current_incidents.py +2 -2
  87. firefighter/slack/views/modals/opening/details/unified.py +203 -0
  88. firefighter/slack/views/modals/opening/select_impact.py +5 -2
  89. firefighter/slack/views/modals/opening/set_details.py +3 -2
  90. firefighter/slack/views/modals/postmortem.py +10 -2
  91. firefighter/slack/views/modals/update_status.py +32 -6
  92. firefighter/slack/views/modals/utils.py +51 -0
  93. firefighter_fixtures/incidents/{components.json → incident_categories.json} +52 -52
  94. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/METADATA +2 -2
  95. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/RECORD +133 -88
  96. firefighter_tests/conftest.py +4 -5
  97. firefighter_tests/test_api/test_api_landbot.py +1 -1
  98. firefighter_tests/test_firefighter/test_sso.py +146 -0
  99. firefighter_tests/test_incidents/test_enums.py +100 -0
  100. firefighter_tests/test_incidents/test_forms/conftest.py +179 -0
  101. firefighter_tests/test_incidents/test_forms/test_closure_reason.py +91 -0
  102. firefighter_tests/test_incidents/test_forms/test_form_utils.py +15 -15
  103. firefighter_tests/test_incidents/test_forms/test_unified_incident_form.py +570 -0
  104. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +581 -0
  105. firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +410 -0
  106. firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +343 -0
  107. firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +167 -0
  108. firefighter_tests/test_incidents/test_incident_urls.py +3 -3
  109. firefighter_tests/test_incidents/test_models/test_incident_category.py +165 -0
  110. firefighter_tests/test_incidents/test_models/test_incident_model.py +70 -2
  111. firefighter_tests/test_raid/conftest.py +154 -0
  112. firefighter_tests/test_raid/test_p1_p3_jira_fields.py +372 -0
  113. firefighter_tests/test_raid/test_priority_mapping.py +267 -0
  114. firefighter_tests/test_raid/test_raid_client.py +580 -0
  115. firefighter_tests/test_raid/test_raid_forms.py +552 -0
  116. firefighter_tests/test_raid/test_raid_models.py +185 -0
  117. firefighter_tests/test_raid/test_raid_serializers.py +507 -0
  118. firefighter_tests/test_raid/test_raid_service.py +442 -0
  119. firefighter_tests/test_raid/test_raid_signals.py +187 -0
  120. firefighter_tests/test_raid/test_raid_views.py +196 -0
  121. firefighter_tests/test_slack/messages/__init__.py +0 -0
  122. firefighter_tests/test_slack/messages/test_slack_messages.py +367 -0
  123. firefighter_tests/test_slack/views/modals/conftest.py +140 -0
  124. firefighter_tests/test_slack/views/modals/test_close.py +71 -9
  125. firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +138 -0
  126. firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py +249 -0
  127. firefighter_tests/test_slack/views/modals/test_open.py +146 -2
  128. firefighter_tests/test_slack/views/modals/test_opening_unified.py +421 -0
  129. firefighter_tests/test_slack/views/modals/test_update_status.py +331 -7
  130. firefighter_tests/test_slack/views/modals/test_utils.py +135 -0
  131. firefighter/raid/views/open_normal.py +0 -139
  132. firefighter/slack/views/modals/opening/details/critical.py +0 -88
  133. firefighter_fixtures/raid/area.json +0 -1
  134. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/WHEEL +0 -0
  135. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/entry_points.txt +0 -0
  136. {firefighter_incident-0.0.13.dist-info → firefighter_incident-0.0.15.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,570 @@
1
+ """Tests for unified incident form with conditional field visibility."""
2
+ from __future__ import annotations
3
+
4
+ import pytest
5
+ from django import forms
6
+
7
+ from firefighter.incidents.models.impact import LevelChoices
8
+ from firefighter.slack.views.modals.opening.details.unified import (
9
+ UnifiedIncidentFormSlack,
10
+ )
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestUnifiedIncidentFormFieldVisibility:
15
+ """Test that UnifiedIncidentForm shows/hides fields based on impacts and response_type."""
16
+
17
+ def test_critical_incident_no_impacts_shows_base_fields_only(self, priority_factory):
18
+ """P1-P3 with no impacts should show only base fields."""
19
+ priority_factory(value=1, default=True)
20
+
21
+ form = UnifiedIncidentFormSlack(
22
+ impacts_data={},
23
+ response_type="critical",
24
+ )
25
+
26
+ # Base fields always visible
27
+ assert "title" in form.fields
28
+ assert "description" in form.fields
29
+ assert "incident_category" in form.fields
30
+ assert "environment" in form.fields
31
+ assert "platform" in form.fields
32
+ assert "priority" in form.fields
33
+
34
+ # Conditional fields should be removed
35
+ assert "suggested_team_routing" not in form.fields
36
+ assert "zendesk_ticket_id" not in form.fields
37
+ assert "seller_contract_id" not in form.fields
38
+ assert "is_key_account" not in form.fields
39
+ assert "is_seller_in_golden_list" not in form.fields
40
+ assert "zoho_desk_ticket_id" not in form.fields
41
+
42
+ def test_normal_incident_shows_feature_team_field(self, priority_factory):
43
+ """P4-P5 should show suggested_team_routing field."""
44
+ priority_factory(value=4, default=True)
45
+
46
+ form = UnifiedIncidentFormSlack(
47
+ impacts_data={},
48
+ response_type="normal",
49
+ )
50
+
51
+ # Should include feature team field for normal incidents
52
+ assert "suggested_team_routing" in form.fields
53
+
54
+ # But not impact-specific fields
55
+ assert "zendesk_ticket_id" not in form.fields
56
+ assert "seller_contract_id" not in form.fields
57
+
58
+ def test_customer_impact_shows_zendesk_field(self, priority_factory, impact_level_factory):
59
+ """Customer impact should show zendesk_ticket_id field."""
60
+ priority_factory(value=1, default=True)
61
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
62
+
63
+ impacts_data = {"customers_impact": customer_impact}
64
+
65
+ form = UnifiedIncidentFormSlack(
66
+ impacts_data=impacts_data,
67
+ response_type="critical",
68
+ )
69
+
70
+ # Should include customer-specific fields
71
+ assert "zendesk_ticket_id" in form.fields
72
+
73
+ # But not seller fields
74
+ assert "seller_contract_id" not in form.fields
75
+ assert "zoho_desk_ticket_id" not in form.fields
76
+
77
+ def test_seller_impact_shows_seller_fields(self, priority_factory, impact_level_factory):
78
+ """Seller impact should show all seller-related fields."""
79
+ priority_factory(value=1, default=True)
80
+ seller_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Sellers")
81
+
82
+ impacts_data = {"sellers_impact": seller_impact}
83
+
84
+ form = UnifiedIncidentFormSlack(
85
+ impacts_data=impacts_data,
86
+ response_type="critical",
87
+ )
88
+
89
+ # Should include all seller-specific fields
90
+ assert "seller_contract_id" in form.fields
91
+ assert "is_key_account" in form.fields
92
+ assert "is_seller_in_golden_list" in form.fields
93
+ assert "zoho_desk_ticket_id" in form.fields
94
+
95
+ # But not customer fields
96
+ assert "zendesk_ticket_id" not in form.fields
97
+
98
+ def test_both_customer_and_seller_impact_shows_all_fields(
99
+ self, priority_factory, impact_level_factory
100
+ ):
101
+ """Both customer and seller impacts should show all related fields."""
102
+ priority_factory(value=1, default=True)
103
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
104
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
105
+
106
+ impacts_data = {
107
+ "customers_impact": customer_impact,
108
+ "sellers_impact": seller_impact,
109
+ }
110
+
111
+ form = UnifiedIncidentFormSlack(
112
+ impacts_data=impacts_data,
113
+ response_type="critical",
114
+ )
115
+
116
+ # Should include both customer and seller fields
117
+ assert "zendesk_ticket_id" in form.fields
118
+ assert "seller_contract_id" in form.fields
119
+ assert "is_key_account" in form.fields
120
+ assert "zoho_desk_ticket_id" in form.fields
121
+
122
+ def test_none_impact_level_hides_fields(self, priority_factory, impact_level_factory):
123
+ """Impact level NONE should not show impact-specific fields."""
124
+ priority_factory(value=1, default=True)
125
+ customer_impact = impact_level_factory(value=LevelChoices.NONE, impact__name="Customers")
126
+
127
+ impacts_data = {"customers_impact": customer_impact}
128
+
129
+ form = UnifiedIncidentFormSlack(
130
+ impacts_data=impacts_data,
131
+ response_type="critical",
132
+ )
133
+
134
+ # Should NOT include customer fields when impact is NONE
135
+ assert "zendesk_ticket_id" not in form.fields
136
+
137
+ def test_employee_impact_shows_no_specific_fields(self, priority_factory, impact_level_factory):
138
+ """Employee impact should not show customer or seller specific fields."""
139
+ priority_factory(value=1, default=True)
140
+ employee_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Employees")
141
+
142
+ impacts_data = {"employees_impact": employee_impact}
143
+
144
+ form = UnifiedIncidentFormSlack(
145
+ impacts_data=impacts_data,
146
+ response_type="critical",
147
+ )
148
+
149
+ # Base fields should be present
150
+ assert "title" in form.fields
151
+ assert "description" in form.fields
152
+
153
+ # Should NOT include customer or seller fields
154
+ assert "zendesk_ticket_id" not in form.fields
155
+ assert "seller_contract_id" not in form.fields
156
+ assert "zoho_desk_ticket_id" not in form.fields
157
+
158
+ def test_business_impact_explicit_shows_no_specific_fields(self, priority_factory, impact_level_factory):
159
+ """Business impact should not show customer or seller specific fields."""
160
+ priority_factory(value=1, default=True)
161
+ business_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Business")
162
+
163
+ impacts_data = {"business_impact": business_impact}
164
+
165
+ form = UnifiedIncidentFormSlack(
166
+ impacts_data=impacts_data,
167
+ response_type="critical",
168
+ )
169
+
170
+ # Base fields should be present
171
+ assert "title" in form.fields
172
+
173
+ # Should NOT include customer or seller fields
174
+ assert "zendesk_ticket_id" not in form.fields
175
+ assert "seller_contract_id" not in form.fields
176
+
177
+ def test_customer_and_employee_impact_shows_customer_fields_only(
178
+ self, priority_factory, impact_level_factory
179
+ ):
180
+ """Customer + Employee impact should show customer fields."""
181
+ priority_factory(value=1, default=True)
182
+ customer_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Customers")
183
+ employee_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Employees")
184
+
185
+ impacts_data = {
186
+ "customers_impact": customer_impact,
187
+ "employees_impact": employee_impact,
188
+ }
189
+
190
+ form = UnifiedIncidentFormSlack(
191
+ impacts_data=impacts_data,
192
+ response_type="critical",
193
+ )
194
+
195
+ # Should include customer fields
196
+ assert "zendesk_ticket_id" in form.fields
197
+
198
+ # But not seller fields
199
+ assert "seller_contract_id" not in form.fields
200
+
201
+ def test_seller_and_employee_impact_shows_seller_fields_only(
202
+ self, priority_factory, impact_level_factory
203
+ ):
204
+ """Seller + Employee impact should show seller fields."""
205
+ priority_factory(value=1, default=True)
206
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
207
+ employee_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Employees")
208
+
209
+ impacts_data = {
210
+ "sellers_impact": seller_impact,
211
+ "employees_impact": employee_impact,
212
+ }
213
+
214
+ form = UnifiedIncidentFormSlack(
215
+ impacts_data=impacts_data,
216
+ response_type="critical",
217
+ )
218
+
219
+ # Should include seller fields
220
+ assert "seller_contract_id" in form.fields
221
+ assert "zoho_desk_ticket_id" in form.fields
222
+
223
+ # But not customer fields
224
+ assert "zendesk_ticket_id" not in form.fields
225
+
226
+ def test_all_impacts_shows_all_fields(self, priority_factory, impact_level_factory):
227
+ """All impact types should show all specific fields."""
228
+ priority_factory(value=1, default=True)
229
+ customer_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Customers")
230
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
231
+ employee_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Employees")
232
+ business_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Business")
233
+
234
+ impacts_data = {
235
+ "customers_impact": customer_impact,
236
+ "sellers_impact": seller_impact,
237
+ "employees_impact": employee_impact,
238
+ "business_impact": business_impact,
239
+ }
240
+
241
+ form = UnifiedIncidentFormSlack(
242
+ impacts_data=impacts_data,
243
+ response_type="critical",
244
+ )
245
+
246
+ # Should include both customer and seller fields
247
+ assert "zendesk_ticket_id" in form.fields
248
+ assert "seller_contract_id" in form.fields
249
+ assert "is_key_account" in form.fields
250
+ assert "zoho_desk_ticket_id" in form.fields
251
+
252
+ def test_priority_field_is_hidden_input(self, priority_factory):
253
+ """Priority field should be a hidden input in Slack form."""
254
+ priority_factory(value=1, default=True)
255
+
256
+ form = UnifiedIncidentFormSlack(
257
+ impacts_data={},
258
+ response_type="critical",
259
+ )
260
+
261
+ # Priority field should exist but be hidden
262
+ assert "priority" in form.fields
263
+ assert isinstance(form.fields["priority"].widget, forms.HiddenInput)
264
+
265
+
266
+ @pytest.mark.django_db
267
+ class TestImpactLevelBehavior:
268
+ """Test behavior with different impact levels (SEV, MIN, NONE)."""
269
+
270
+ def test_customer_impact_min_shows_zendesk(self, priority_factory, impact_level_factory):
271
+ """Customer impact with MIN level should show Zendesk field."""
272
+ priority_factory(value=1, default=True)
273
+ customer_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Customers")
274
+
275
+ form = UnifiedIncidentFormSlack(
276
+ impacts_data={"customers_impact": customer_impact},
277
+ response_type="critical",
278
+ )
279
+
280
+ assert "zendesk_ticket_id" in form.fields
281
+
282
+ def test_customer_impact_sev_shows_zendesk(self, priority_factory, impact_level_factory):
283
+ """Customer impact with SEV level should show Zendesk field."""
284
+ priority_factory(value=1, default=True)
285
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
286
+
287
+ form = UnifiedIncidentFormSlack(
288
+ impacts_data={"customers_impact": customer_impact},
289
+ response_type="critical",
290
+ )
291
+
292
+ assert "zendesk_ticket_id" in form.fields
293
+
294
+ def test_seller_impact_min_shows_fields(self, priority_factory, impact_level_factory):
295
+ """Seller impact with MIN level should show seller fields."""
296
+ priority_factory(value=1, default=True)
297
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
298
+
299
+ form = UnifiedIncidentFormSlack(
300
+ impacts_data={"sellers_impact": seller_impact},
301
+ response_type="critical",
302
+ )
303
+
304
+ assert "seller_contract_id" in form.fields
305
+ assert "zoho_desk_ticket_id" in form.fields
306
+
307
+ def test_seller_impact_sev_shows_fields(self, priority_factory, impact_level_factory):
308
+ """Seller impact with SEV level should show seller fields."""
309
+ priority_factory(value=1, default=True)
310
+ seller_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Sellers")
311
+
312
+ form = UnifiedIncidentFormSlack(
313
+ impacts_data={"sellers_impact": seller_impact},
314
+ response_type="critical",
315
+ )
316
+
317
+ assert "seller_contract_id" in form.fields
318
+ assert "zoho_desk_ticket_id" in form.fields
319
+
320
+ def test_multiple_none_impacts_hides_all_fields(self, priority_factory, impact_level_factory):
321
+ """All impacts at NONE level should not show any specific fields."""
322
+ priority_factory(value=1, default=True)
323
+ customer_impact = impact_level_factory(value=LevelChoices.NONE, impact__name="Customers")
324
+ seller_impact = impact_level_factory(value=LevelChoices.NONE, impact__name="Sellers")
325
+
326
+ form = UnifiedIncidentFormSlack(
327
+ impacts_data={
328
+ "customers_impact": customer_impact,
329
+ "sellers_impact": seller_impact,
330
+ },
331
+ response_type="critical",
332
+ )
333
+
334
+ assert "zendesk_ticket_id" not in form.fields
335
+ assert "seller_contract_id" not in form.fields
336
+
337
+
338
+ @pytest.mark.django_db
339
+ class TestNormalIncidentBehavior:
340
+ """Test P4-P5 normal incident specific behavior."""
341
+
342
+ def test_normal_incident_with_customer_impact_shows_both_team_and_zendesk(
343
+ self, priority_factory, impact_level_factory
344
+ ):
345
+ """P4-P5 with customer impact should show both feature team and Zendesk."""
346
+ priority_factory(value=4, default=True)
347
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
348
+
349
+ form = UnifiedIncidentFormSlack(
350
+ impacts_data={"customers_impact": customer_impact},
351
+ response_type="normal",
352
+ )
353
+
354
+ # Should show both P4-P5 field and customer field
355
+ assert "suggested_team_routing" in form.fields
356
+ assert "zendesk_ticket_id" in form.fields
357
+
358
+ def test_normal_incident_with_seller_impact_shows_both_team_and_seller_fields(
359
+ self, priority_factory, impact_level_factory
360
+ ):
361
+ """P4-P5 with seller impact should show both feature team and seller fields."""
362
+ priority_factory(value=5, default=True)
363
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
364
+
365
+ form = UnifiedIncidentFormSlack(
366
+ impacts_data={"sellers_impact": seller_impact},
367
+ response_type="normal",
368
+ )
369
+
370
+ # Should show both P4-P5 field and seller fields
371
+ assert "suggested_team_routing" in form.fields
372
+ assert "seller_contract_id" in form.fields
373
+ assert "zoho_desk_ticket_id" in form.fields
374
+
375
+ def test_normal_incident_employee_only_shows_team_field_only(
376
+ self, priority_factory, impact_level_factory
377
+ ):
378
+ """P4-P5 with only employee impact should show only feature team."""
379
+ priority_factory(value=4, default=True)
380
+ employee_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Employees")
381
+
382
+ form = UnifiedIncidentFormSlack(
383
+ impacts_data={"employees_impact": employee_impact},
384
+ response_type="normal",
385
+ )
386
+
387
+ # Should only show P4-P5 field
388
+ assert "suggested_team_routing" in form.fields
389
+
390
+ # But not customer or seller fields
391
+ assert "zendesk_ticket_id" not in form.fields
392
+ assert "seller_contract_id" not in form.fields
393
+
394
+
395
+ @pytest.mark.django_db
396
+ class TestUnifiedIncidentFormValidation:
397
+ """Test form validation with conditional requirements."""
398
+
399
+ def test_suggested_team_routing_required_for_normal_incidents(
400
+ self, priority_factory
401
+ ):
402
+ """Feature Team should be required for P4-P5 incidents."""
403
+ priority_factory(value=4, default=True)
404
+
405
+ form = UnifiedIncidentFormSlack(
406
+ data={},
407
+ initial={"response_type": "normal"},
408
+ impacts_data={},
409
+ response_type="normal",
410
+ )
411
+
412
+ # Should be invalid because suggested_team_routing is required
413
+ assert not form.is_valid()
414
+ assert "suggested_team_routing" in form.errors
415
+
416
+
417
+ @pytest.mark.django_db
418
+ class TestGetVisibleFieldsForImpacts:
419
+ """Test the get_visible_fields_for_impacts method."""
420
+
421
+ def test_returns_base_fields_for_empty_impacts(self, priority_factory):
422
+ """Should return base fields when no impacts."""
423
+ priority_factory(value=1, default=True)
424
+ form = UnifiedIncidentFormSlack()
425
+
426
+ visible = form.get_visible_fields_for_impacts({}, "critical")
427
+
428
+ assert "title" in visible
429
+ assert "description" in visible
430
+ assert "incident_category" in visible
431
+ assert "environment" in visible
432
+ assert "platform" in visible
433
+ assert "priority" in visible
434
+
435
+ # Should not include conditional fields
436
+ assert "suggested_team_routing" not in visible
437
+ assert "zendesk_ticket_id" not in visible
438
+
439
+ def test_includes_feature_team_for_normal_response(self, priority_factory):
440
+ """Should include suggested_team_routing for normal incidents."""
441
+ priority_factory(value=4, default=True)
442
+ form = UnifiedIncidentFormSlack()
443
+
444
+ visible = form.get_visible_fields_for_impacts({}, "normal")
445
+
446
+ assert "suggested_team_routing" in visible
447
+
448
+
449
+ @pytest.mark.django_db
450
+ class TestGetVisibleFieldsForImpactsWithUUIDs:
451
+ """Test the get_visible_fields_for_impacts method with UUID strings (Slack use case)."""
452
+
453
+ def test_customer_impact_uuid_shows_zendesk_field(self, priority_factory, impact_level_factory):
454
+ """Customer impact UUID should show zendesk_ticket_id field."""
455
+ priority_factory(value=1, default=True)
456
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
457
+
458
+ # Pass UUID string instead of object (simulates Slack form submission)
459
+ impacts_data = {"set_impact_type_customers_impact": str(customer_impact.id)}
460
+
461
+ form = UnifiedIncidentFormSlack(
462
+ impacts_data=impacts_data,
463
+ response_type="critical",
464
+ )
465
+
466
+ # Should include customer-specific fields
467
+ assert "zendesk_ticket_id" in form.fields
468
+
469
+ # But not seller fields
470
+ assert "seller_contract_id" not in form.fields
471
+ assert "zoho_desk_ticket_id" not in form.fields
472
+
473
+ def test_seller_impact_uuid_shows_seller_fields(self, priority_factory, impact_level_factory):
474
+ """Seller impact UUID should show all seller-related fields."""
475
+ priority_factory(value=1, default=True)
476
+ seller_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Sellers")
477
+
478
+ # Pass UUID string instead of object
479
+ impacts_data = {"set_impact_type_sellers_impact": str(seller_impact.id)}
480
+
481
+ form = UnifiedIncidentFormSlack(
482
+ impacts_data=impacts_data,
483
+ response_type="critical",
484
+ )
485
+
486
+ # Should include all seller-specific fields
487
+ assert "seller_contract_id" in form.fields
488
+ assert "is_key_account" in form.fields
489
+ assert "is_seller_in_golden_list" in form.fields
490
+ assert "zoho_desk_ticket_id" in form.fields
491
+
492
+ # But not customer fields
493
+ assert "zendesk_ticket_id" not in form.fields
494
+
495
+ def test_none_impact_uuid_hides_fields(self, priority_factory, impact_level_factory):
496
+ """Impact level NONE UUID should not show impact-specific fields."""
497
+ priority_factory(value=1, default=True)
498
+ customer_impact = impact_level_factory(value=LevelChoices.NONE, impact__name="Customers")
499
+
500
+ # Pass UUID string for NONE impact
501
+ impacts_data = {"set_impact_type_customers_impact": str(customer_impact.id)}
502
+
503
+ form = UnifiedIncidentFormSlack(
504
+ impacts_data=impacts_data,
505
+ response_type="critical",
506
+ )
507
+
508
+ # Should NOT include customer fields when impact is NONE
509
+ assert "zendesk_ticket_id" not in form.fields
510
+
511
+ def test_both_customer_and_seller_impact_uuids_show_all_fields(
512
+ self, priority_factory, impact_level_factory
513
+ ):
514
+ """Both customer and seller impact UUIDs should show all related fields."""
515
+ priority_factory(value=1, default=True)
516
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
517
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
518
+
519
+ # Pass UUID strings for both impacts
520
+ impacts_data = {
521
+ "set_impact_type_customers_impact": str(customer_impact.id),
522
+ "set_impact_type_sellers_impact": str(seller_impact.id),
523
+ }
524
+
525
+ form = UnifiedIncidentFormSlack(
526
+ impacts_data=impacts_data,
527
+ response_type="critical",
528
+ )
529
+
530
+ # Should include both customer and seller fields
531
+ assert "zendesk_ticket_id" in form.fields
532
+ assert "seller_contract_id" in form.fields
533
+ assert "is_key_account" in form.fields
534
+ assert "zoho_desk_ticket_id" in form.fields
535
+
536
+ def test_invalid_uuid_hides_fields(self, priority_factory):
537
+ """Invalid/non-existent UUID should not show impact-specific fields."""
538
+ priority_factory(value=1, default=True)
539
+
540
+ # Pass invalid UUID
541
+ impacts_data = {"set_impact_type_customers_impact": "00000000-0000-0000-0000-000000000000"}
542
+
543
+ form = UnifiedIncidentFormSlack(
544
+ impacts_data=impacts_data,
545
+ response_type="critical",
546
+ )
547
+
548
+ # Should NOT include customer fields for invalid UUID
549
+ assert "zendesk_ticket_id" not in form.fields
550
+
551
+ def test_mixed_objects_and_uuids(self, priority_factory, impact_level_factory):
552
+ """Should handle mix of ImpactLevel objects and UUID strings."""
553
+ priority_factory(value=1, default=True)
554
+ customer_impact = impact_level_factory(value=LevelChoices.HIGH, impact__name="Customers")
555
+ seller_impact = impact_level_factory(value=LevelChoices.LOW, impact__name="Sellers")
556
+
557
+ # Mix object and UUID string
558
+ impacts_data = {
559
+ "customers_impact": customer_impact, # Object
560
+ "set_impact_type_sellers_impact": str(seller_impact.id), # UUID string
561
+ }
562
+
563
+ form = UnifiedIncidentFormSlack(
564
+ impacts_data=impacts_data,
565
+ response_type="critical",
566
+ )
567
+
568
+ # Should handle both types correctly
569
+ assert "zendesk_ticket_id" in form.fields
570
+ assert "seller_contract_id" in form.fields