Skip to content

Commit bbded4e

Browse files
Merge pull request #182 from ManoManoTech/fix/handle-missing-jira-ticket-watchers
fix: handle 404 errors when fetching Jira ticket watchers
2 parents 1aa9a9f + e5cf7fb commit bbded4e

4 files changed

Lines changed: 177 additions & 7 deletions

File tree

src/firefighter/jira_app/client.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,24 @@ def get_watchers_from_jira_ticket(
254254
ValueError: Empty issue id
255255
256256
Returns:
257-
list(JiraAPIUser): List of Jira users object
257+
list(JiraAPIUser): List of Jira users object, or empty list if ticket doesn't exist
258258
"""
259-
watchers = self.jira.watchers(jira_issue_id).raw.get("watchers")
260-
if len(watchers) == 0:
261-
logger.debug(
262-
"No watchers found for jira_issue_id '%s'.", jira_issue_id
263-
)
264-
return watchers
259+
try:
260+
watchers = self.jira.watchers(jira_issue_id).raw.get("watchers")
261+
except exceptions.JIRAError as e:
262+
if e.status_code == 404:
263+
logger.warning(
264+
"Jira ticket %s not found or no permission to access it. Cannot fetch watchers.",
265+
jira_issue_id,
266+
)
267+
return []
268+
raise
269+
else:
270+
if len(watchers) == 0:
271+
logger.debug(
272+
"No watchers found for jira_issue_id '%s'.", jira_issue_id
273+
)
274+
return watchers
265275

266276
@staticmethod
267277
def _create_user_from_jira_info(

tests/test_jira_app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for jira_app module."""

tests/test_jira_app/conftest.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Fixtures for jira_app tests."""
2+
3+
from __future__ import annotations
4+
5+
from unittest.mock import Mock, patch
6+
7+
import pytest
8+
9+
from firefighter.jira_app.client import JiraClient
10+
11+
12+
@pytest.fixture
13+
def mock_jira_api():
14+
"""Create a mock JIRA API object."""
15+
return Mock()
16+
17+
18+
@pytest.fixture
19+
def jira_client(mock_jira_api):
20+
"""Create a JiraClient with mocked JIRA API."""
21+
with patch("firefighter.jira_app.client.JIRA", return_value=mock_jira_api):
22+
client = JiraClient()
23+
client.jira = mock_jira_api
24+
return client
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Tests for JiraClient.get_watchers_from_jira_ticket method."""
2+
3+
from __future__ import annotations
4+
5+
from unittest.mock import Mock
6+
7+
import pytest
8+
from jira.exceptions import JIRAError
9+
10+
11+
@pytest.mark.django_db
12+
class TestGetWatchersFromJiraTicket:
13+
"""Test get_watchers_from_jira_ticket method."""
14+
15+
def test_get_watchers_success_with_watchers(self, jira_client, mock_jira_api):
16+
"""Test successful retrieval of watchers when watchers exist."""
17+
# Given
18+
mock_watchers_response = Mock()
19+
mock_watchers_response.raw = {
20+
"watchers": [
21+
{"accountId": "user1", "displayName": "User One"},
22+
{"accountId": "user2", "displayName": "User Two"},
23+
]
24+
}
25+
mock_jira_api.watchers.return_value = mock_watchers_response
26+
27+
# When
28+
result = jira_client.get_watchers_from_jira_ticket(12345)
29+
30+
# Then
31+
mock_jira_api.watchers.assert_called_once_with(12345)
32+
assert len(result) == 2
33+
assert result[0]["accountId"] == "user1"
34+
assert result[1]["accountId"] == "user2"
35+
36+
def test_get_watchers_success_empty_list(
37+
self, jira_client, mock_jira_api, caplog
38+
):
39+
"""Test successful retrieval when no watchers exist."""
40+
# Given
41+
mock_watchers_response = Mock()
42+
mock_watchers_response.raw = {"watchers": []}
43+
mock_jira_api.watchers.return_value = mock_watchers_response
44+
45+
# When
46+
result = jira_client.get_watchers_from_jira_ticket(12345)
47+
48+
# Then
49+
mock_jira_api.watchers.assert_called_once_with(12345)
50+
assert result == []
51+
# Should log debug message
52+
assert "No watchers found for jira_issue_id '12345'" in caplog.text
53+
54+
def test_get_watchers_404_ticket_not_found(
55+
self, jira_client, mock_jira_api, caplog
56+
):
57+
"""Test handling of 404 error when ticket doesn't exist."""
58+
# Given
59+
jira_error = JIRAError(
60+
status_code=404,
61+
text="Issue does not exist or you do not have permission to see it.",
62+
url="https://jira.example.com/rest/api/2/issue/404295/watchers",
63+
)
64+
mock_jira_api.watchers.side_effect = jira_error
65+
66+
# When
67+
result = jira_client.get_watchers_from_jira_ticket(404295)
68+
69+
# Then
70+
mock_jira_api.watchers.assert_called_once_with(404295)
71+
assert result == []
72+
# Should log warning
73+
assert (
74+
"Jira ticket 404295 not found or no permission to access it"
75+
in caplog.text
76+
)
77+
78+
def test_get_watchers_404_no_permission(self, jira_client, mock_jira_api, caplog):
79+
"""Test handling of 404 error when bot has no permission."""
80+
# Given
81+
jira_error = JIRAError(
82+
status_code=404,
83+
text="Issue does not exist or you do not have permission to see it.",
84+
url="https://jira.example.com/rest/api/2/issue/999999/watchers",
85+
)
86+
mock_jira_api.watchers.side_effect = jira_error
87+
88+
# When
89+
result = jira_client.get_watchers_from_jira_ticket("999999")
90+
91+
# Then
92+
assert result == []
93+
assert "not found or no permission" in caplog.text
94+
95+
def test_get_watchers_other_jira_error_raised(self, jira_client, mock_jira_api):
96+
"""Test that non-404 JIRA errors are re-raised."""
97+
# Given
98+
jira_error = JIRAError(
99+
status_code=500, text="Internal Server Error", url="https://jira.example.com"
100+
)
101+
mock_jira_api.watchers.side_effect = jira_error
102+
103+
# When / Then
104+
with pytest.raises(JIRAError) as exc_info:
105+
jira_client.get_watchers_from_jira_ticket(12345)
106+
107+
assert exc_info.value.status_code == 500
108+
109+
def test_get_watchers_403_error_raised(self, jira_client, mock_jira_api):
110+
"""Test that 403 (Forbidden) errors are re-raised."""
111+
# Given
112+
jira_error = JIRAError(
113+
status_code=403, text="Forbidden", url="https://jira.example.com"
114+
)
115+
mock_jira_api.watchers.side_effect = jira_error
116+
117+
# When / Then
118+
with pytest.raises(JIRAError) as exc_info:
119+
jira_client.get_watchers_from_jira_ticket(12345)
120+
121+
assert exc_info.value.status_code == 403
122+
123+
def test_get_watchers_with_string_id(self, jira_client, mock_jira_api):
124+
"""Test get_watchers with string issue ID."""
125+
# Given
126+
mock_watchers_response = Mock()
127+
mock_watchers_response.raw = {"watchers": [{"accountId": "user1"}]}
128+
mock_jira_api.watchers.return_value = mock_watchers_response
129+
130+
# When
131+
result = jira_client.get_watchers_from_jira_ticket("INCIDENT-123")
132+
133+
# Then
134+
mock_jira_api.watchers.assert_called_once_with("INCIDENT-123")
135+
assert len(result) == 1

0 commit comments

Comments
 (0)