// file: crates/ksp-offchain-transport-lib/unit_tests/provider.rs // version: 1 #[test] fn provider_id_is_opaque_bounded_and_stable() -> ksp_core_lib::Result<()> { let id = match crate::PriceProviderId::new("coingecko-main") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; assert_eq!(id.as_str(), "coingecko-main"); assert_eq!(id.to_string(), "coingecko-main"); for invalid in ["", "CoinGecko", "coin gecko", "coin/gecko", "é"] { assert!(crate::PriceProviderId::new(invalid).is_err()); } return std::result::Result::Ok(()); } #[test] fn provider_descriptor_preserves_semantics_auth_limits_and_informational_quota() -> ksp_core_lib::Result<()> { let id = match crate::PriceProviderId::new("provider-a") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let rate_limit = match crate::PriceProviderRateLimit::fixed(1, 2, std::option::Option::None, crate::PriceProviderRateLimitScope::Ip) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let quota = match crate::PriceProviderLongTermQuota::new(10_000, crate::PriceProviderQuotaPeriod::Month, crate::PriceProviderQuotaUnit::Credits) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let descriptor = match crate::PriceProviderDescriptor::new( id, "Provider A", crate::PriceSemantics::AggregatedMarket, crate::PriceProviderAuthMode::OptionalApiKey, rate_limit, std::option::Option::Some(quota), true, ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; assert_eq!(descriptor.display_name(), "Provider A"); assert_eq!(descriptor.semantics(), crate::PriceSemantics::AggregatedMarket); assert_eq!(descriptor.auth_mode(), crate::PriceProviderAuthMode::OptionalApiKey); assert_eq!(descriptor.rate_limit(), rate_limit); assert_eq!(descriptor.long_term_quota(), std::option::Option::Some(quota)); assert!(descriptor.supports_sol_usd()); return std::result::Result::Ok(()); } #[test] fn provider_limit_descriptors_reject_zero_and_model_dynamic_scope() { assert!(crate::PriceProviderRateLimit::fixed(0, 1, std::option::Option::None, crate::PriceProviderRateLimitScope::Ip).is_err()); assert!(crate::PriceProviderRateLimit::fixed(1, 0, std::option::Option::None, crate::PriceProviderRateLimitScope::Ip).is_err()); assert!(crate::PriceProviderRateLimit::fixed(1, 1, std::option::Option::Some(0), crate::PriceProviderRateLimitScope::Ip).is_err()); let dynamic = crate::PriceProviderRateLimit::dynamic(crate::PriceProviderRateLimitScope::Ip); assert_eq!(dynamic.kind(), crate::PriceProviderRateLimitKind::Dynamic); assert_eq!(dynamic.scope(), crate::PriceProviderRateLimitScope::Ip); assert_eq!(dynamic.requests(), std::option::Option::None); assert_eq!(dynamic.window_seconds(), std::option::Option::None); assert_eq!(dynamic.burst(), std::option::Option::None); } #[test] fn provider_availability_keeps_cooldown_and_outage_distinct() -> ksp_core_lib::Result<()> { let id = match crate::PriceProviderId::new("jupiter") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let retry_at = crate::PriceTimestamp::from_unix_millis(1_777_777_777_000); let state = crate::PriceProviderState::new(id, crate::PriceProviderAvailability::CoolingDown { retry_at }); assert_eq!(state.availability(), crate::PriceProviderAvailability::CoolingDown { retry_at }); assert_ne!(state.availability(), crate::PriceProviderAvailability::TemporarilyUnavailable { retry_at: std::option::Option::Some(retry_at) }); return std::result::Result::Ok(()); }