Skip to content

Commit 96cc0a1

Browse files
rlundeen2Copilot
andcommitted
Move exhaustive dataset tests to e2e, keep smoke tests in integration
Replace TestSeedDatasetProviderIntegration (parameterized over all 58 providers) with TestSeedDatasetSmoke testing 3 representative providers: - 1 local YAML provider (no network) - 1 remote URL-based provider (_XSTestDataset) - 1 remote HuggingFace provider (_SimpleSafetyTestsDataset) Move the full parametric test to tests/end_to_end/test_all_datasets.py for daily CI runs, avoiding flaky HF rate-limiting failures on every PR. Add tests/integration/datasets/test_load_default_datasets_integration.py to verify the LoadDefaultDatasets initializer pipeline with real data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 599ab00 commit 96cc0a1

3 files changed

Lines changed: 171 additions & 33 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT license.
3+
4+
"""
5+
End-to-end tests that verify every registered dataset provider can be fetched.
6+
7+
These tests download real data from HuggingFace and GitHub, are slow, and are
8+
subject to transient network failures. They are intended to run daily in e2e CI,
9+
not on every PR.
10+
"""
11+
12+
import logging
13+
14+
import pytest
15+
16+
from pyrit.datasets import SeedDatasetProvider
17+
from pyrit.datasets.seed_datasets.remote import _VLSUMultimodalDataset
18+
from pyrit.models import SeedDataset
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
def get_dataset_providers():
24+
"""Helper to get all registered providers for parameterization."""
25+
providers = SeedDatasetProvider.get_all_providers()
26+
return [(name, cls) for name, cls in providers.items()]
27+
28+
29+
class TestAllDatasets:
30+
"""Exhaustive test that every registered dataset provider can be fetched."""
31+
32+
@pytest.mark.asyncio
33+
@pytest.mark.parametrize("name,provider_cls", get_dataset_providers())
34+
async def test_fetch_dataset(self, name, provider_cls):
35+
"""
36+
Verify that a specific registered dataset can be fetched.
37+
38+
This test is parameterized to run for each registered provider.
39+
It verifies that:
40+
1. The dataset can be downloaded/loaded without error
41+
2. The result is a SeedDataset
42+
3. The dataset is not empty (has seeds)
43+
"""
44+
logger.info(f"Testing provider: {name}")
45+
46+
try:
47+
# Use max_examples for slow providers that fetch many remote images
48+
provider = provider_cls(max_examples=6) if provider_cls == _VLSUMultimodalDataset else provider_cls()
49+
dataset = await provider.fetch_dataset(cache=False)
50+
51+
assert isinstance(dataset, SeedDataset), f"{name} did not return a SeedDataset"
52+
assert len(dataset.seeds) > 0, f"{name} returned an empty dataset"
53+
assert dataset.dataset_name, f"{name} has no dataset_name"
54+
55+
# Verify seeds have required fields
56+
for seed in dataset.seeds:
57+
assert seed.value, f"Seed in {name} has no value"
58+
assert seed.dataset_name == dataset.dataset_name, (
59+
f"Seed dataset_name mismatch in {name}: {seed.dataset_name} != {dataset.dataset_name}"
60+
)
61+
62+
logger.info(f"Successfully verified {name} with {len(dataset.seeds)} seeds")
63+
64+
except Exception as e:
65+
pytest.fail(f"Failed to fetch dataset from {name}: {str(e)}")
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT license.
3+
4+
"""
5+
Integration test for the LoadDefaultDatasets initializer.
6+
7+
Runs the full pipeline: discovers scenario default datasets, fetches them
8+
from real remote sources, and stores them in in-memory CentralMemory.
9+
"""
10+
11+
import logging
12+
13+
import pytest
14+
15+
from pyrit.datasets import SeedDatasetProvider
16+
from pyrit.memory import CentralMemory
17+
from pyrit.setup.initializers.scenarios.load_default_datasets import LoadDefaultDatasets
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
class TestLoadDefaultDatasetsIntegration:
23+
"""Integration test that LoadDefaultDatasets loads real datasets into memory."""
24+
25+
@pytest.mark.asyncio
26+
async def test_initialize_loads_datasets_into_memory(self):
27+
"""
28+
Verify that LoadDefaultDatasets.initialize_async() successfully fetches
29+
real datasets and stores them in CentralMemory.
30+
"""
31+
initializer = LoadDefaultDatasets()
32+
await initializer.initialize_async()
33+
34+
memory = CentralMemory.get_memory_instance()
35+
seed_datasets = await memory.get_seed_datasets_async()
36+
37+
assert len(seed_datasets) > 0, "No datasets were loaded into memory"
38+
logger.info(f"LoadDefaultDatasets loaded {len(seed_datasets)} datasets into memory")
39+
40+
# Verify basic structure of loaded datasets
41+
for dataset in seed_datasets:
42+
assert dataset.dataset_name, "Loaded dataset has no name"
43+
assert len(dataset.seeds) > 0, f"Dataset '{dataset.dataset_name}' has no seeds"
44+
45+
@pytest.mark.asyncio
46+
async def test_all_scenario_datasets_are_fetchable(self):
47+
"""
48+
Verify that every dataset name referenced by registered scenarios
49+
can actually be fetched from SeedDatasetProvider.
50+
"""
51+
from pyrit.registry import ScenarioRegistry
52+
53+
registry = ScenarioRegistry.get_registry_singleton()
54+
scenario_names = registry.get_names()
55+
56+
all_dataset_names: list[str] = []
57+
for scenario_name in scenario_names:
58+
scenario_class = registry.get_class(scenario_name)
59+
if scenario_class:
60+
try:
61+
datasets = scenario_class.default_dataset_config().get_default_dataset_names()
62+
all_dataset_names.extend(datasets)
63+
except Exception as e:
64+
logger.warning(f"Could not get default datasets from scenario '{scenario_name}': {e}")
65+
66+
unique_names = list(dict.fromkeys(all_dataset_names))
67+
assert len(unique_names) > 0, "No scenarios registered any default datasets"
68+
69+
fetched = await SeedDatasetProvider.fetch_datasets_async(dataset_names=unique_names)
70+
assert len(fetched) == len(unique_names), (
71+
f"Expected {len(unique_names)} datasets but fetched {len(fetched)}. "
72+
f"Missing: {set(unique_names) - {d.dataset_name for d in fetched}}"
73+
)

tests/integration/datasets/test_seed_dataset_provider_integration.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from pyrit.datasets import SeedDatasetProvider
1212
from pyrit.datasets.seed_datasets.local.local_dataset_loader import _LocalDatasetLoader
13-
from pyrit.datasets.seed_datasets.remote import _VLSUMultimodalDataset
13+
from pyrit.datasets.seed_datasets.remote import _SimpleSafetyTestsDataset, _XSTestDataset
1414
from pyrit.datasets.seed_datasets.seed_metadata import (
1515
SeedDatasetFilter,
1616
)
@@ -19,49 +19,49 @@
1919
logger = logging.getLogger(__name__)
2020

2121

22-
def get_dataset_providers():
23-
"""Helper to get all registered providers for parameterization."""
24-
providers = SeedDatasetProvider.get_all_providers()
25-
return [(name, cls) for name, cls in providers.items()]
22+
# Smoke-test providers covering the three distinct fetch paths:
23+
# - local YAML (no network)
24+
# - remote URL-based (_fetch_from_url via GitHub)
25+
# - remote HuggingFace (_fetch_from_huggingface)
26+
_all_providers = SeedDatasetProvider.get_all_providers()
27+
_SMOKE_PROVIDERS: list[tuple[str, type]] = [
28+
("LocalDataset_access_shell_commands", _all_providers["LocalDataset_access_shell_commands"]),
29+
("_XSTestDataset", _XSTestDataset),
30+
("_SimpleSafetyTestsDataset", _SimpleSafetyTestsDataset),
31+
]
2632

2733

28-
class TestSeedDatasetProviderIntegration:
29-
"""Integration tests for SeedDatasetProvider."""
34+
class TestSeedDatasetSmoke:
35+
"""Smoke tests for a small representative set of dataset providers.
36+
37+
The exhaustive test over all providers lives in tests/end_to_end/test_all_datasets.py.
38+
"""
3039

3140
@pytest.mark.asyncio
32-
@pytest.mark.parametrize("name,provider_cls", get_dataset_providers())
33-
async def test_fetch_dataset_integration(self, name, provider_cls):
41+
@pytest.mark.parametrize("name,provider_cls", _SMOKE_PROVIDERS, ids=[p[0] for p in _SMOKE_PROVIDERS])
42+
async def test_fetch_dataset_smoke(self, name, provider_cls):
3443
"""
35-
Integration test to verify that a specific registered dataset can be fetched.
44+
Verify that a representative provider can be fetched successfully.
3645
37-
This test is parameterized to run for each registered provider.
38-
It verifies that:
39-
1. The dataset can be downloaded/loaded without error
40-
2. The result is a SeedDataset
41-
3. The dataset is not empty (has seeds)
46+
Covers one local, one URL-remote, and one HuggingFace-remote provider
47+
to catch regressions in each fetch path without downloading all 58 datasets.
4248
"""
43-
logger.info(f"Testing provider: {name}")
49+
logger.info(f"Smoke testing provider: {name}")
4450

45-
try:
46-
# Use max_examples for slow providers that fetch many remote images
47-
provider = provider_cls(max_examples=6) if provider_cls == _VLSUMultimodalDataset else provider_cls()
48-
dataset = await provider.fetch_dataset(cache=False)
51+
provider = provider_cls()
52+
dataset = await provider.fetch_dataset(cache=False)
4953

50-
assert isinstance(dataset, SeedDataset), f"{name} did not return a SeedDataset"
51-
assert len(dataset.seeds) > 0, f"{name} returned an empty dataset"
52-
assert dataset.dataset_name, f"{name} has no dataset_name"
54+
assert isinstance(dataset, SeedDataset), f"{name} did not return a SeedDataset"
55+
assert len(dataset.seeds) > 0, f"{name} returned an empty dataset"
56+
assert dataset.dataset_name, f"{name} has no dataset_name"
5357

54-
# Verify seeds have required fields
55-
for seed in dataset.seeds:
56-
assert seed.value, f"Seed in {name} has no value"
57-
assert seed.dataset_name == dataset.dataset_name, (
58-
f"Seed dataset_name mismatch in {name}: {seed.dataset_name} != {dataset.dataset_name}"
59-
)
60-
61-
logger.info(f"Successfully verified {name} with {len(dataset.seeds)} seeds")
58+
for seed in dataset.seeds:
59+
assert seed.value, f"Seed in {name} has no value"
60+
assert seed.dataset_name == dataset.dataset_name, (
61+
f"Seed dataset_name mismatch in {name}: {seed.dataset_name} != {dataset.dataset_name}"
62+
)
6263

63-
except Exception as e:
64-
pytest.fail(f"Failed to fetch dataset from {name}: {str(e)}")
64+
logger.info(f"Smoke test passed for {name} with {len(dataset.seeds)} seeds")
6565

6666

6767
class TestRemoteFilteringIntegration:

0 commit comments

Comments
 (0)