Skip to content

Commit a0202a9

Browse files
rlundeen2Copilot
andcommitted
Add retry and timeout resiliency to e2e dataset tests
- Retry up to 3 times with exponential backoff (5s, 10s, 60s max) on transient network errors (OSError, ConnectionError, TimeoutError) - 5-minute per-test timeout to prevent hung downloads from blocking CI - Uses tenacity (already a project dependency) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 96cc0a1 commit a0202a9

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

tests/end_to_end/test_all_datasets.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,51 @@
77
These tests download real data from HuggingFace and GitHub, are slow, and are
88
subject to transient network failures. They are intended to run daily in e2e CI,
99
not on every PR.
10+
11+
Resiliency: each fetch is retried up to 3 times with exponential backoff to
12+
handle transient HuggingFace / GitHub rate-limiting and network errors.
1013
"""
1114

1215
import logging
1316

1417
import pytest
18+
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
1519

1620
from pyrit.datasets import SeedDatasetProvider
1721
from pyrit.datasets.seed_datasets.remote import _VLSUMultimodalDataset
1822
from pyrit.models import SeedDataset
1923

2024
logger = logging.getLogger(__name__)
2125

26+
# Per-test timeout in seconds (5 minutes per dataset)
27+
_TEST_TIMEOUT = 300
28+
29+
# Transient error types that warrant a retry
30+
_RETRYABLE_ERRORS = (OSError, ConnectionError, TimeoutError)
31+
2232

2333
def get_dataset_providers():
2434
"""Helper to get all registered providers for parameterization."""
2535
providers = SeedDatasetProvider.get_all_providers()
2636
return [(name, cls) for name, cls in providers.items()]
2737

2838

39+
@retry(
40+
stop=stop_after_attempt(3),
41+
wait=wait_exponential(multiplier=5, min=5, max=60),
42+
retry=retry_if_exception_type(_RETRYABLE_ERRORS),
43+
reraise=True,
44+
)
45+
async def _fetch_with_retry(provider) -> SeedDataset:
46+
"""Fetch a dataset with retry on transient network errors."""
47+
return await provider.fetch_dataset(cache=False)
48+
49+
2950
class TestAllDatasets:
3051
"""Exhaustive test that every registered dataset provider can be fetched."""
3152

3253
@pytest.mark.asyncio
54+
@pytest.mark.timeout(_TEST_TIMEOUT)
3355
@pytest.mark.parametrize("name,provider_cls", get_dataset_providers())
3456
async def test_fetch_dataset(self, name, provider_cls):
3557
"""
@@ -40,13 +62,15 @@ async def test_fetch_dataset(self, name, provider_cls):
4062
1. The dataset can be downloaded/loaded without error
4163
2. The result is a SeedDataset
4264
3. The dataset is not empty (has seeds)
65+
66+
Retries up to 3 times on transient network errors.
4367
"""
4468
logger.info(f"Testing provider: {name}")
4569

4670
try:
4771
# Use max_examples for slow providers that fetch many remote images
4872
provider = provider_cls(max_examples=6) if provider_cls == _VLSUMultimodalDataset else provider_cls()
49-
dataset = await provider.fetch_dataset(cache=False)
73+
dataset = await _fetch_with_retry(provider)
5074

5175
assert isinstance(dataset, SeedDataset), f"{name} did not return a SeedDataset"
5276
assert len(dataset.seeds) > 0, f"{name} returned an empty dataset"

0 commit comments

Comments
 (0)