From a64da507aee25b7953d4c4010744089b5fe90c18 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Fri, 31 Oct 2025 18:17:33 -0400 Subject: [PATCH] Richer tracking of transient failures. - Update error message to reflect the error is likely a known transient failure. - Track explicit issue numbers with errors so we can more easily track information about the error over time. --- lib/galaxy/util/unittest_utils/__init__.py | 36 +++++++++++++++++++ lib/galaxy_test/api/test_jobs.py | 2 ++ .../selenium/test_history_sharing.py | 2 ++ .../selenium/test_tool_discovery_view.py | 2 ++ .../objectstore/test_swift_objectstore.py | 2 ++ 5 files changed, 44 insertions(+) diff --git a/lib/galaxy/util/unittest_utils/__init__.py b/lib/galaxy/util/unittest_utils/__init__.py index 3618551cbc6..e3ccc8e46d8 100644 --- a/lib/galaxy/util/unittest_utils/__init__.py +++ b/lib/galaxy/util/unittest_utils/__init__.py @@ -57,3 +57,39 @@ def skip_unless_environ(env_var: str) -> Union[Callable[[Callable[P, T]], Callab return _identity return pytest.mark.skip(f"{env_var} must be set for this test") + + +def transient_failure(issue: int) -> Callable[[Callable[P, T]], Callable[P, T]]: + """Mark test as known transient failure with GitHub issue tracking. + + This decorator catches exceptions from tests and rewraps them with a marker + indicating this is a known transient failure. This allows automated tooling + to categorize failures and helps reviewers quickly identify flaky tests. + + Args: + issue: GitHub issue number tracking this transient failure + + Example: + @transient_failure(issue=12345) + def test_flaky_selenium(self): + # Test that sometimes fails due to race condition + ... + """ + + def decorator(func: Callable[P, T]) -> Callable[P, T]: + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + try: + return func(*args, **kwargs) + except Exception as e: + msg = f"TRANSIENT FAILURE [Issue #{issue}]: {str(e)}" + # Try to preserve exception type, fallback to plain Exception + try: + raise type(e)(msg) from e + except (TypeError, AttributeError): + # type(e) doesn't accept single string arg + raise Exception(msg) from e + + return wrapper + + return decorator diff --git a/lib/galaxy_test/api/test_jobs.py b/lib/galaxy_test/api/test_jobs.py index 52fb8a42f71..5639449b106 100644 --- a/lib/galaxy_test/api/test_jobs.py +++ b/lib/galaxy_test/api/test_jobs.py @@ -10,6 +10,7 @@ import pytest import requests from dateutil.parser import isoparse +from galaxy.util.unittest_utils import transient_failure from galaxy_test.api.test_tools import TestsTools from galaxy_test.base.api_asserts import assert_status_code_is_ok from galaxy_test.base.populators import ( @@ -851,6 +852,7 @@ steps: search_payload = self._search_payload(history_id=history_id, tool_id="multi_data_param", inputs=inputs) self._search(search_payload, expected_search_count=0) + @transient_failure(issue=21230) @pytest.mark.require_new_history def test_search_delete_hdca_output(self, history_id): list_id_a = self.__history_with_ok_collection(collection_type="list", history_id=history_id) diff --git a/lib/galaxy_test/selenium/test_history_sharing.py b/lib/galaxy_test/selenium/test_history_sharing.py index 16371bcd68f..7c2673a38e3 100644 --- a/lib/galaxy_test/selenium/test_history_sharing.py +++ b/lib/galaxy_test/selenium/test_history_sharing.py @@ -1,3 +1,4 @@ +from galaxy.util.unittest_utils import transient_failure from .framework import ( selenium_only, selenium_test, @@ -157,6 +158,7 @@ class TestPrivateHistorySharingRequiresPermissionChanges(SeleniumTestCase): me and users this history is shared with" rather than "Make datasets public". """ + @transient_failure(issue=21224) @selenium_test def test_sharing_private_history_default_permission(self): # Create two test users - one to own the history, one to share with diff --git a/lib/galaxy_test/selenium/test_tool_discovery_view.py b/lib/galaxy_test/selenium/test_tool_discovery_view.py index 43cecfd4d31..612eb91e973 100644 --- a/lib/galaxy_test/selenium/test_tool_discovery_view.py +++ b/lib/galaxy_test/selenium/test_tool_discovery_view.py @@ -1,3 +1,4 @@ +from galaxy.util.unittest_utils import transient_failure from .framework import ( selenium_test, SeleniumTestCase, @@ -15,6 +16,7 @@ class TestToolDiscoveryViewAnonymous(SeleniumTestCase): advanced search, and list vs grid view toggling. """ + @transient_failure(issue=21225) @selenium_test def test_tool_discovery_landing(self): """Test navigation to the tool discovery view.""" diff --git a/test/integration/objectstore/test_swift_objectstore.py b/test/integration/objectstore/test_swift_objectstore.py index 9802831f0a7..cf64b426f3c 100644 --- a/test/integration/objectstore/test_swift_objectstore.py +++ b/test/integration/objectstore/test_swift_objectstore.py @@ -18,6 +18,8 @@ TEST_TOOL_IDS = [ "tool_provided_metadata_10", "tool_provided_metadata_11", "tool_provided_metadata_12", + # Transiently fails - see issue #21226 - I'd love to mark this with the decorator but + # the test framework here doesn't support it yet. "composite_output", "composite_output_tests", "metadata",