firefighter-incident 0.0.10__py3-none-any.whl → 0.0.11__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 CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.10'
21
- __version_tuple__ = version_tuple = (0, 0, 10)
20
+ __version__ = version = '0.0.11'
21
+ __version_tuple__ = version_tuple = (0, 0, 11)
@@ -12,7 +12,6 @@ from django.utils.translation import ngettext
12
12
  from slack_sdk.models.blocks.basic_components import MarkdownTextObject, Option
13
13
  from slack_sdk.models.blocks.block_elements import ButtonElement, StaticSelectElement
14
14
  from slack_sdk.models.blocks.blocks import (
15
- ActionsBlock,
16
15
  Block,
17
16
  ContextBlock,
18
17
  DividerBlock,
@@ -74,7 +73,9 @@ class OpenModal(SlackModal):
74
73
  # 1. Check if impact form is good
75
74
  is_impact_form_valid: bool = self._check_impact_form(open_incident_context)
76
75
 
77
- # 2. Check if we have a normal incident type
76
+ # 2. Auto-determine response type based on priority
77
+ self._auto_set_response_type(open_incident_context)
78
+
78
79
  incident_type_value: str | None = open_incident_context.get(
79
80
  "incident_type", None
80
81
  )
@@ -181,11 +182,18 @@ class OpenModal(SlackModal):
181
182
  SelectImpactModal,
182
183
  )
183
184
 
185
+ # Check if we have actual impacts (not all "NO") by checking if response_type is set
186
+ has_real_impacts = open_incident_context.get("response_type") is not None
187
+
188
+ # Show ✅ only if form is valid AND has real impacts, otherwise 📝
189
+ emoji = "✅" if impact_form_done and has_real_impacts else "📝"
190
+ button_text = "Edit impacts" if impact_form_done and has_real_impacts else "Set impacts"
191
+
184
192
  return [
185
193
  SectionBlock(
186
- text=f"{'✅' if impact_form_done else '📝'} First, define the incident impacts and priority.",
194
+ text=f"{emoji} First, define the incident impacts and priority.",
187
195
  accessory=ButtonElement(
188
- text="Edit impacts" if impact_form_done else "Set impacts",
196
+ text=button_text,
189
197
  action_id=SelectImpactModal.push_action,
190
198
  value=json.dumps(open_incident_context, cls=SlackFormJSONEncoder),
191
199
  ),
@@ -371,40 +379,53 @@ class OpenModal(SlackModal):
371
379
 
372
380
  return is_valid, details_form_class, details_form
373
381
 
382
+ @staticmethod
383
+ def _auto_set_response_type(open_incident_context: OpeningData) -> None:
384
+ """Auto-determine response type based on priority from impact form."""
385
+ impact_form_data = open_incident_context.get("impact_form_data")
386
+ if not impact_form_data:
387
+ # Clear response_type and priority if no impact data
388
+ open_incident_context.pop("response_type", None)
389
+ open_incident_context.pop("priority", None)
390
+ return
391
+
392
+ impact_form = SelectImpactForm(impact_form_data)
393
+ if not impact_form.is_valid():
394
+ # Clear response_type and priority if form is invalid
395
+ open_incident_context.pop("response_type", None)
396
+ open_incident_context.pop("priority", None)
397
+ return
398
+
399
+ priority_value = impact_form.suggest_priority_from_impact()
400
+
401
+ # If no impacts are selected (all set to "NO"), don't set priority/response_type
402
+ # Priority value 6 corresponds to LevelChoices.NONE.priority
403
+ if priority_value == 6:
404
+ open_incident_context.pop("response_type", None)
405
+ open_incident_context.pop("priority", None)
406
+ return
407
+
408
+ priority = Priority.objects.get(value=priority_value)
409
+
410
+ # Set priority in context
411
+ open_incident_context["priority"] = priority
412
+
413
+ # Set response type based on priority recommendation
414
+ if priority.recommended_response_type:
415
+ open_incident_context["response_type"] = cast("ResponseType | None", priority.recommended_response_type)
416
+ else:
417
+ # Default fallback: P1/P2/P3 = critical, P4/P5 = normal
418
+ response_type = cast("ResponseType", "critical" if priority_value < 4 else "normal")
419
+ open_incident_context["response_type"] = response_type
420
+
374
421
  @staticmethod
375
422
  def _build_response_type_blocks(open_incident_context: OpeningData) -> list[Block]:
376
423
  selected_response_type = open_incident_context.get("response_type")
377
424
  if selected_response_type not in {"critical", "normal"}:
378
425
  return []
379
426
 
380
- response_types: list[ResponseType] = cast(
381
- "list[ResponseType]", INCIDENT_TYPES.keys()
382
- )
383
- elements: list[ButtonElement] = []
384
-
385
- for response_type in response_types:
386
- if response_type != selected_response_type:
387
- continue
388
-
389
- is_selected = (
390
- open_incident_context.get("response_type") == response_type
391
- or len(INCIDENT_TYPES) == 1
392
- )
393
- style: str | None = "primary" if is_selected else None
394
- text = (
395
- ":slack: Slack :jira_new: Jira ticket"
396
- if response_type == "critical"
397
- else ":jira_new: Jira ticket"
398
- )
399
- button = ButtonElement(
400
- text=text,
401
- action_id=f"incident_open_set_res_type_{response_type}",
402
- value=json.dumps(open_incident_context, cls=SlackFormJSONEncoder),
403
- style=style,
404
- )
405
- elements.append(button)
406
-
407
- blocks: list[Block] = [ActionsBlock(elements=elements)]
427
+ blocks: list[Block] = []
428
+ # No buttons needed - response type is auto-determined
408
429
  if impact_form_data := open_incident_context.get("impact_form_data"):
409
430
  impact_form = SelectImpactForm(impact_form_data)
410
431
  if impact_form.is_valid():
@@ -414,6 +435,7 @@ class OpenModal(SlackModal):
414
435
  process = ":slack: Slack :jira_new: Jira ticket" if open_incident_context.get("response_type") == "critical" else ":jira_new: Jira ticket"
415
436
 
416
437
  impact_descriptions = OpenModal._get_impact_descriptions(open_incident_context)
438
+
417
439
  blocks.append(
418
440
  ContextBlock(
419
441
  elements=[
@@ -422,12 +444,12 @@ class OpenModal(SlackModal):
422
444
  f"> ⏱️ SLA: {priority.sla}\n"
423
445
  f"> :gear: Process: {process}\n"
424
446
  f"> :pushpin: Selected impacts:\n"
425
- f"{impact_descriptions}\n"
447
+ f"{impact_descriptions}"
426
448
  + (
427
449
  (
428
- "\n> Critical incidents are for *emergency* only"
450
+ "> :warning: Critical incidents are for *emergency* only"
429
451
  + (
430
- f" <{SLACK_SEVERITY_HELP_GUIDE_URL}|learn more>"
452
+ f" <{SLACK_SEVERITY_HELP_GUIDE_URL}|more info>"
431
453
  if SLACK_SEVERITY_HELP_GUIDE_URL
432
454
  else "."
433
455
  )
@@ -459,18 +481,56 @@ class OpenModal(SlackModal):
459
481
  @staticmethod
460
482
  def _get_impact_descriptions(open_incident_context: OpeningData) -> str:
461
483
  impact_form_data = open_incident_context.get("impact_form_data", {})
484
+ if not impact_form_data:
485
+ return ""
486
+
462
487
  impact_descriptions = ""
463
- if impact_form_data:
464
- for value in impact_form_data.values():
465
- if value.name != "NO" and value.description:
466
- if hasattr(value, "impact_type_id") and value.impact_type_id:
467
- impact_type = ImpactType.objects.get(pk=value.impact_type_id)
468
- if impact_type:
469
- impact_descriptions += f"> \u00A0\u00A0 :exclamation: {impact_type} - {value}\n"
470
- for line in str(value.description).splitlines():
471
- impact_descriptions += f"> \u00A0\u00A0\u00A0\u00A0\u00A0\u00A0 {line}\n"
488
+ for field_name, original_value in impact_form_data.items():
489
+ value = original_value
490
+ # Handle case where value might be an ID instead of an object
491
+ if isinstance(value, int | str) and not hasattr(value, "name"):
492
+ # Try to get the object from the database
493
+ form = SelectImpactForm()
494
+ if field_name in form.fields:
495
+ field = form.fields[field_name]
496
+ if hasattr(field, "queryset") and field.queryset is not None:
497
+ try:
498
+ value = field.queryset.get(pk=value)
499
+ except field.queryset.model.DoesNotExist:
500
+ logger.warning(f"Could not find impact object with pk={value} for field {field_name}")
501
+ continue
502
+
503
+ description = OpenModal._format_single_impact_description(value)
504
+ if description:
505
+ impact_descriptions += description
472
506
  return impact_descriptions
473
507
 
508
+ @staticmethod
509
+ def _format_single_impact_description(value: Any) -> str:
510
+ """Format a single impact value into description text."""
511
+ # Handle object with name and description attributes (impact levels)
512
+ if hasattr(value, "name") and hasattr(value, "description"):
513
+ if value.name == "NO" or not value.description:
514
+ return ""
515
+
516
+ description = ""
517
+ # Add impact type header if available
518
+ if hasattr(value, "impact_type_id") and value.impact_type_id:
519
+ try:
520
+ impact_type = ImpactType.objects.get(pk=value.impact_type_id)
521
+ # Use value.name instead of value to avoid showing IDs
522
+ description += f"> \u00A0\u00A0 :exclamation: {impact_type} - {value.name}\n"
523
+ except ImpactType.DoesNotExist:
524
+ description += f"> \u00A0\u00A0 :exclamation: {value.name}\n"
525
+
526
+ # Add description lines
527
+ for line in str(value.description).splitlines():
528
+ description += f"> \u00A0\u00A0\u00A0\u00A0\u00A0\u00A0 • {line}\n"
529
+ return description
530
+
531
+ # Skip string values - incident_type is handled separately, not in impact descriptions
532
+ return ""
533
+
474
534
  @staticmethod
475
535
  def get_details_modal_form_class(
476
536
  open_incident_context: OpeningData,
@@ -531,21 +591,7 @@ class OpenModal(SlackModal):
531
591
  logger.exception("Error triggering incident workflow")
532
592
  # XXX warn the user via DM!
533
593
 
534
- @app.action("incident_open_set_res_type_normal")
535
- @app.action("incident_open_set_res_type_critical")
536
- @staticmethod
537
- def handle_set_incident_response_type_action(
538
- ack: Ack, body: dict[str, Any]
539
- ) -> None:
540
- action_name: str = body.get("actions", [{}])[0].get("action_id", "")
541
- action_name = action_name.replace("incident_open_set_res_type_", "")
542
- opening_data = cast(
543
- "OpeningData", json.loads(body.get("actions", [{}])[0].get("value", {})) or {}
544
- )
545
-
546
- OpenModal._update_incident_modal(
547
- action_name, "response_type", ack, body, opening_data
548
- )
594
+ # Response type buttons removed - now auto-determined based on priority
549
595
 
550
596
  @app.action("set_type")
551
597
  @staticmethod
@@ -567,7 +613,11 @@ class OpenModal(SlackModal):
567
613
  body: dict[str, Any],
568
614
  opening_data: OpeningData,
569
615
  ) -> None:
570
- data: OpeningData = {**opening_data, metadata_key: action_value} # type: ignore
616
+ # Ensure we preserve all existing data, especially impact_form_data
617
+ data: OpeningData = OpeningData()
618
+ data.update(opening_data)
619
+ data[metadata_key] = action_value
620
+
571
621
  user = get_user_from_context(body)
572
622
  view = cls().build_modal_fn(open_incident_context=data, user=user)
573
623
 
@@ -205,7 +205,16 @@ class SelectImpactModal(
205
205
  def _calculate_proposed_incident_type(
206
206
  suggested_priority_value: int,
207
207
  ) -> ResponseType:
208
- return "critical" if suggested_priority_value <= 3 else "normal"
208
+ try:
209
+ priority = Priority.objects.get(value=suggested_priority_value)
210
+ # Use priority recommendation if available
211
+ if priority.recommended_response_type:
212
+ return cast("ResponseType", priority.recommended_response_type)
213
+ except Priority.DoesNotExist:
214
+ logger.warning(f"Priority with value {suggested_priority_value} does not exist")
215
+
216
+ # Fallback logic: P1/P2/P3 = critical, P4/P5 = normal
217
+ return cast("ResponseType", "critical" if suggested_priority_value < 4 else "normal")
209
218
 
210
219
  @staticmethod
211
220
  def _update_private_metadata(
@@ -230,12 +239,36 @@ class SelectImpactModal(
230
239
  )
231
240
  except queryset.model.DoesNotExist:
232
241
  form.form.data[field_name] = None # type: ignore
242
+ suggested_priority_value = form.form.suggest_priority_from_impact()
243
+
244
+ # If no impacts are selected (all set to "NO"), don't set priority/response_type
245
+ if suggested_priority_value == 6: # LevelChoices.NONE.priority
246
+ return OpeningData(
247
+ priority=None,
248
+ response_type=None,
249
+ impact_form_data=cast("dict[str, Any]", form.form.data),
250
+ details_form_data=private_metadata_raw.get("details_form_data", {}),
251
+ incident_type=private_metadata_raw.get("incident_type"),
252
+ )
253
+
254
+ try:
255
+ priority = Priority.objects.get(value=suggested_priority_value)
256
+ except Priority.DoesNotExist as err:
257
+ logger.exception(
258
+ f"Priority with value {suggested_priority_value} does not exist"
259
+ )
260
+ # Fallback to default priority (assuming P3 exists)
261
+ fallback_priority = Priority.objects.filter(value__gte=3).first()
262
+ if not fallback_priority:
263
+ # If no priority exists, create a minimal fallback
264
+ logger.exception("No priority found in database")
265
+ raise ValueError("No priority configuration found in database") from err
266
+ priority = fallback_priority
267
+
233
268
  return OpeningData(
234
- priority=Priority.objects.get(
235
- value=form.form.suggest_priority_from_impact()
236
- ),
269
+ priority=priority,
237
270
  response_type=SelectImpactModal._calculate_proposed_incident_type(
238
- form.form.suggest_priority_from_impact()
271
+ suggested_priority_value
239
272
  ),
240
273
  impact_form_data=cast("dict[str, Any]", form.form.data),
241
274
  details_form_data=private_metadata_raw.get("details_form_data", {}),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firefighter-incident
3
- Version: 0.0.10
3
+ Version: 0.0.11
4
4
  Summary: Incident Management tool made for Slack using Django
5
5
  Project-URL: Repository, https://github.com/ManoManoTech/firefighter-incident
6
6
  Project-URL: Documentation, https://manomanotech.github.io/firefighter-incident/latest/
@@ -6,7 +6,7 @@ gunicorn.conf.py,sha256=vHsTGjaKOr8FDMp6fTKYTX4AtokmPgYvvt5Mr0Q6APc,273
6
6
  main.py,sha256=CsbprHoOYhjCLpTJmq9Z_aRYFoFgWxoz2pDLuwm8Eqg,1558
7
7
  manage.py,sha256=5ivHGD13C6nJ8QvltKsJ9T9akA5he8da70HLWaEP3k8,689
8
8
  firefighter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- firefighter/_version.py,sha256=Gdu8ebkoRvvSp8uMsCgaPOF73AAzSJAhOOfRVCoJCTk,513
9
+ firefighter/_version.py,sha256=rk0lhpp6Em5toAI4J7GwApfOdY7w_QTcFpJpUR4GdVY,513
10
10
  firefighter/api/__init__.py,sha256=JQW0Bv6xwGqy7ioxx3h6UGMzkkJ4DntDpbvV1Ncgi8k,136
11
11
  firefighter/api/admin.py,sha256=x9Ysy-GiYjb0rynmFdS9g56e6n24fkN0ouGy5QD9Yrc,4629
12
12
  firefighter/api/apps.py,sha256=P5uU1_gMrDfzurdMbfqw1Bnb2uNKKcMq17WBPg2sLhc,204
@@ -385,7 +385,7 @@ firefighter/slack/views/modals/close.py,sha256=ur1SSRWk9NYFfL24gjOqoIiXKquDy6qeE
385
385
  firefighter/slack/views/modals/downgrade_workflow.py,sha256=S0y0_GYH4q7ewZUr_eA9Ly2c1FQueZzNCTiuIiWYUoY,3109
386
386
  firefighter/slack/views/modals/edit.py,sha256=60xav4XG4KGS9KknqsQNCQjl3qQzk7OtmHiEYTQ9pUk,3861
387
387
  firefighter/slack/views/modals/key_event_message.py,sha256=ga3-ITZyzJExwzctX-GfgnDqyQaxTfcqpqnOwY2E38M,5620
388
- firefighter/slack/views/modals/open.py,sha256=qA8lN8pAAdK7j1qa8zEwe-Fvu5Tu18UG3Bcwxpg6Ymk,23012
388
+ firefighter/slack/views/modals/open.py,sha256=LX9aBZ4bUosoffJlIepzYjpbf7LsvzppYAjqep8tVtM,25495
389
389
  firefighter/slack/views/modals/postmortem.py,sha256=AeEtmiam_XgCRxDmltKluNT2VN1gcuCB2VbYeeATVcA,2525
390
390
  firefighter/slack/views/modals/select.py,sha256=Y-Ji_ALnzhYkXDBAyi497UL1Xn2vCGqXCtj8eog75Jk,3312
391
391
  firefighter/slack/views/modals/send_sos.py,sha256=bP6HgYyDwPrIcTq7n_sQz6UQsxhYbvBDS4HjM0uRccA,4838
@@ -402,7 +402,7 @@ firefighter/slack/views/modals/base_modal/mixins.py,sha256=c7WYs0aXKXVktEMNSZ8IU
402
402
  firefighter/slack/views/modals/base_modal/modal_utils.py,sha256=1uHTlLxxeXUQttH3bHaehJwCuI6a-h04s-GzdnVA4sI,2459
403
403
  firefighter/slack/views/modals/opening/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
404
404
  firefighter/slack/views/modals/opening/check_current_incidents.py,sha256=28GN0SXP7rVPa55arX1aI98k45w9568GCRDA73eCHEM,2535
405
- firefighter/slack/views/modals/opening/select_impact.py,sha256=qTggWabR6lgs4OZa7wYEmFlQTbgUka1052n01NNLcsc,9754
405
+ firefighter/slack/views/modals/opening/select_impact.py,sha256=McVKE5z8vjcg0Z1kbqTsXBW9FvTqX02W6HiIPQ8cicI,11424
406
406
  firefighter/slack/views/modals/opening/set_details.py,sha256=i6zQM2FYz3Z6s5AZH7lXgB2e8yjS0rDwgfMBZaiOqIw,5791
407
407
  firefighter/slack/views/modals/opening/types.py,sha256=ETpp0DAz5OMI5h7iv62Of7yJCbI-Q4-3kKSS6msPQeY,563
408
408
  firefighter/slack/views/modals/opening/details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -444,12 +444,12 @@ firefighter_tests/test_slack/test_models/test_conversations.py,sha256=t3ttmgwiu7
444
444
  firefighter_tests/test_slack/test_models/test_incident_channel.py,sha256=qWoGe9iadmK6-R8usWvjH87AHRkvhG_dHQeC3kHeJrs,17487
445
445
  firefighter_tests/test_slack/test_models/test_slack_user.py,sha256=uzur-Rf03I5dpUTO4ZI6O1arBUrAorg1Zvgshf8M-J4,7000
446
446
  firefighter_tests/test_slack/views/modals/test_close.py,sha256=kcwGwonjIiniGb5f78ZwlKjuvYB-xat-SrbouV9VCEc,42894
447
- firefighter_tests/test_slack/views/modals/test_open.py,sha256=Iatphd7vnrEMrv8ysKxDVDAuZNEsVXBksIEpIaDHQss,4100
447
+ firefighter_tests/test_slack/views/modals/test_open.py,sha256=z3lvAPOXCUSt7i_9jWYcQWGIRwRg7Z1DT6AfMOK22_s,4900
448
448
  firefighter_tests/test_slack/views/modals/test_send_sos.py,sha256=_rE6jD-gOzcGyhlY0R9GzlGtPx65oOOguJYdENgxtLc,1289
449
449
  firefighter_tests/test_slack/views/modals/test_status.py,sha256=oQzPfwdg2tkbo9nfkO1GfS3WydxqSC6vy1AZjZDKT30,2226
450
450
  firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=Y8Oa_fraj1vtaGig9Y28_6tOWvMrRPS-wyg3rY-DHBk,39380
451
- firefighter_incident-0.0.10.dist-info/METADATA,sha256=hXTBfaxg9pA1hQgk54ZjQnhxRjQm3aZ6zbRiwUu2-pA,5488
452
- firefighter_incident-0.0.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
453
- firefighter_incident-0.0.10.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
454
- firefighter_incident-0.0.10.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
455
- firefighter_incident-0.0.10.dist-info/RECORD,,
451
+ firefighter_incident-0.0.11.dist-info/METADATA,sha256=9Xvtj0AnQmzza1kfVGGGEMcJJEokzGn9vUs6FzslagA,5488
452
+ firefighter_incident-0.0.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
453
+ firefighter_incident-0.0.11.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
454
+ firefighter_incident-0.0.11.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
455
+ firefighter_incident-0.0.11.dist-info/RECORD,,
@@ -1,12 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import Any
4
- from unittest.mock import MagicMock
4
+ from unittest.mock import MagicMock, Mock, patch
5
5
 
6
6
  import pytest
7
- from slack_sdk.models.blocks.block_elements import ButtonElement
8
7
  from slack_sdk.models.blocks.blocks import (
9
- ActionsBlock,
8
+ ContextBlock,
10
9
  )
11
10
 
12
11
  from firefighter.incidents.forms.create_incident import CreateIncidentFormBase
@@ -102,14 +101,32 @@ def test_validate_details_form_invalid() -> None:
102
101
 
103
102
 
104
103
  def test_build_response_type_blocks_bis(open_incident_context: OpeningData) -> None:
104
+ # With no impact_form_data, should return empty list
105
105
  open_incident_context["response_type"] = "critical"
106
106
  blocks = OpenModal._build_response_type_blocks(open_incident_context)
107
+ assert len(blocks) == 0
107
108
 
108
- assert len(blocks) == 1
109
- first_block = blocks[0]
110
- assert isinstance(first_block, ActionsBlock)
111
- assert len(first_block.elements) == 1
112
- assert all(isinstance(element, ButtonElement) for element in first_block.elements)
109
+ # With valid impact_form_data, should return context blocks
110
+ mock_impact_form = Mock()
111
+ mock_impact_form.is_valid.return_value = True
112
+ mock_impact_form.suggest_priority_from_impact.return_value = 1
113
+
114
+ # Mock Priority object
115
+ mock_priority = Mock()
116
+ mock_priority.emoji = "🔴"
117
+ mock_priority.description = "Critical"
118
+ mock_priority.sla = "15 min"
119
+ mock_priority.recommended_response_type = None
120
+
121
+ open_incident_context["impact_form_data"] = {"test_field": "test_value"}
122
+
123
+ with patch("firefighter.slack.views.modals.open.SelectImpactForm", return_value=mock_impact_form), \
124
+ patch("firefighter.slack.views.modals.open.Priority.objects.get", return_value=mock_priority), \
125
+ patch.object(OpenModal, "_get_impact_descriptions", return_value="Test impact"):
126
+ blocks = OpenModal._build_response_type_blocks(open_incident_context)
127
+ assert len(blocks) == 1
128
+ first_block = blocks[0]
129
+ assert isinstance(first_block, ContextBlock)
113
130
 
114
131
 
115
132
  @pytest.mark.django_db