""" Unit tests for agentgraph/testing/config.py Tests configuration models and preset configurations. """ import pytest from pydantic import ValidationError from agentgraph.testing.config import ( ExecutionConfig, JailbreakTestConfig, DemographicConfig, CounterfactualBiasTestConfig, PerturbationTestConfig, PRESET_CONFIGS, EXTENDED_DEMOGRAPHICS, get_preset_config, create_config_from_dict, ) class TestExecutionConfig: """Tests for ExecutionConfig model.""" def test_default_values(self): """Test default configuration values.""" config = ExecutionConfig() assert config.max_workers == 5 assert config.max_retries == 3 assert config.base_delay == 1.0 assert config.max_delay == 60.0 assert config.rate_limit_per_minute == 60 def test_custom_values(self): """Test custom configuration values.""" config = ExecutionConfig( max_workers=10, max_retries=5, base_delay=2.0, max_delay=120.0, rate_limit_per_minute=100 ) assert config.max_workers == 10 assert config.max_retries == 5 assert config.base_delay == 2.0 assert config.max_delay == 120.0 assert config.rate_limit_per_minute == 100 def test_max_workers_validation(self): """Test max_workers validation bounds.""" # Valid bounds assert ExecutionConfig(max_workers=1).max_workers == 1 assert ExecutionConfig(max_workers=20).max_workers == 20 # Invalid bounds with pytest.raises(ValidationError): ExecutionConfig(max_workers=0) with pytest.raises(ValidationError): ExecutionConfig(max_workers=21) def test_max_retries_validation(self): """Test max_retries validation bounds.""" assert ExecutionConfig(max_retries=1).max_retries == 1 assert ExecutionConfig(max_retries=10).max_retries == 10 with pytest.raises(ValidationError): ExecutionConfig(max_retries=0) with pytest.raises(ValidationError): ExecutionConfig(max_retries=11) def test_base_delay_validation(self): """Test base_delay validation bounds.""" assert ExecutionConfig(base_delay=0.1).base_delay == 0.1 assert ExecutionConfig(base_delay=10.0).base_delay == 10.0 with pytest.raises(ValidationError): ExecutionConfig(base_delay=0.0) with pytest.raises(ValidationError): ExecutionConfig(base_delay=11.0) class TestJailbreakTestConfig: """Tests for JailbreakTestConfig model.""" def test_default_values(self): """Test default configuration values.""" config = JailbreakTestConfig() assert config.enabled is True assert config.num_techniques == 10 assert config.technique_categories is None assert config.random_seed is None assert config.prompt_source == "standard" assert config.custom_prompts is None def test_num_techniques_validation(self): """Test num_techniques validation bounds.""" assert JailbreakTestConfig(num_techniques=1).num_techniques == 1 assert JailbreakTestConfig(num_techniques=50).num_techniques == 50 with pytest.raises(ValidationError): JailbreakTestConfig(num_techniques=0) with pytest.raises(ValidationError): JailbreakTestConfig(num_techniques=51) def test_technique_categories(self): """Test technique categories filtering.""" config = JailbreakTestConfig( technique_categories=["DAN", "Omega"] ) assert config.technique_categories == ["DAN", "Omega"] def test_custom_prompts(self): """Test custom prompts configuration.""" custom = [ {"name": "test", "prompt": "Test prompt", "description": "Test"} ] config = JailbreakTestConfig(custom_prompts=custom) assert config.custom_prompts == custom def test_disabled_config(self): """Test disabled jailbreak testing.""" config = JailbreakTestConfig(enabled=False) assert config.enabled is False class TestDemographicConfig: """Tests for DemographicConfig model.""" def test_basic_demographic(self): """Test basic demographic configuration.""" demo = DemographicConfig(gender="male", race="White") assert demo.gender == "male" assert demo.race == "White" def test_str_representation(self): """Test string representation.""" demo = DemographicConfig(gender="female", race="Black") assert str(demo) == "female Black" def test_various_demographics(self): """Test various demographic combinations.""" demos = [ ("male", "White"), ("female", "Black"), ("non-binary", "Asian"), ("male", "Hispanic"), ] for gender, race in demos: demo = DemographicConfig(gender=gender, race=race) assert demo.gender == gender assert demo.race == race class TestCounterfactualBiasTestConfig: """Tests for CounterfactualBiasTestConfig model.""" def test_default_values(self): """Test default configuration values.""" config = CounterfactualBiasTestConfig() assert config.enabled is True assert len(config.demographics) == 4 assert config.include_baseline is True assert config.comparison_mode == "both" assert config.extended_dimensions is None def test_comparison_mode_enum(self): """Test comparison mode enumeration.""" for mode in ["all_pairs", "vs_baseline", "both"]: config = CounterfactualBiasTestConfig(comparison_mode=mode) assert config.comparison_mode == mode with pytest.raises(ValidationError): CounterfactualBiasTestConfig(comparison_mode="invalid") def test_custom_demographics(self): """Test custom demographics configuration.""" demos = [ DemographicConfig(gender="male", race="Asian"), DemographicConfig(gender="female", race="Hispanic"), ] config = CounterfactualBiasTestConfig(demographics=demos) assert len(config.demographics) == 2 assert config.demographics[0].race == "Asian" def test_extended_dimensions(self): """Test extended dimensions configuration.""" config = CounterfactualBiasTestConfig( extended_dimensions=["age", "disability"] ) assert config.extended_dimensions == ["age", "disability"] def test_disabled_config(self): """Test disabled bias testing.""" config = CounterfactualBiasTestConfig(enabled=False) assert config.enabled is False class TestPerturbationTestConfig: """Tests for PerturbationTestConfig model.""" def test_default_values(self): """Test default configuration values.""" config = PerturbationTestConfig() assert config.model == "gpt-4o-mini" assert config.judge_model == "gpt-4o-mini" assert config.max_relations is None assert isinstance(config.execution, ExecutionConfig) assert isinstance(config.jailbreak, JailbreakTestConfig) assert isinstance(config.counterfactual_bias, CounterfactualBiasTestConfig) def test_custom_models(self): """Test custom model configuration.""" config = PerturbationTestConfig( model="gpt-4o", judge_model="gpt-4" ) assert config.model == "gpt-4o" assert config.judge_model == "gpt-4" def test_max_relations(self): """Test max_relations configuration.""" config = PerturbationTestConfig(max_relations=5) assert config.max_relations == 5 config_all = PerturbationTestConfig(max_relations=None) assert config_all.max_relations is None def test_nested_config(self): """Test nested configuration objects.""" config = PerturbationTestConfig( execution=ExecutionConfig(max_workers=10), jailbreak=JailbreakTestConfig(num_techniques=15), counterfactual_bias=CounterfactualBiasTestConfig(comparison_mode="all_pairs") ) assert config.execution.max_workers == 10 assert config.jailbreak.num_techniques == 15 assert config.counterfactual_bias.comparison_mode == "all_pairs" def test_model_dump(self): """Test model serialization.""" config = PerturbationTestConfig() data = config.model_dump() assert "model" in data assert "judge_model" in data assert "execution" in data assert "jailbreak" in data assert "counterfactual_bias" in data class TestPresetConfigs: """Tests for preset configurations.""" def test_preset_keys(self): """Test preset configuration keys exist.""" assert "quick" in PRESET_CONFIGS assert "standard" in PRESET_CONFIGS assert "comprehensive" in PRESET_CONFIGS def test_quick_preset(self): """Test quick preset configuration.""" config = PRESET_CONFIGS["quick"] assert config.max_relations == 3 assert config.execution.max_workers == 3 assert config.jailbreak.num_techniques == 3 assert len(config.counterfactual_bias.demographics) == 2 assert config.counterfactual_bias.comparison_mode == "vs_baseline" def test_standard_preset(self): """Test standard preset configuration.""" config = PRESET_CONFIGS["standard"] assert config.max_relations == 10 assert config.execution.max_workers == 5 assert config.jailbreak.num_techniques == 10 assert config.counterfactual_bias.comparison_mode == "both" def test_comprehensive_preset(self): """Test comprehensive preset configuration.""" config = PRESET_CONFIGS["comprehensive"] assert config.max_relations is None assert config.execution.max_workers == 10 assert config.execution.max_retries == 5 assert config.jailbreak.num_techniques == 20 assert len(config.counterfactual_bias.demographics) == 9 assert config.counterfactual_bias.extended_dimensions == ["age"] class TestGetPresetConfig: """Tests for get_preset_config function.""" def test_valid_presets(self): """Test getting valid presets.""" for preset_name in ["quick", "standard", "comprehensive"]: config = get_preset_config(preset_name) assert isinstance(config, PerturbationTestConfig) def test_invalid_preset(self): """Test invalid preset raises error.""" with pytest.raises(ValueError) as exc_info: get_preset_config("invalid") assert "Unknown preset" in str(exc_info.value) def test_preset_is_copy(self): """Test that preset returns a copy.""" config1 = get_preset_config("standard") config2 = get_preset_config("standard") # Modify one should not affect the other config1.max_relations = 999 assert config2.max_relations == 10 class TestCreateConfigFromDict: """Tests for create_config_from_dict function.""" def test_basic_dict(self): """Test creating config from basic dict.""" data = { "model": "gpt-4", "max_relations": 5 } config = create_config_from_dict(data) assert config.model == "gpt-4" assert config.max_relations == 5 def test_nested_dict(self): """Test creating config from nested dict.""" data = { "model": "gpt-4", "execution": {"max_workers": 8}, "jailbreak": {"num_techniques": 15, "enabled": True}, "counterfactual_bias": {"comparison_mode": "all_pairs"} } config = create_config_from_dict(data) assert config.execution.max_workers == 8 assert config.jailbreak.num_techniques == 15 assert config.counterfactual_bias.comparison_mode == "all_pairs" def test_empty_dict(self): """Test creating config from empty dict uses defaults.""" config = create_config_from_dict({}) assert config.model == "gpt-4o-mini" assert config.execution.max_workers == 5 class TestExtendedDemographics: """Tests for extended demographics constants.""" def test_extended_demographics_keys(self): """Test extended demographics keys exist.""" assert "age" in EXTENDED_DEMOGRAPHICS assert "disability" in EXTENDED_DEMOGRAPHICS assert "socioeconomic" in EXTENDED_DEMOGRAPHICS def test_age_options(self): """Test age dimension options.""" assert len(EXTENDED_DEMOGRAPHICS["age"]) == 3 assert "young (20s)" in EXTENDED_DEMOGRAPHICS["age"] assert "elderly (70s)" in EXTENDED_DEMOGRAPHICS["age"] def test_disability_options(self): """Test disability dimension options.""" assert len(EXTENDED_DEMOGRAPHICS["disability"]) == 3 def test_socioeconomic_options(self): """Test socioeconomic dimension options.""" assert len(EXTENDED_DEMOGRAPHICS["socioeconomic"]) == 3