firefighter-incident 0.0.16__py3-none-any.whl → 0.0.17__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/incidents/forms/edit.py +5 -3
- firefighter/incidents/forms/unified_incident.py +180 -56
- firefighter/incidents/forms/update_status.py +94 -58
- firefighter/incidents/forms/utils.py +14 -0
- firefighter/incidents/models/incident.py +3 -2
- firefighter/raid/apps.py +0 -1
- firefighter/slack/signals/__init__.py +16 -0
- firefighter/slack/signals/incident_updated.py +43 -1
- firefighter/slack/utils.py +43 -6
- firefighter/slack/views/modals/base_modal/form_utils.py +3 -1
- firefighter/slack/views/modals/downgrade_workflow.py +3 -1
- firefighter/slack/views/modals/edit.py +53 -7
- firefighter/slack/views/modals/opening/set_details.py +20 -0
- firefighter_fixtures/incidents/priorities.json +1 -1
- {firefighter_incident-0.0.16.dist-info → firefighter_incident-0.0.17.dist-info}/METADATA +1 -1
- {firefighter_incident-0.0.16.dist-info → firefighter_incident-0.0.17.dist-info}/RECORD +28 -29
- firefighter_tests/test_incidents/test_forms/test_unified_incident_form_integration.py +160 -23
- firefighter_tests/test_incidents/test_forms/test_unified_incident_form_p4_p5.py +38 -60
- firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py +35 -20
- firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py +8 -6
- firefighter_tests/test_slack/test_signals_downgrade.py +147 -0
- firefighter_tests/test_slack/views/modals/test_edit.py +324 -0
- firefighter_tests/test_slack/views/modals/test_opening_unified.py +42 -0
- firefighter_tests/test_slack/views/modals/test_update_status.py +72 -2
- firefighter/raid/signals/incident_created.py +0 -129
- firefighter_tests/test_raid/test_p1_p3_jira_fields.py +0 -372
- firefighter_tests/test_raid/test_priority_mapping.py +0 -267
- {firefighter_incident-0.0.16.dist-info → firefighter_incident-0.0.17.dist-info}/WHEEL +0 -0
- {firefighter_incident-0.0.16.dist-info → firefighter_incident-0.0.17.dist-info}/entry_points.txt +0 -0
- {firefighter_incident-0.0.16.dist-info → firefighter_incident-0.0.17.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
-
|
|
6
|
-
from django.conf import settings
|
|
7
|
-
from django.dispatch.dispatcher import receiver
|
|
8
|
-
|
|
9
|
-
from firefighter.jira_app.client import JiraAPIError, JiraUserNotFoundError
|
|
10
|
-
from firefighter.raid.client import client
|
|
11
|
-
from firefighter.raid.forms import prepare_jira_fields
|
|
12
|
-
from firefighter.raid.models import JiraTicket
|
|
13
|
-
from firefighter.raid.service import get_jira_user_from_user
|
|
14
|
-
from firefighter.slack.messages.slack_messages import (
|
|
15
|
-
SlackMessageIncidentDeclaredAnnouncement,
|
|
16
|
-
)
|
|
17
|
-
from firefighter.slack.signals import incident_channel_done
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING:
|
|
20
|
-
from firefighter.incidents.models.incident import Incident
|
|
21
|
-
from firefighter.slack.models.incident_channel import IncidentChannel
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger(__name__)
|
|
24
|
-
RAID_DEFAULT_JIRA_QRAFT_USER_ID: str = settings.RAID_DEFAULT_JIRA_QRAFT_USER_ID
|
|
25
|
-
APP_DISPLAY_NAME: str = settings.APP_DISPLAY_NAME
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@receiver(signal=incident_channel_done)
|
|
29
|
-
def create_ticket(
|
|
30
|
-
sender: Any, incident: Incident, channel: IncidentChannel, **kwargs: Any
|
|
31
|
-
) -> JiraTicket:
|
|
32
|
-
# pylint: disable=unused-argument
|
|
33
|
-
|
|
34
|
-
# Extract jira_extra_fields and impacts_data from kwargs (passed from unified incident form)
|
|
35
|
-
jira_extra_fields = kwargs.get("jira_extra_fields", {})
|
|
36
|
-
impacts_data = kwargs.get("impacts_data", {})
|
|
37
|
-
logger.info(f"CREATE_TICKET - kwargs keys: {list(kwargs.keys())}")
|
|
38
|
-
logger.info(f"CREATE_TICKET - jira_extra_fields received: {jira_extra_fields}")
|
|
39
|
-
|
|
40
|
-
jira_user = get_jira_user_from_user(incident.created_by)
|
|
41
|
-
account_id = jira_user.id
|
|
42
|
-
|
|
43
|
-
# Map Impact priority (1-5) to JIRA priority (1-5), fallback to P1 for invalid values
|
|
44
|
-
priority: int = incident.priority.value if 1 <= incident.priority.value <= 5 else 1
|
|
45
|
-
|
|
46
|
-
# Build enhanced description with incident metadata
|
|
47
|
-
description = f"""{incident.description}\n
|
|
48
|
-
\n
|
|
49
|
-
🧯 This incident has been created for a critical incident. Links below to Slack and {APP_DISPLAY_NAME}.\n
|
|
50
|
-
📦 Incident category: {incident.incident_category.name} ({incident.incident_category.group.name})\n
|
|
51
|
-
{incident.priority.emoji} Priority: {incident.priority.name}\n"""
|
|
52
|
-
|
|
53
|
-
# Prepare all Jira fields using the common function
|
|
54
|
-
# P1-P3 use first environment only (for backward compatibility)
|
|
55
|
-
environments = jira_extra_fields.get("environments", [incident.environment.value])
|
|
56
|
-
platforms = jira_extra_fields.get("platforms", ["platform-All"])
|
|
57
|
-
|
|
58
|
-
jira_fields = prepare_jira_fields(
|
|
59
|
-
title=incident.title,
|
|
60
|
-
description=description,
|
|
61
|
-
priority=priority,
|
|
62
|
-
reporter=account_id,
|
|
63
|
-
incident_category=incident.incident_category.name,
|
|
64
|
-
environments=[environments[0]] if environments else [incident.environment.value], # P1-P3: first only
|
|
65
|
-
platforms=platforms,
|
|
66
|
-
impacts_data=impacts_data,
|
|
67
|
-
optional_fields={
|
|
68
|
-
"zendesk_ticket_id": jira_extra_fields.get("zendesk_ticket_id", ""),
|
|
69
|
-
"seller_contract_id": jira_extra_fields.get("seller_contract_id", ""),
|
|
70
|
-
"zoho_desk_ticket_id": jira_extra_fields.get("zoho_desk_ticket_id", ""),
|
|
71
|
-
"is_key_account": jira_extra_fields.get("is_key_account"),
|
|
72
|
-
"is_seller_in_golden_list": jira_extra_fields.get("is_seller_in_golden_list"),
|
|
73
|
-
"suggested_team_routing": jira_extra_fields.get("suggested_team_routing"),
|
|
74
|
-
},
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
# Create Jira issue with all prepared fields
|
|
78
|
-
issue = client.create_issue(**jira_fields)
|
|
79
|
-
issue_id = issue.get("id")
|
|
80
|
-
if issue_id is None:
|
|
81
|
-
logger.error(f"Could not create Jira ticket for incident {incident.id}")
|
|
82
|
-
raise JiraAPIError("Could not create Jira ticket")
|
|
83
|
-
try:
|
|
84
|
-
default_jira_user = client.get_jira_user_from_jira_id(
|
|
85
|
-
RAID_DEFAULT_JIRA_QRAFT_USER_ID
|
|
86
|
-
)
|
|
87
|
-
except JiraUserNotFoundError:
|
|
88
|
-
logger.exception(
|
|
89
|
-
f"Could not find Jira default reporter user with account id {RAID_DEFAULT_JIRA_QRAFT_USER_ID}"
|
|
90
|
-
)
|
|
91
|
-
# Add watcher reporter
|
|
92
|
-
try:
|
|
93
|
-
client.jira.add_watcher(issue=issue_id, watcher=account_id)
|
|
94
|
-
except JiraAPIError:
|
|
95
|
-
logger.exception(
|
|
96
|
-
f"Could not add the watcher with account id {account_id} to the ticket {issue_id}"
|
|
97
|
-
)
|
|
98
|
-
# Removing default watcher TeamQraft
|
|
99
|
-
try:
|
|
100
|
-
client.jira.remove_watcher(issue=issue_id, watcher=default_jira_user)
|
|
101
|
-
except JiraAPIError:
|
|
102
|
-
logger.exception(
|
|
103
|
-
f"Could not remove the watcher {default_jira_user} to the ticket {issue_id}"
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
client.jira.add_simple_link(
|
|
107
|
-
issue=str(issue_id),
|
|
108
|
-
object={
|
|
109
|
-
"url": incident.status_page_url,
|
|
110
|
-
"title": f"{APP_DISPLAY_NAME} incident #{incident.id}",
|
|
111
|
-
},
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
client.jira.add_simple_link(
|
|
115
|
-
issue=str(issue_id),
|
|
116
|
-
object={"url": channel.link, "title": f"Slack conversation #{channel.name}"},
|
|
117
|
-
)
|
|
118
|
-
issue["business_impact"] = issue.get("business_impact", "")
|
|
119
|
-
jira_ticket = JiraTicket.objects.create(**issue, incident=incident)
|
|
120
|
-
|
|
121
|
-
channel.add_bookmark(
|
|
122
|
-
title="Jira ticket",
|
|
123
|
-
link=jira_ticket.url,
|
|
124
|
-
emoji=":jira_new:",
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
channel.send_message_and_save(SlackMessageIncidentDeclaredAnnouncement(incident))
|
|
128
|
-
|
|
129
|
-
return jira_ticket
|
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
"""Tests for P1-P3 (critical) incident Jira ticket creation with all custom fields.
|
|
2
|
-
|
|
3
|
-
This test suite verifies that P1-P3 incidents properly pass all custom fields
|
|
4
|
-
to Jira when creating tickets via the incident_channel_done signal, including:
|
|
5
|
-
- environments (customfield_11049)
|
|
6
|
-
- platform (customfield_10201)
|
|
7
|
-
- business_impact (customfield_10936)
|
|
8
|
-
- customer/seller specific fields from jira_extra_fields
|
|
9
|
-
"""
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
from unittest.mock import MagicMock, PropertyMock, patch
|
|
13
|
-
|
|
14
|
-
import pytest
|
|
15
|
-
|
|
16
|
-
from firefighter.incidents.forms.unified_incident import UnifiedIncidentForm
|
|
17
|
-
from firefighter.incidents.models.impact import ImpactLevel, ImpactType, LevelChoices
|
|
18
|
-
from firefighter.incidents.signals import create_incident_conversation
|
|
19
|
-
from firefighter.jira_app.client import client
|
|
20
|
-
from firefighter.jira_app.models import JiraUser
|
|
21
|
-
from firefighter.raid.signals.incident_created import create_ticket
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@pytest.mark.django_db
|
|
25
|
-
class TestP1P2P3JiraTicketFields:
|
|
26
|
-
"""Test P1-P3 incident Jira ticket creation includes all custom fields."""
|
|
27
|
-
|
|
28
|
-
def test_p1_with_customer_impact_creates_jira_with_all_fields(
|
|
29
|
-
self,
|
|
30
|
-
priority_factory,
|
|
31
|
-
environment_factory,
|
|
32
|
-
incident_category_factory,
|
|
33
|
-
user_factory,
|
|
34
|
-
):
|
|
35
|
-
"""P1 customer impact should create Jira with zendesk, environments, platform, business_impact."""
|
|
36
|
-
# Setup P1 priority
|
|
37
|
-
p1_priority = priority_factory(value=1, name="P1", default=True)
|
|
38
|
-
env_prd = environment_factory(value="PRD", default=True)
|
|
39
|
-
env_stg = environment_factory(value="STG", default=False)
|
|
40
|
-
category = incident_category_factory()
|
|
41
|
-
user = user_factory()
|
|
42
|
-
|
|
43
|
-
# Get customer impact
|
|
44
|
-
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
45
|
-
customer_impact = ImpactLevel.objects.get(
|
|
46
|
-
impact_type=customers_impact_type,
|
|
47
|
-
value=LevelChoices.HIGHEST.value
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Form data
|
|
51
|
-
form_data = {
|
|
52
|
-
"title": "P1 Critical customer issue",
|
|
53
|
-
"description": "Test P1 critical incident",
|
|
54
|
-
"incident_category": category.id,
|
|
55
|
-
"environment": [env_prd.id, env_stg.id], # Multiple environments!
|
|
56
|
-
"platform": ["platform-FR", "platform-DE"], # Multiple platforms!
|
|
57
|
-
"priority": p1_priority.id,
|
|
58
|
-
"zendesk_ticket_id": "ZD-CRITICAL-123",
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
impacts_data = {
|
|
62
|
-
"customers_impact": customer_impact, # Pass object for get_business_impact()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
form = UnifiedIncidentForm(form_data)
|
|
66
|
-
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
67
|
-
|
|
68
|
-
# Setup mock channel
|
|
69
|
-
mock_channel = MagicMock()
|
|
70
|
-
mock_channel.channel_id = "C123456"
|
|
71
|
-
|
|
72
|
-
# Mock the signal send to intercept kwargs and directly call create_ticket handler
|
|
73
|
-
def mock_signal_send(sender, incident, **kwargs):
|
|
74
|
-
# Skip calling the real signal handler (which would call Slack API)
|
|
75
|
-
# Instead, directly call the JIRA ticket creation handler
|
|
76
|
-
create_ticket(
|
|
77
|
-
sender="test",
|
|
78
|
-
incident=incident,
|
|
79
|
-
channel=mock_channel,
|
|
80
|
-
jira_extra_fields=kwargs.get("jira_extra_fields", {}),
|
|
81
|
-
impacts_data=kwargs.get("impacts_data", {}),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Mock Jira client property at the CLASS level to prevent real connection
|
|
85
|
-
mock_jira_client = MagicMock()
|
|
86
|
-
|
|
87
|
-
with (
|
|
88
|
-
patch.object(create_incident_conversation, "send", side_effect=mock_signal_send),
|
|
89
|
-
patch.object(type(client), "jira", new_callable=PropertyMock, return_value=mock_jira_client),
|
|
90
|
-
patch("firefighter.raid.signals.incident_created.client.create_issue") as mock_jira_create,
|
|
91
|
-
patch("firefighter.raid.signals.incident_created.client.get_jira_user_from_jira_id") as mock_get_default_jira_user,
|
|
92
|
-
patch("firefighter.raid.signals.incident_created.get_jira_user_from_user") as mock_get_jira_user,
|
|
93
|
-
patch("firefighter.raid.forms.get_business_impact") as mock_get_business_impact,
|
|
94
|
-
patch("firefighter.incidents.forms.unified_incident.SelectImpactForm.save"),
|
|
95
|
-
patch("firefighter.raid.signals.incident_created.JiraTicket.objects.create"),
|
|
96
|
-
):
|
|
97
|
-
# Mock Jira ticket creation (format from _jira_object)
|
|
98
|
-
mock_jira_create.return_value = {
|
|
99
|
-
"id": 99999,
|
|
100
|
-
"key": "P1-TEST-123",
|
|
101
|
-
"project_key": "P1",
|
|
102
|
-
"assignee_id": None,
|
|
103
|
-
"reporter_id": "test_account",
|
|
104
|
-
"description": "Test",
|
|
105
|
-
"summary": "Test",
|
|
106
|
-
"issue_type": "Incident",
|
|
107
|
-
"business_impact": "",
|
|
108
|
-
}
|
|
109
|
-
mock_jira_user = JiraUser(id="test_account")
|
|
110
|
-
mock_get_jira_user.return_value = mock_jira_user
|
|
111
|
-
mock_default_jira_user = JiraUser(id="default_account")
|
|
112
|
-
mock_get_default_jira_user.return_value = mock_default_jira_user
|
|
113
|
-
mock_get_business_impact.return_value = "High"
|
|
114
|
-
|
|
115
|
-
# Trigger the P1-P3 workflow
|
|
116
|
-
form.trigger_incident_workflow(
|
|
117
|
-
creator=user,
|
|
118
|
-
impacts_data=impacts_data,
|
|
119
|
-
response_type="critical",
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
# Verify Jira create_issue was called
|
|
123
|
-
assert mock_jira_create.called, "Jira create_issue should have been called"
|
|
124
|
-
|
|
125
|
-
# Get the call arguments
|
|
126
|
-
call_kwargs = mock_jira_create.call_args.kwargs
|
|
127
|
-
|
|
128
|
-
# ✅ CRITICAL ASSERTIONS - These will FAIL initially
|
|
129
|
-
assert "environments" in call_kwargs, "environments should be passed to Jira for P1-P3"
|
|
130
|
-
# P1-P3 use first environment only (from non-deterministic QuerySet order)
|
|
131
|
-
assert len(call_kwargs["environments"]) == 1, "Should pass exactly one environment"
|
|
132
|
-
assert call_kwargs["environments"][0] in {"PRD", "STG"}, "Should pass one of the form environments"
|
|
133
|
-
|
|
134
|
-
assert "platform" in call_kwargs, "platform should be passed to Jira for P1-P3"
|
|
135
|
-
assert call_kwargs["platform"] == "platform-FR", "Should pass first platform value"
|
|
136
|
-
|
|
137
|
-
assert "business_impact" in call_kwargs, "business_impact should be passed to Jira for P1-P3"
|
|
138
|
-
assert call_kwargs["business_impact"] is not None, "Business impact should be computed from customer impact"
|
|
139
|
-
|
|
140
|
-
# Verify zendesk is passed via jira_extra_fields
|
|
141
|
-
assert "zendesk_ticket_id" in call_kwargs
|
|
142
|
-
assert call_kwargs["zendesk_ticket_id"] == "ZD-CRITICAL-123"
|
|
143
|
-
|
|
144
|
-
def test_p2_with_seller_impact_creates_jira_with_all_fields(
|
|
145
|
-
self,
|
|
146
|
-
priority_factory,
|
|
147
|
-
environment_factory,
|
|
148
|
-
incident_category_factory,
|
|
149
|
-
user_factory,
|
|
150
|
-
):
|
|
151
|
-
"""P2 seller impact should create Jira with seller fields + environments."""
|
|
152
|
-
p2_priority = priority_factory(value=2, name="P2", default=False)
|
|
153
|
-
env_prd = environment_factory(value="PRD", default=True)
|
|
154
|
-
category = incident_category_factory()
|
|
155
|
-
user = user_factory()
|
|
156
|
-
|
|
157
|
-
# Get seller impact
|
|
158
|
-
sellers_impact_type = ImpactType.objects.get(value="sellers_impact")
|
|
159
|
-
seller_impact = ImpactLevel.objects.get(
|
|
160
|
-
impact_type=sellers_impact_type,
|
|
161
|
-
value=LevelChoices.HIGH.value
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
form_data = {
|
|
165
|
-
"title": "P2 Critical seller issue",
|
|
166
|
-
"description": "Test P2 seller incident",
|
|
167
|
-
"incident_category": category.id,
|
|
168
|
-
"environment": [env_prd.id],
|
|
169
|
-
"platform": ["platform-FR"],
|
|
170
|
-
"priority": p2_priority.id,
|
|
171
|
-
"seller_contract_id": "SC-CRITICAL-999",
|
|
172
|
-
"zoho_desk_ticket_id": "ZOHO-CRITICAL-888",
|
|
173
|
-
"is_key_account": True,
|
|
174
|
-
"is_seller_in_golden_list": True,
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
impacts_data = {
|
|
178
|
-
"sellers_impact": seller_impact, # Pass object for get_business_impact()
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
form = UnifiedIncidentForm(form_data)
|
|
182
|
-
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
183
|
-
|
|
184
|
-
# Setup mock channel
|
|
185
|
-
mock_channel = MagicMock()
|
|
186
|
-
mock_channel.channel_id = "C789012"
|
|
187
|
-
|
|
188
|
-
# Mock the signal send to intercept kwargs and directly call create_ticket handler
|
|
189
|
-
def mock_signal_send(sender, incident, **kwargs):
|
|
190
|
-
# Skip calling the real signal handler (which would call Slack API)
|
|
191
|
-
# Instead, directly call the JIRA ticket creation handler
|
|
192
|
-
create_ticket(
|
|
193
|
-
sender="test",
|
|
194
|
-
incident=incident,
|
|
195
|
-
channel=mock_channel,
|
|
196
|
-
jira_extra_fields=kwargs.get("jira_extra_fields", {}),
|
|
197
|
-
impacts_data=kwargs.get("impacts_data", {}),
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
# Mock Jira client property at the CLASS level to prevent real connection
|
|
201
|
-
mock_jira_client = MagicMock()
|
|
202
|
-
|
|
203
|
-
with (
|
|
204
|
-
patch.object(create_incident_conversation, "send", side_effect=mock_signal_send),
|
|
205
|
-
patch.object(type(client), "jira", new_callable=PropertyMock, return_value=mock_jira_client),
|
|
206
|
-
patch("firefighter.raid.signals.incident_created.client.create_issue") as mock_jira_create,
|
|
207
|
-
patch("firefighter.raid.signals.incident_created.client.get_jira_user_from_jira_id") as mock_get_default_jira_user,
|
|
208
|
-
patch("firefighter.raid.signals.incident_created.get_jira_user_from_user") as mock_get_jira_user,
|
|
209
|
-
patch("firefighter.raid.forms.get_business_impact") as mock_get_business_impact,
|
|
210
|
-
patch("firefighter.incidents.forms.unified_incident.SelectImpactForm.save"),
|
|
211
|
-
patch("firefighter.raid.signals.incident_created.JiraTicket.objects.create"),
|
|
212
|
-
):
|
|
213
|
-
mock_jira_create.return_value = {
|
|
214
|
-
"id": 88888,
|
|
215
|
-
"key": "P2-SELLER-456",
|
|
216
|
-
"project_key": "P2",
|
|
217
|
-
"assignee_id": None,
|
|
218
|
-
"reporter_id": "test_account",
|
|
219
|
-
"description": "Test",
|
|
220
|
-
"summary": "Test",
|
|
221
|
-
"issue_type": "Incident",
|
|
222
|
-
"business_impact": "",
|
|
223
|
-
}
|
|
224
|
-
mock_jira_user = JiraUser(id="test_account")
|
|
225
|
-
mock_get_jira_user.return_value = mock_jira_user
|
|
226
|
-
mock_default_jira_user = JiraUser(id="default_account")
|
|
227
|
-
mock_get_default_jira_user.return_value = mock_default_jira_user
|
|
228
|
-
mock_get_business_impact.return_value = "High"
|
|
229
|
-
|
|
230
|
-
form.trigger_incident_workflow(
|
|
231
|
-
creator=user,
|
|
232
|
-
impacts_data=impacts_data,
|
|
233
|
-
response_type="critical",
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
call_kwargs = mock_jira_create.call_args.kwargs
|
|
237
|
-
|
|
238
|
-
# ✅ CRITICAL: environments must be passed with exact value
|
|
239
|
-
assert "environments" in call_kwargs, "environments should be passed to Jira for P2"
|
|
240
|
-
assert call_kwargs["environments"] == ["PRD"], "Should pass environment value"
|
|
241
|
-
|
|
242
|
-
# ✅ Verify platform and business_impact with exact values
|
|
243
|
-
assert "platform" in call_kwargs, "platform should be passed to Jira for P2"
|
|
244
|
-
assert call_kwargs["platform"] == "platform-FR", "Should pass platform value"
|
|
245
|
-
assert "business_impact" in call_kwargs, "business_impact should be passed to Jira for P2"
|
|
246
|
-
assert call_kwargs["business_impact"] is not None, "Business impact should be computed"
|
|
247
|
-
|
|
248
|
-
# ✅ Verify seller-specific fields
|
|
249
|
-
assert "seller_contract_id" in call_kwargs
|
|
250
|
-
assert call_kwargs["seller_contract_id"] == "SC-CRITICAL-999"
|
|
251
|
-
assert "zoho_desk_ticket_id" in call_kwargs
|
|
252
|
-
assert call_kwargs["zoho_desk_ticket_id"] == "ZOHO-CRITICAL-888"
|
|
253
|
-
assert "is_key_account" in call_kwargs
|
|
254
|
-
assert call_kwargs["is_key_account"] is True
|
|
255
|
-
assert "is_seller_in_golden_list" in call_kwargs
|
|
256
|
-
assert call_kwargs["is_seller_in_golden_list"] is True
|
|
257
|
-
|
|
258
|
-
def test_p3_with_both_impacts_creates_jira_with_all_fields(
|
|
259
|
-
self,
|
|
260
|
-
priority_factory,
|
|
261
|
-
environment_factory,
|
|
262
|
-
incident_category_factory,
|
|
263
|
-
user_factory,
|
|
264
|
-
):
|
|
265
|
-
"""P3 with both customer and seller impact should pass all fields to Jira."""
|
|
266
|
-
p3_priority = priority_factory(value=3, name="P3", default=False)
|
|
267
|
-
env_prd = environment_factory(value="PRD", default=True)
|
|
268
|
-
env_stg = environment_factory(value="STG", default=False)
|
|
269
|
-
category = incident_category_factory()
|
|
270
|
-
user = user_factory()
|
|
271
|
-
|
|
272
|
-
# Get both impacts
|
|
273
|
-
customers_impact_type = ImpactType.objects.get(value="customers_impact")
|
|
274
|
-
customer_impact = ImpactLevel.objects.get(
|
|
275
|
-
impact_type=customers_impact_type,
|
|
276
|
-
value=LevelChoices.MEDIUM.value
|
|
277
|
-
)
|
|
278
|
-
sellers_impact_type = ImpactType.objects.get(value="sellers_impact")
|
|
279
|
-
seller_impact = ImpactLevel.objects.get(
|
|
280
|
-
impact_type=sellers_impact_type,
|
|
281
|
-
value=LevelChoices.LOW.value
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
form_data = {
|
|
285
|
-
"title": "P3 Combined customer and seller",
|
|
286
|
-
"description": "Test P3 with multiple impacts",
|
|
287
|
-
"incident_category": category.id,
|
|
288
|
-
"environment": [env_prd.id, env_stg.id],
|
|
289
|
-
"platform": ["platform-All"],
|
|
290
|
-
"priority": p3_priority.id,
|
|
291
|
-
"zendesk_ticket_id": "ZD-P3-111",
|
|
292
|
-
"seller_contract_id": "SC-P3-222",
|
|
293
|
-
"zoho_desk_ticket_id": "ZOHO-P3-333",
|
|
294
|
-
"is_key_account": False,
|
|
295
|
-
"is_seller_in_golden_list": False,
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
impacts_data = {
|
|
299
|
-
"customers_impact": customer_impact, # Pass object for get_business_impact()
|
|
300
|
-
"sellers_impact": seller_impact, # Pass object for get_business_impact()
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
form = UnifiedIncidentForm(form_data)
|
|
304
|
-
assert form.is_valid(), f"Form should be valid. Errors: {form.errors}"
|
|
305
|
-
|
|
306
|
-
# Setup mock channel
|
|
307
|
-
mock_channel = MagicMock()
|
|
308
|
-
mock_channel.channel_id = "C111222"
|
|
309
|
-
|
|
310
|
-
# Mock the signal send to intercept kwargs and directly call create_ticket handler
|
|
311
|
-
def mock_signal_send(sender, incident, **kwargs):
|
|
312
|
-
# Skip calling the real signal handler (which would call Slack API)
|
|
313
|
-
# Instead, directly call the JIRA ticket creation handler
|
|
314
|
-
create_ticket(
|
|
315
|
-
sender="test",
|
|
316
|
-
incident=incident,
|
|
317
|
-
channel=mock_channel,
|
|
318
|
-
jira_extra_fields=kwargs.get("jira_extra_fields", {}),
|
|
319
|
-
impacts_data=kwargs.get("impacts_data", {}),
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
# Mock Jira client property at the CLASS level to prevent real connection
|
|
323
|
-
mock_jira_client = MagicMock()
|
|
324
|
-
|
|
325
|
-
with (
|
|
326
|
-
patch.object(create_incident_conversation, "send", side_effect=mock_signal_send),
|
|
327
|
-
patch.object(type(client), "jira", new_callable=PropertyMock, return_value=mock_jira_client),
|
|
328
|
-
patch("firefighter.raid.signals.incident_created.client.create_issue") as mock_jira_create,
|
|
329
|
-
patch("firefighter.raid.signals.incident_created.client.get_jira_user_from_jira_id") as mock_get_default_jira_user,
|
|
330
|
-
patch("firefighter.raid.signals.incident_created.get_jira_user_from_user") as mock_get_jira_user,
|
|
331
|
-
patch("firefighter.raid.forms.get_business_impact") as mock_get_business_impact,
|
|
332
|
-
patch("firefighter.incidents.forms.unified_incident.SelectImpactForm.save"),
|
|
333
|
-
patch("firefighter.raid.signals.incident_created.JiraTicket.objects.create"),
|
|
334
|
-
):
|
|
335
|
-
mock_jira_create.return_value = {
|
|
336
|
-
"id": 77777,
|
|
337
|
-
"key": "P3-COMBO-789",
|
|
338
|
-
"project_key": "P3",
|
|
339
|
-
"assignee_id": None,
|
|
340
|
-
"reporter_id": "test_account",
|
|
341
|
-
"description": "Test",
|
|
342
|
-
"summary": "Test",
|
|
343
|
-
"issue_type": "Incident",
|
|
344
|
-
"business_impact": "",
|
|
345
|
-
}
|
|
346
|
-
mock_jira_user = JiraUser(id="test_account")
|
|
347
|
-
mock_get_jira_user.return_value = mock_jira_user
|
|
348
|
-
mock_default_jira_user = JiraUser(id="default_account")
|
|
349
|
-
mock_get_default_jira_user.return_value = mock_default_jira_user
|
|
350
|
-
mock_get_business_impact.return_value = "High"
|
|
351
|
-
|
|
352
|
-
form.trigger_incident_workflow(
|
|
353
|
-
creator=user,
|
|
354
|
-
impacts_data=impacts_data,
|
|
355
|
-
response_type="critical",
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
call_kwargs = mock_jira_create.call_args.kwargs
|
|
359
|
-
|
|
360
|
-
# ✅ All fields should be present with exact values
|
|
361
|
-
assert "environments" in call_kwargs, "environments should be passed to Jira for P3"
|
|
362
|
-
# P1-P3 use first environment only (from non-deterministic QuerySet order)
|
|
363
|
-
assert len(call_kwargs["environments"]) == 1, "Should pass exactly one environment"
|
|
364
|
-
assert call_kwargs["environments"][0] in {"PRD", "STG"}, "Should pass one of the form environments"
|
|
365
|
-
assert "platform" in call_kwargs, "platform should be passed to Jira for P3"
|
|
366
|
-
assert call_kwargs["platform"] == "platform-All", "Should pass first platform value"
|
|
367
|
-
assert "business_impact" in call_kwargs, "business_impact should be passed to Jira for P3"
|
|
368
|
-
assert call_kwargs["business_impact"] is not None, "Business impact should be computed"
|
|
369
|
-
assert "zendesk_ticket_id" in call_kwargs
|
|
370
|
-
assert call_kwargs["zendesk_ticket_id"] == "ZD-P3-111"
|
|
371
|
-
assert "seller_contract_id" in call_kwargs
|
|
372
|
-
assert call_kwargs["seller_contract_id"] == "SC-P3-222"
|