//! Comprehensive validation edge cases tests for config package //! //! This test suite provides additional edge case coverage for: //! - ML config validation (SimulationConfig, TrainingConfig) //! - Risk config validation (StressScenarioConfig, AssetClassMapping) //! - Broker config validation (routing rules, commission rates) //! - URL parsing and malformed input handling //! - Numeric overflow and boundary conditions //! - String edge cases (Unicode, special chars, empty strings) //! - Deserialization edge cases (malformed JSON/TOML) //! - Environment variable parsing edge cases use config::{ ml_config::{SimulationConfig, MarketState, SymbolConfig as MLSymbolConfig, MarketCapTier}, risk_config::{AssetClass as RiskAssetClass, AssetClassMapping, RiskConfig as RiskConfigV2, StressScenarioConfig}, schemas::{AssetClassificationSchema, S3Config}, structures::{BrokerConfig, BrokerRoutingRule, CircuitBreakerConfig, CommissionConfig, KellyConfig, PositionLimitsConfig, VarConfig}, database::DatabaseConfig, runtime::{DatabaseRuntimeConfig, Environment, TimeoutConfig, LimitsConfig}, }; use std::collections::HashMap; use std::time::Duration; // ============================================================================ // ML Config Validation Tests // ============================================================================ #[test] fn test_simulation_config_negative_price() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.initial_price = -100.0; // Negative prices are structurally allowed but economically invalid // In production, this should be validated assert_eq!(config.initial_market_state.default_symbol.initial_price, -100.0); } #[test] fn test_simulation_config_zero_price() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.initial_price = 0.0; // Zero prices are structurally allowed assert_eq!(config.initial_market_state.default_symbol.initial_price, 0.0); } #[test] fn test_simulation_config_infinity_price() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.initial_price = f64::INFINITY; // Infinity is structurally allowed (serde serializes it) assert!(config.initial_market_state.default_symbol.initial_price.is_infinite()); } #[test] fn test_simulation_config_nan_price() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.initial_price = f64::NAN; // NaN is structurally allowed assert!(config.initial_market_state.default_symbol.initial_price.is_nan()); } #[test] fn test_simulation_config_negative_volatility() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.volatility = -0.5; // Negative volatility is economically invalid but structurally allowed assert_eq!(config.initial_market_state.default_symbol.volatility, -0.5); } #[test] fn test_simulation_config_extreme_volatility() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.volatility = 100.0; // 10,000% volatility // Extreme volatility is structurally allowed assert_eq!(config.initial_market_state.default_symbol.volatility, 100.0); } #[test] fn test_simulation_config_negative_volume() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.base_volume = -1000000.0; // Negative volume is economically invalid but structurally allowed assert_eq!(config.initial_market_state.default_symbol.base_volume, -1000000.0); } #[test] fn test_simulation_config_spread_min_greater_than_max() { let mut config = SimulationConfig::default(); config.initial_market_state.default_symbol.min_spread_bps = 50.0; config.initial_market_state.default_symbol.max_spread_bps = 10.0; // Min > Max is logically invalid but structurally allowed assert!(config.initial_market_state.default_symbol.min_spread_bps > config.initial_market_state.default_symbol.max_spread_bps); } #[test] fn test_simulation_parameters_zero_update_rate() { let mut config = SimulationConfig::default(); config.parameters.update_rate_hz = 0; // Zero update rate would cause division by zero in interval calculations assert_eq!(config.parameters.update_rate_hz, 0); } #[test] fn test_simulation_parameters_extreme_update_rate() { let mut config = SimulationConfig::default(); config.parameters.update_rate_hz = u32::MAX; // ~4 billion Hz // Extreme update rate is structurally allowed assert_eq!(config.parameters.update_rate_hz, u32::MAX); } #[test] fn test_simulation_parameters_trend_out_of_range() { let mut config = SimulationConfig::default(); config.parameters.trend = 5.0; // Should be -1.0 to 1.0 // Out-of-range trend is structurally allowed assert_eq!(config.parameters.trend, 5.0); } #[test] fn test_test_symbol_config_zero_count() { let mut config = SimulationConfig::default(); config.test_symbols.count = 0; // Zero symbols is valid (no test symbols generated) assert_eq!(config.test_symbols.count, 0); } #[test] fn test_test_symbol_config_extreme_count() { let mut config = SimulationConfig::default(); config.test_symbols.count = usize::MAX; // Extreme count would exhaust memory but is structurally allowed assert_eq!(config.test_symbols.count, usize::MAX); } #[test] fn test_test_symbol_config_inverted_price_range() { let mut config = SimulationConfig::default(); config.test_symbols.price_range = (500.0, 50.0); // Max < Min // Inverted range is logically invalid but structurally allowed assert!(config.test_symbols.price_range.0 > config.test_symbols.price_range.1); } #[test] fn test_test_symbol_config_negative_price_range() { let mut config = SimulationConfig::default(); config.test_symbols.price_range = (-100.0, -10.0); // Negative prices are economically invalid but structurally allowed assert!(config.test_symbols.price_range.0 < 0.0); } #[test] fn test_market_state_empty_symbols() { let market_state = MarketState { symbols: HashMap::new(), default_symbol: MLSymbolConfig { initial_price: 100.0, volatility: 0.3, base_volume: 1_000_000.0, min_spread_bps: 5.0, max_spread_bps: 20.0, market_cap_tier: MarketCapTier::Test, }, }; // Empty symbols map is valid (all symbols use default) assert_eq!(market_state.symbols.len(), 0); } #[test] fn test_market_state_duplicate_symbol_keys() { let mut symbols = HashMap::new(); symbols.insert("AAPL".to_string(), MLSymbolConfig { initial_price: 150.0, volatility: 0.25, base_volume: 50_000_000.0, min_spread_bps: 1.0, max_spread_bps: 5.0, market_cap_tier: MarketCapTier::LargeCap, }); // HashMap automatically handles duplicates (overwrites) symbols.insert("AAPL".to_string(), MLSymbolConfig { initial_price: 160.0, volatility: 0.3, base_volume: 60_000_000.0, min_spread_bps: 2.0, max_spread_bps: 8.0, market_cap_tier: MarketCapTier::LargeCap, }); // Second insert overwrites first assert_eq!(symbols.get("AAPL").unwrap().initial_price, 160.0); } // ============================================================================ // Risk Config Validation Tests // ============================================================================ #[test] fn test_stress_scenario_empty_id() { let scenario = StressScenarioConfig { id: String::new(), name: "Test Scenario".to_string(), description: "Test".to_string(), instrument_shocks: HashMap::new(), asset_class_shocks: HashMap::new(), volatility_multiplier: 1.5, volatility_multipliers: HashMap::new(), correlation_adjustments: HashMap::new(), liquidity_haircuts: HashMap::new(), is_active: true, }; // Empty ID is structurally allowed but should be validated assert!(scenario.id.is_empty()); } #[test] fn test_stress_scenario_negative_volatility_multiplier() { let scenario = StressScenarioConfig { id: "test-1".to_string(), name: "Test Scenario".to_string(), description: "Test".to_string(), instrument_shocks: HashMap::new(), asset_class_shocks: HashMap::new(), volatility_multiplier: -2.0, volatility_multipliers: HashMap::new(), correlation_adjustments: HashMap::new(), liquidity_haircuts: HashMap::new(), is_active: true, }; // Negative multiplier is economically invalid assert!(scenario.volatility_multiplier < 0.0); } #[test] fn test_stress_scenario_extreme_instrument_shocks() { let mut shocks = HashMap::new(); shocks.insert("AAPL".to_string(), -100.0); // -10,000% shock (total loss + more) shocks.insert("MSFT".to_string(), 1000.0); // 100,000% gain let scenario = StressScenarioConfig { id: "extreme-1".to_string(), name: "Extreme Scenario".to_string(), description: "Extreme shocks".to_string(), instrument_shocks: shocks, asset_class_shocks: HashMap::new(), volatility_multiplier: 1.0, volatility_multipliers: HashMap::new(), correlation_adjustments: HashMap::new(), liquidity_haircuts: HashMap::new(), is_active: true, }; // Extreme shocks are structurally allowed assert_eq!(scenario.instrument_shocks.get("AAPL"), Some(&-100.0)); assert_eq!(scenario.instrument_shocks.get("MSFT"), Some(&1000.0)); } #[test] fn test_stress_scenario_liquidity_haircut_greater_than_one() { let mut haircuts = HashMap::new(); haircuts.insert(RiskAssetClass::SmallCapEquity, 1.5); // 150% haircut let scenario = StressScenarioConfig { id: "liquidity-1".to_string(), name: "Liquidity Test".to_string(), description: "Test liquidity haircuts".to_string(), instrument_shocks: HashMap::new(), asset_class_shocks: HashMap::new(), volatility_multiplier: 1.0, volatility_multipliers: HashMap::new(), correlation_adjustments: HashMap::new(), liquidity_haircuts: haircuts, is_active: true, }; // Haircut > 1.0 means position value goes negative (economically invalid) assert!(scenario.liquidity_haircuts.get(&RiskAssetClass::SmallCapEquity).unwrap() > &1.0); } #[test] fn test_asset_class_mapping_empty_mappings() { let mapping = AssetClassMapping { mappings: HashMap::new(), default_class: RiskAssetClass::Alternatives, }; // Empty mappings is valid (all symbols use default) assert_eq!(mapping.mappings.len(), 0); } #[test] fn test_asset_class_mapping_get_unmapped_symbol() { let mapping = AssetClassMapping { mappings: HashMap::new(), default_class: RiskAssetClass::Technology, }; // Unmapped symbol should return None assert_eq!(mapping.mappings.get("UNKNOWN"), None); } #[test] fn test_risk_config_negative_var_confidence() { let config = RiskConfigV2 { stress_scenarios: Vec::new(), asset_class_mapping: AssetClassMapping { mappings: HashMap::new(), default_class: RiskAssetClass::Alternatives, }, default_volatility_multiplier: 1.0, max_portfolio_loss_pct: 20.0, var_confidence_level: -0.5, // Invalid: should be 0.0-1.0 var_time_horizon_days: 1, }; // Negative confidence is mathematically invalid assert!(config.var_confidence_level < 0.0); } #[test] fn test_risk_config_var_confidence_greater_than_one() { let config = RiskConfigV2 { stress_scenarios: Vec::new(), asset_class_mapping: AssetClassMapping { mappings: HashMap::new(), default_class: RiskAssetClass::Alternatives, }, default_volatility_multiplier: 1.0, max_portfolio_loss_pct: 20.0, var_confidence_level: 1.5, // Invalid: should be 0.0-1.0 var_time_horizon_days: 1, }; // Confidence > 1.0 is mathematically invalid assert!(config.var_confidence_level > 1.0); } #[test] fn test_risk_config_zero_time_horizon() { let config = RiskConfigV2 { stress_scenarios: Vec::new(), asset_class_mapping: AssetClassMapping { mappings: HashMap::new(), default_class: RiskAssetClass::Alternatives, }, default_volatility_multiplier: 1.0, max_portfolio_loss_pct: 20.0, var_confidence_level: 0.95, var_time_horizon_days: 0, // Zero horizon is invalid }; // Zero time horizon makes no economic sense assert_eq!(config.var_time_horizon_days, 0); } // ============================================================================ // Broker Config Validation Tests // ============================================================================ #[test] fn test_broker_config_empty_routing_rules() { let config = BrokerConfig { routing_rules: Vec::new(), default_broker: "IBKR".to_string(), commission_rates: HashMap::new(), }; // Empty rules is valid (falls back to default broker) assert_eq!(config.routing_rules.len(), 0); } #[test] fn test_broker_config_empty_default_broker() { let config = BrokerConfig { routing_rules: Vec::new(), default_broker: String::new(), commission_rates: HashMap::new(), }; // Empty default broker would cause issues but is structurally allowed assert!(config.default_broker.is_empty()); } #[test] fn test_broker_routing_rule_invalid_regex() { let rule = BrokerRoutingRule { priority: 100, symbol_pattern: "[invalid(regex".to_string(), // Invalid regex min_quantity: None, max_quantity: None, broker_id: "IBKR".to_string(), description: "Test invalid regex".to_string(), }; // Invalid regex is structurally allowed (fails at runtime) assert!(regex::Regex::new(&rule.symbol_pattern).is_err()); } #[test] fn test_broker_routing_rule_min_greater_than_max_quantity() { let rule = BrokerRoutingRule { priority: 100, symbol_pattern: ".*".to_string(), min_quantity: Some(1_000_000.0), max_quantity: Some(10_000.0), // Min > Max broker_id: "IBKR".to_string(), description: "Test invalid quantity range".to_string(), }; // Inverted range is logically invalid assert!(rule.min_quantity.unwrap() > rule.max_quantity.unwrap()); } #[test] fn test_broker_routing_rule_negative_quantity() { let rule = BrokerRoutingRule { priority: 100, symbol_pattern: ".*".to_string(), min_quantity: Some(-10_000.0), max_quantity: Some(-1_000.0), broker_id: "IBKR".to_string(), description: "Test negative quantities".to_string(), }; // Negative quantities are economically invalid assert!(rule.min_quantity.unwrap() < 0.0); } #[test] fn test_commission_config_negative_rate() { let config = CommissionConfig { rate_bps: -0.00005, // Negative commission (broker pays you) min_commission: 1.0, }; // Negative rate is economically unusual but structurally allowed assert!(config.rate_bps < 0.0); } #[test] fn test_commission_config_negative_min_commission() { let config = CommissionConfig { rate_bps: 0.00005, min_commission: -10.0, // Negative minimum }; // Negative minimum is economically invalid assert!(config.min_commission < 0.0); } #[test] fn test_commission_config_extreme_rate() { let config = CommissionConfig { rate_bps: 100.0, // 10,000 bps = 100% commission min_commission: 0.0, }; // 100% commission is economically absurd but structurally allowed assert_eq!(config.rate_bps, 100.0); } // ============================================================================ // Structure Config Validation Tests // ============================================================================ #[test] fn test_var_config_confidence_at_boundaries() { let config_zero = VarConfig { confidence_level: 0.0, ..VarConfig::default() }; let config_one = VarConfig { confidence_level: 1.0, ..VarConfig::default() }; // Boundaries are mathematically valid assert_eq!(config_zero.confidence_level, 0.0); assert_eq!(config_one.confidence_level, 1.0); } #[test] fn test_var_config_negative_lookback_period() { // u32 can't be negative, but testing zero let config = VarConfig { lookback_period_days: 0, ..VarConfig::default() }; // Zero lookback makes no sense assert_eq!(config.lookback_period_days, 0); } #[test] fn test_var_config_empty_calculation_method() { let config = VarConfig { calculation_method: String::new(), ..VarConfig::default() }; // Empty method is structurally allowed but should be validated assert!(config.calculation_method.is_empty()); } #[test] fn test_var_config_unknown_calculation_method() { let config = VarConfig { calculation_method: "quantum_flux_capacitor".to_string(), ..VarConfig::default() }; // Unknown method is structurally allowed assert_eq!(config.calculation_method, "quantum_flux_capacitor"); } #[test] fn test_kelly_config_negative_fraction() { let config = KellyConfig { kelly_fraction: -0.5, ..KellyConfig::default() }; // Negative Kelly fraction is mathematically invalid assert!(config.kelly_fraction < 0.0); } #[test] fn test_kelly_config_fraction_greater_than_one() { let config = KellyConfig { kelly_fraction: 2.0, // 200% Kelly = extreme leverage ..KellyConfig::default() }; // Fraction > 1.0 means over-leveraging assert!(config.kelly_fraction > 1.0); } #[test] fn test_kelly_config_min_greater_than_max_leverage() { let config = KellyConfig { min_kelly_leverage: 5.0, max_kelly_leverage: 2.0, // Min > Max ..KellyConfig::default() }; // Inverted range is logically invalid assert!(config.min_kelly_leverage > config.max_kelly_leverage); } #[test] fn test_kelly_config_zero_lookback_periods() { let config = KellyConfig { lookback_periods: 0, ..KellyConfig::default() }; // Zero lookback makes no sense assert_eq!(config.lookback_periods, 0); } #[test] fn test_circuit_breaker_negative_threshold() { let config = CircuitBreakerConfig { enabled: true, price_move_threshold: -0.05, // Negative threshold makes no sense halt_duration_seconds: 300, }; // Negative threshold is logically invalid assert!(config.price_move_threshold < 0.0); } #[test] fn test_circuit_breaker_zero_halt_duration() { let config = CircuitBreakerConfig { enabled: true, price_move_threshold: 0.05, halt_duration_seconds: 0, // Zero duration = instant resume }; // Zero duration defeats the purpose but is structurally allowed assert_eq!(config.halt_duration_seconds, 0); } #[test] fn test_circuit_breaker_extreme_halt_duration() { let config = CircuitBreakerConfig { enabled: true, price_move_threshold: 0.05, halt_duration_seconds: u64::MAX, // ~585 billion years }; // Extreme duration is structurally allowed assert_eq!(config.halt_duration_seconds, u64::MAX); } #[test] fn test_position_limits_negative_limits() { let config = PositionLimitsConfig { global_limit: -10_000_000.0, max_leverage: -3.0, max_var_limit: -100_000.0, }; // Negative limits are economically invalid assert!(config.global_limit < 0.0); assert!(config.max_leverage < 0.0); assert!(config.max_var_limit < 0.0); } #[test] fn test_position_limits_zero_max_leverage() { let config = PositionLimitsConfig { global_limit: 10_000_000.0, max_leverage: 0.0, // Zero leverage = cash only max_var_limit: 100_000.0, }; // Zero leverage is valid (no borrowing) assert_eq!(config.max_leverage, 0.0); } // ============================================================================ // URL Parsing and Malformed Input Tests // ============================================================================ #[test] fn test_s3_config_malformed_endpoint_no_scheme() { let config = S3Config { endpoint_url: Some("localhost:9000".to_string()), // Missing http:// ..S3Config::default() }; // Missing scheme is structurally allowed (fails at connection time) assert_eq!(config.endpoint_url, Some("localhost:9000".to_string())); } #[test] fn test_s3_config_endpoint_invalid_characters() { let config = S3Config { endpoint_url: Some("http://local host:9000".to_string()), // Space in URL ..S3Config::default() }; // Invalid URL is structurally allowed assert!(config.endpoint_url.unwrap().contains(' ')); } #[test] fn test_database_config_malformed_url_no_protocol() { let config = DatabaseConfig { url: "localhost:5432/foxhunt".to_string(), // Missing postgresql:// ..DatabaseConfig::default() }; // Missing protocol is structurally allowed assert!(config.validate().is_ok()); assert!(!config.url.starts_with("postgresql://")); } #[test] fn test_database_config_url_special_characters() { let config = DatabaseConfig { url: "postgresql://user:p@ss#word!@localhost:5432/db".to_string(), ..DatabaseConfig::default() }; // Special characters in password are valid if properly encoded assert!(config.validate().is_ok()); } #[test] fn test_database_config_url_unicode_characters() { let config = DatabaseConfig { url: "postgresql://用户:密码@localhost:5432/数据库".to_string(), ..DatabaseConfig::default() }; // Unicode in URL is structurally allowed (may fail at connection) assert!(config.validate().is_ok()); } // ============================================================================ // Deserialization Edge Cases // ============================================================================ #[test] fn test_s3_config_json_deserialization_with_nulls() { let json = r#"{ "bucket_name": "test-bucket", "region": "us-east-1", "access_key_id": null, "secret_access_key": null, "session_token": null, "endpoint_url": null, "force_path_style": false, "timeout": {"secs": 30, "nanos": 0}, "max_retry_attempts": 3, "use_ssl": true }"#; let result: Result = serde_json::from_str(json); assert!(result.is_ok()); let config = result.unwrap(); assert!(config.access_key_id.is_none()); } #[test] fn test_s3_config_json_deserialization_missing_optional_fields() { let json = r#"{ "bucket_name": "test-bucket", "region": "us-east-1", "force_path_style": false, "timeout": {"secs": 30, "nanos": 0}, "max_retry_attempts": 3, "use_ssl": true }"#; let result: Result = serde_json::from_str(json); assert!(result.is_ok()); let config = result.unwrap(); assert!(config.access_key_id.is_none()); } #[test] fn test_s3_config_json_deserialization_wrong_types() { let json = r#"{ "bucket_name": "test-bucket", "region": "us-east-1", "force_path_style": false, "timeout": {"secs": 30, "nanos": 0}, "max_retry_attempts": "three", "use_ssl": true }"#; // String instead of u32 should fail deserialization let result: Result = serde_json::from_str(json); assert!(result.is_err()); } #[test] fn test_database_config_json_with_extra_fields() { let json = r#"{ "url": "postgresql://localhost/db", "max_connections": 10, "min_connections": 1, "connect_timeout": {"secs": 30, "nanos": 0}, "query_timeout": {"secs": 60, "nanos": 0}, "enable_query_logging": false, "application_name": "test", "pool": { "min_connections": 1, "max_connections": 10, "acquire_timeout_secs": 30, "max_lifetime_secs": 3600, "idle_timeout_secs": 3600, "test_before_acquire": true, "database_url": "postgresql://localhost/db", "health_check_enabled": true, "health_check_interval_secs": 60 }, "transaction": { "isolation_level": "READ_COMMITTED", "timeout": {"secs": 30, "nanos": 0}, "default_timeout_secs": 30, "enable_retry": true, "max_retries": 3, "retry_delay_ms": 100, "max_savepoints": 10 } }"#; // Should deserialize successfully with correct field names let result: Result = serde_json::from_str(json); assert!(result.is_ok(), "Failed to deserialize: {:?}", result.err()); } #[test] fn test_asset_classification_schema_json_empty_patterns() { let json = r#"{ "asset_type_rules": {}, "default_sectors": {}, "currency_patterns": [], "crypto_patterns": [] }"#; let result: Result = serde_json::from_str(json); assert!(result.is_ok()); let schema = result.unwrap(); assert_eq!(schema.currency_patterns.len(), 0); } // ============================================================================ // Timeout Configuration Edge Cases // ============================================================================ #[test] fn test_timeout_config_duration_overflow() { // Duration::MAX is ~584 years - structurally valid but may not have validation let config = TimeoutConfig { grpc_connect_timeout: Duration::MAX, grpc_request_timeout: Duration::MAX, keep_alive_interval: Duration::MAX, keep_alive_timeout: Duration::MAX, max_concurrent_connections: 100, }; // Extreme durations are structurally allowed (no upper bound validation currently) // In production, these would timeout at connection time assert_eq!(config.grpc_connect_timeout, Duration::MAX); } #[test] fn test_database_runtime_config_very_short_timeouts() { let config = DatabaseRuntimeConfig { query_timeout: Duration::from_nanos(1), // 1 nanosecond connection_timeout: Duration::from_nanos(1), acquire_timeout: Duration::from_nanos(1), ..DatabaseRuntimeConfig::with_defaults(Environment::Development) }; // Very short timeouts are structurally allowed assert_eq!(config.query_timeout.as_nanos(), 1); } // ============================================================================ // Limits Configuration Edge Cases // ============================================================================ #[test] fn test_limits_config_extreme_batch_size() { let config = LimitsConfig { ml_max_batch_size: usize::MAX, ..LimitsConfig::with_defaults(Environment::Development) }; // Extreme batch size would exhaust memory but passes structural validation // Validation only checks > 0, not upper bounds let result = config.validate(); assert!(result.is_ok(), "Extreme batch size should be structurally valid"); assert_eq!(config.ml_max_batch_size, usize::MAX); } #[test] fn test_limits_config_var_confidence_boundary_conditions() { // Test exactly at boundaries (0.0 and 1.0) let config_zero = LimitsConfig { risk_var_confidence: 0.0, ..LimitsConfig::with_defaults(Environment::Development) }; let config_one = LimitsConfig { risk_var_confidence: 1.0, ..LimitsConfig::with_defaults(Environment::Development) }; // Boundaries should be valid assert!(config_zero.validate().is_ok()); assert!(config_one.validate().is_ok()); } #[test] fn test_limits_config_var_confidence_epsilon_outside_bounds() { let config_below = LimitsConfig { risk_var_confidence: -f64::EPSILON, ..LimitsConfig::with_defaults(Environment::Development) }; let config_above = LimitsConfig { risk_var_confidence: 1.0 + f64::EPSILON, ..LimitsConfig::with_defaults(Environment::Development) }; // Even epsilon outside bounds should fail assert!(config_below.validate().is_err()); assert!(config_above.validate().is_err()); }