Merge pull request #21227 from jmchilton/transient_error_decorator

Richer tracking of transient failures.
This commit is contained in:
John Chilton
2025-11-04 20:34:11 +01:00
committed by GitHub
5 changed files with 44 additions and 0 deletions
@@ -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
+2
View File
@@ -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)
@@ -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
@@ -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."""
@@ -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",