t-bug-catcher 0.6.11__tar.gz → 0.6.13__tar.gz
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.
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/PKG-INFO +1 -1
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/setup.cfg +1 -1
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/setup.py +1 -1
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/__init__.py +1 -1
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/jira.py +27 -13
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/PKG-INFO +1 -1
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/tests/test_t_bug_catcher.py +32 -11
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/MANIFEST.in +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/README.rst +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/pyproject.toml +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/requirements.txt +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/bug_catcher.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/bug_snag.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/config.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/exceptions.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/resources/whispers_config.yml +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/stack_saver.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/utils/__init__.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/utils/common.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/utils/logger.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher/workitems.py +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/SOURCES.txt +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/not-zip-safe +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/requires.txt +0 -0
- {t_bug_catcher-0.6.11 → t_bug_catcher-0.6.13}/t_bug_catcher.egg-info/top_level.txt +0 -0
|
@@ -26,7 +26,7 @@ setup(
|
|
|
26
26
|
packages=find_packages(include=["t_bug_catcher", "t_bug_catcher.*"]),
|
|
27
27
|
test_suite="tests",
|
|
28
28
|
url="https://www.thoughtful.ai/",
|
|
29
|
-
version="0.6.
|
|
29
|
+
version="0.6.13",
|
|
30
30
|
zip_safe=False,
|
|
31
31
|
install_requires=install_requirements,
|
|
32
32
|
include_package_data=True,
|
|
@@ -60,6 +60,14 @@ class Jira:
|
|
|
60
60
|
self._build_info: Optional[dict] = None
|
|
61
61
|
self._status_to_transition = ["to do", "open", "backlog"]
|
|
62
62
|
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _get_package_version() -> str:
|
|
65
|
+
"""Get the package version safely."""
|
|
66
|
+
try:
|
|
67
|
+
return version("t_bug_catcher")
|
|
68
|
+
except Exception:
|
|
69
|
+
return "unknown"
|
|
70
|
+
|
|
63
71
|
@staticmethod
|
|
64
72
|
def _is_json_response(response) -> bool:
|
|
65
73
|
try:
|
|
@@ -161,20 +169,24 @@ class Jira:
|
|
|
161
169
|
"""A function to get the issues using a Jira API.
|
|
162
170
|
|
|
163
171
|
It updates the headers, sets up a JQL query, specifies additional query parameters,
|
|
164
|
-
makes a
|
|
172
|
+
makes a POST request to the Jira API, and returns the JSON response.
|
|
165
173
|
"""
|
|
166
174
|
project_key = project_key or self._project_key
|
|
167
175
|
jql_query = f'project = "{project_key}"'
|
|
168
176
|
|
|
169
|
-
#
|
|
170
|
-
|
|
177
|
+
# Use POST method with JSON body for the /rest/api/3/search/jql endpoint
|
|
178
|
+
request_body = {
|
|
179
|
+
"jql": jql_query,
|
|
180
|
+
"maxResults": 100,
|
|
181
|
+
"fields": ["id", "key", "summary", "description", "status", "assignee", "attachment", "comment"],
|
|
182
|
+
}
|
|
171
183
|
|
|
172
184
|
response = requests.request(
|
|
173
|
-
"
|
|
174
|
-
self._base_url + "/rest/api/
|
|
185
|
+
"POST",
|
|
186
|
+
self._base_url + "/rest/api/3/search/jql",
|
|
175
187
|
headers=self.__get_headers(),
|
|
176
188
|
auth=self._auth,
|
|
177
|
-
|
|
189
|
+
json=request_body,
|
|
178
190
|
)
|
|
179
191
|
self.check_response(response)
|
|
180
192
|
return response.json()
|
|
@@ -762,7 +774,7 @@ class Jira:
|
|
|
762
774
|
},
|
|
763
775
|
{
|
|
764
776
|
"type": "text",
|
|
765
|
-
"text": f" (v{
|
|
777
|
+
"text": f" (v{Jira._get_package_version()})",
|
|
766
778
|
"marks": [
|
|
767
779
|
{"type": "em"},
|
|
768
780
|
{"type": "subsup", "attrs": {"type": "sub"}},
|
|
@@ -1121,7 +1133,8 @@ class Jira:
|
|
|
1121
1133
|
auth=self._auth,
|
|
1122
1134
|
)
|
|
1123
1135
|
self.check_response(response)
|
|
1124
|
-
|
|
1136
|
+
response_data = response.json()
|
|
1137
|
+
return response_data.get("fields", {}).get("status", {}).get("name", "Unknown")
|
|
1125
1138
|
|
|
1126
1139
|
def __update_existing_ticket(
|
|
1127
1140
|
self,
|
|
@@ -1173,7 +1186,7 @@ class Jira:
|
|
|
1173
1186
|
)
|
|
1174
1187
|
issue = self.get_issue(existing_ticket["id"])
|
|
1175
1188
|
|
|
1176
|
-
if len(issue
|
|
1189
|
+
if len(issue.get("fields", {}).get("attachment", [])) >= CONFIG.LIMITS.MAX_ISSUE_ATTACHMENTS:
|
|
1177
1190
|
logger.warning(
|
|
1178
1191
|
f"Attachments were not uploaded due to exceeding "
|
|
1179
1192
|
f"{CONFIG.LIMITS.MAX_ISSUE_ATTACHMENTS} attachments limit."
|
|
@@ -1712,9 +1725,10 @@ class Jira:
|
|
|
1712
1725
|
dict or None: The matching ticket if found, otherwise None.
|
|
1713
1726
|
"""
|
|
1714
1727
|
for ticket in all_tickets:
|
|
1715
|
-
|
|
1728
|
+
description = ticket.get("fields", {}).get("description", "")
|
|
1729
|
+
if not description:
|
|
1716
1730
|
continue
|
|
1717
|
-
if f"{error_id}_~" not in
|
|
1731
|
+
if f"{error_id}_~" not in description:
|
|
1718
1732
|
continue
|
|
1719
1733
|
return ticket
|
|
1720
1734
|
|
|
@@ -1812,8 +1826,8 @@ class Jira:
|
|
|
1812
1826
|
logger.warning(f"Failed to convert exception to string due to: {e}")
|
|
1813
1827
|
return exception.__class__.__name__
|
|
1814
1828
|
|
|
1815
|
-
message = re.sub("<([a-z]+)(?![^>]*\/>)[^>]*>", r"<\1>", exception_str)
|
|
1816
|
-
message = re.sub(">([^<]+)
|
|
1829
|
+
message = re.sub(r"<([a-z]+)(?![^>]*\/>)[^>]*>", r"<\1>", exception_str)
|
|
1830
|
+
message = re.sub(r">([^<]+)</", ">...</", message)
|
|
1817
1831
|
|
|
1818
1832
|
if "selenium" not in exception.__class__.__name__.lower() and not isinstance(exception, AssertionError):
|
|
1819
1833
|
return message
|
|
@@ -50,22 +50,43 @@ class TestBugCatcher(unittest.TestCase):
|
|
|
50
50
|
assert hasattr(ex, "custom_attachments")
|
|
51
51
|
|
|
52
52
|
@patch.object(CONFIG, "ENVIRONMENT", "robocloud")
|
|
53
|
-
def
|
|
54
|
-
"""Test report_error function."""
|
|
53
|
+
def test_report_error_success(self):
|
|
54
|
+
"""Test report_error function when everything works correctly."""
|
|
55
55
|
with patch.object(
|
|
56
56
|
self.bug_catcher, "_BugCatcher__configurator", new_callable=PropertyMock
|
|
57
57
|
) as mock_configurator:
|
|
58
58
|
patch.object(mock_configurator, "is_jira_configured", return_value=True)
|
|
59
59
|
patch.object(mock_configurator, "is_bugsnag_configured", return_value=True)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
with patch.object(self.bug_catcher._BugCatcher__jira, "report_error") as mock_jira_report:
|
|
61
|
+
mock_jira_report.return_value = {"key": "TEST-123", "id": "10001"}
|
|
62
|
+
try:
|
|
63
|
+
raise Exception("Test exception")
|
|
64
|
+
except Exception as ex:
|
|
65
|
+
with patch.object(logger, "info") as mock_info:
|
|
66
|
+
self.bug_catcher.report_error(exception=ex)
|
|
67
|
+
mock_jira_report.assert_called_once()
|
|
68
|
+
mock_info.assert_called()
|
|
69
|
+
if mock_info.call_args:
|
|
70
|
+
info_message = mock_info.call_args[0][0]
|
|
71
|
+
self.assertTrue("reported" in info_message.lower())
|
|
72
|
+
|
|
73
|
+
@patch.object(CONFIG, "ENVIRONMENT", "robocloud")
|
|
74
|
+
def test_report_error_failure(self):
|
|
75
|
+
"""Test report_error function when Jira API fails (original test intention)."""
|
|
76
|
+
with patch.object(
|
|
77
|
+
self.bug_catcher, "_BugCatcher__configurator", new_callable=PropertyMock
|
|
78
|
+
) as mock_configurator:
|
|
79
|
+
patch.object(mock_configurator, "is_jira_configured", return_value=True)
|
|
80
|
+
patch.object(mock_configurator, "is_bugsnag_configured", return_value=True)
|
|
81
|
+
with patch.object(self.bug_catcher._BugCatcher__jira, "report_error") as mock_jira_report:
|
|
82
|
+
mock_jira_report.return_value = False # Simulate API failure
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
raise Exception("Test exception")
|
|
86
|
+
except Exception as ex:
|
|
87
|
+
with patch.object(logger, "warning"):
|
|
88
|
+
self.bug_catcher.report_error(exception=ex)
|
|
89
|
+
mock_jira_report.assert_called_once()
|
|
69
90
|
|
|
70
91
|
|
|
71
92
|
if __name__ == "__main__":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|