use alloy_chains::NamedChain;
use semioscan::{ChainConfig, RpcConfig, SemioscanConfig, SemioscanConfigBuilder};
use std::time::Duration;
#[test]
fn test_default_config_has_strict_chain_limits() {
let config = SemioscanConfig::default();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(Duration::from_millis(250)),
"Base should have 250ms delay by default"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Sonic),
Some(Duration::from_millis(250)),
"Sonic should have 250ms delay by default"
);
}
#[test]
fn test_default_config_no_limits_for_permissive_chains() {
let config = SemioscanConfig::default();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
None,
"Arbitrum should have no delay by default"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Optimism),
None,
"Optimism should have no delay by default"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Polygon),
None,
"Polygon should have no delay by default"
);
}
#[test]
fn test_minimal_config_no_limits() {
let config = SemioscanConfig::minimal();
assert_eq!(config.get_rate_limit_delay(NamedChain::Base), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Sonic), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Arbitrum), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Mainnet), None);
}
#[test]
fn test_global_rate_limit_applies_to_all_chains() {
let global_delay = Duration::from_millis(500);
let config = SemioscanConfigBuilder::new()
.rate_limit_delay(global_delay)
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(global_delay)
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(global_delay)
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Polygon),
Some(global_delay)
);
}
#[test]
fn test_chain_override_precedence() {
let global_delay = Duration::from_millis(500);
let arbitrum_delay = Duration::from_millis(100);
let config = SemioscanConfigBuilder::new()
.rate_limit_delay(global_delay)
.chain_rate_limit(NamedChain::Arbitrum, arbitrum_delay)
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(arbitrum_delay),
"Chain-specific delay should override global"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(global_delay),
"Non-overridden chains should use global delay"
);
}
#[test]
fn test_with_defaults_preserves_chain_limits() {
let config = SemioscanConfigBuilder::with_defaults()
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(Duration::from_millis(250)),
"Base default should be preserved"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Sonic),
Some(Duration::from_millis(250)),
"Sonic default should be preserved"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(Duration::from_millis(100)),
"New chain override should be applied"
);
}
#[test]
fn test_multiple_chain_overrides() {
let config = SemioscanConfigBuilder::new()
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Polygon, Duration::from_millis(200))
.chain_rate_limit(NamedChain::Mainnet, Duration::from_millis(300))
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(Duration::from_millis(100))
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Polygon),
Some(Duration::from_millis(200))
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Mainnet),
Some(Duration::from_millis(300))
);
assert_eq!(config.get_rate_limit_delay(NamedChain::Optimism), None);
}
#[test]
#[should_panic(expected = "rate_limit_delay must be > 0")]
fn test_rate_limit_delay_rejects_zero() {
let _ = SemioscanConfigBuilder::new()
.rate_limit_delay(Duration::from_millis(0))
.build();
}
#[test]
#[should_panic(expected = "rate_limit_delay must be > 0")]
fn test_chain_rate_limit_rejects_zero() {
let _ = SemioscanConfigBuilder::new()
.chain_rate_limit(NamedChain::Arbitrum, Duration::ZERO)
.build();
}
#[test]
#[should_panic(expected = "rate_limit_delay must be > 0")]
fn test_set_chain_override_rejects_zero_delay() {
let mut config = SemioscanConfig::minimal();
config.set_chain_override(
NamedChain::Arbitrum,
ChainConfig {
max_block_range: None,
rate_limit_delay: Some(Duration::ZERO),
rpc_timeout: None,
serial_lookup_fallback_attempts: None,
},
);
}
#[test]
#[should_panic(expected = "rate_limit_delay must be > 0")]
fn test_chain_config_builder_rejects_zero_delay() {
let _ = SemioscanConfigBuilder::new()
.chain_config(
NamedChain::Arbitrum,
ChainConfig {
max_block_range: None,
rate_limit_delay: Some(Duration::ZERO),
rpc_timeout: None,
serial_lookup_fallback_attempts: None,
},
)
.build();
}
#[test]
#[should_panic(expected = "rate_limit_delay must be > 0")]
fn test_rpc_config_with_rate_limit_delay_rejects_zero() {
let _ = RpcConfig::new(Duration::from_secs(30)).with_rate_limit_delay(Duration::ZERO);
}
#[test]
fn test_large_delay_values() {
let large_delay = Duration::from_secs(60); let config = SemioscanConfigBuilder::new()
.rate_limit_delay(large_delay)
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(large_delay)
);
}
#[test]
fn test_config_independence() {
let config1 = SemioscanConfigBuilder::new()
.rate_limit_delay(Duration::from_millis(100))
.build();
let config2 = SemioscanConfigBuilder::new()
.rate_limit_delay(Duration::from_millis(500))
.build();
assert_eq!(
config1.get_rate_limit_delay(NamedChain::Arbitrum),
Some(Duration::from_millis(100))
);
assert_eq!(
config2.get_rate_limit_delay(NamedChain::Arbitrum),
Some(Duration::from_millis(500))
);
}
#[test]
fn test_rate_limiting_for_major_chains() {
let config = SemioscanConfigBuilder::new()
.chain_rate_limit(NamedChain::Mainnet, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Base, Duration::from_millis(250))
.chain_rate_limit(NamedChain::Optimism, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Polygon, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Avalanche, Duration::from_millis(100))
.chain_rate_limit(NamedChain::BinanceSmartChain, Duration::from_millis(100))
.build();
assert!(config.get_rate_limit_delay(NamedChain::Mainnet).is_some());
assert!(config.get_rate_limit_delay(NamedChain::Arbitrum).is_some());
assert!(config.get_rate_limit_delay(NamedChain::Base).is_some());
assert!(config.get_rate_limit_delay(NamedChain::Optimism).is_some());
assert!(config.get_rate_limit_delay(NamedChain::Polygon).is_some());
assert!(config.get_rate_limit_delay(NamedChain::Avalanche).is_some());
assert!(config
.get_rate_limit_delay(NamedChain::BinanceSmartChain)
.is_some());
}
#[test]
fn test_config_clone_preserves_rate_limits() {
let original = SemioscanConfigBuilder::new()
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.build();
let cloned = original.clone();
assert_eq!(
original.get_rate_limit_delay(NamedChain::Arbitrum),
cloned.get_rate_limit_delay(NamedChain::Arbitrum)
);
}
#[test]
fn test_production_alchemy_config() {
let config = SemioscanConfigBuilder::with_defaults()
.chain_rate_limit(NamedChain::Mainnet, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.chain_rate_limit(NamedChain::Optimism, Duration::from_millis(100))
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(Duration::from_millis(250))
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Sonic),
Some(Duration::from_millis(250))
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Mainnet),
Some(Duration::from_millis(100))
);
}
#[test]
fn test_production_premium_rpc_config() {
let config = SemioscanConfig::minimal();
assert_eq!(config.get_rate_limit_delay(NamedChain::Base), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Sonic), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Mainnet), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Arbitrum), None);
}
#[test]
fn test_override_default_chain_limit() {
let config = SemioscanConfigBuilder::with_defaults()
.chain_rate_limit(NamedChain::Base, Duration::from_millis(500)) .build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(Duration::from_millis(500)),
"Should use overridden delay for Base"
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Sonic),
Some(Duration::from_millis(250)),
"Sonic default should be unchanged"
);
}
#[test]
fn test_clearing_chain_limits_with_minimal() {
let _ = SemioscanConfig::with_common_defaults();
let minimal = SemioscanConfig::minimal();
assert_eq!(minimal.get_rate_limit_delay(NamedChain::Base), None);
assert_eq!(minimal.get_rate_limit_delay(NamedChain::Sonic), None);
}