v0.2.11-pre.002-fix.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_decimal.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[test]
|
||||
fn decimal_normalizes_fractional_and_scientific_forms_without_f64() -> ksp_core_lib::Result<()> {
|
||||
@@ -11,7 +11,7 @@ fn decimal_normalizes_fractional_and_scientific_forms_without_f64() -> ksp_core_
|
||||
("1e-3", "0.001", 1, 3),
|
||||
];
|
||||
for (source, expected, coefficient, scale) in cases {
|
||||
let value = match crate::PriceDecimal::parse(source) {
|
||||
let value = match crate::MarketPriceDecimal::parse(source) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -26,34 +26,34 @@ fn decimal_normalizes_fractional_and_scientific_forms_without_f64() -> ksp_core_
|
||||
fn decimal_rejects_zero_negative_nonfinite_excessive_scale_and_overflow() {
|
||||
let invalid = ["0", "0.000", "-1", "+1", "NaN", "inf", "1e-19", "1e129", "340282366920938463463374607431768211456", " 1", "1 ", ".1", "1."];
|
||||
for source in invalid {
|
||||
let result = crate::PriceDecimal::parse(source);
|
||||
let result = crate::MarketPriceDecimal::parse(source);
|
||||
assert!(result.is_err());
|
||||
if let std::result::Result::Err(error) = result {
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_PRICE_DECIMAL_INVALID);
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_MARKET_PRICE_DECIMAL_INVALID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decimal_serde_is_canonical_string_and_round_trips_exactly() -> ksp_core_lib::Result<()> {
|
||||
let value = match crate::PriceDecimal::parse("123.4500") {
|
||||
let value = match crate::MarketPriceDecimal::parse("123.4500") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let encoded = match serde_json::to_string(&value) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_PRICE_DECIMAL_INVALID, "test serialization failed"));
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_MARKET_PRICE_DECIMAL_INVALID, "test serialization failed"));
|
||||
},
|
||||
};
|
||||
assert_eq!(encoded, "\"123.45\"");
|
||||
let decoded: crate::PriceDecimal = match serde_json::from_str(encoded.as_str()) {
|
||||
let decoded: crate::MarketPriceDecimal = match serde_json::from_str(encoded.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_PRICE_DECIMAL_INVALID, "test deserialization failed"));
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_MARKET_PRICE_DECIMAL_INVALID, "test deserialization failed"));
|
||||
},
|
||||
};
|
||||
assert_eq!(decoded, value);
|
||||
assert!(serde_json::from_str::<crate::PriceDecimal>("123.45").is_err());
|
||||
assert!(serde_json::from_str::<crate::MarketPriceDecimal>("123.45").is_err());
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_observation.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[test]
|
||||
fn observation_preserves_pair_exact_price_semantics_timestamps_and_safe_provenance() -> ksp_core_lib::Result<()> {
|
||||
let provider_id = match crate::PriceProviderId::new("kraken") {
|
||||
let provider_id = match crate::MarketPriceProviderId::new("kraken") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let price = match crate::PriceDecimal::parse("204.125") {
|
||||
let price = match crate::MarketPriceDecimal::parse("204.125") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let provenance = match crate::PriceProvenance::new("market=SOL/USD") {
|
||||
let provenance = match crate::MarketPriceProvenance::new("market=SOL/USD") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let started = crate::PriceTimestamp::from_unix_millis(10_000);
|
||||
let received = crate::PriceTimestamp::from_unix_millis(10_250);
|
||||
let observation = match crate::SolUsdPriceObservation::new(
|
||||
let started = crate::MarketPriceTimestamp::from_unix_millis(10_000);
|
||||
let received = crate::MarketPriceTimestamp::from_unix_millis(10_250);
|
||||
let observation = match crate::MarketPriceObservation::new(
|
||||
provider_id,
|
||||
price,
|
||||
crate::PriceSemantics::ExchangeLastTrade,
|
||||
crate::MarketPriceSemantics::ExchangeLastTrade,
|
||||
started,
|
||||
received,
|
||||
std::option::Option::None,
|
||||
@@ -29,10 +29,10 @@ fn observation_preserves_pair_exact_price_semantics_timestamps_and_safe_provenan
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
assert_eq!(observation.pair(), crate::PricePair::SolUsd);
|
||||
assert_eq!(observation.pair(), crate::MarketPricePair::SolUsd);
|
||||
assert_eq!(observation.pair().code(), "SOL/USD");
|
||||
assert_eq!(observation.price(), price);
|
||||
assert_eq!(observation.semantics(), crate::PriceSemantics::ExchangeLastTrade);
|
||||
assert_eq!(observation.semantics(), crate::MarketPriceSemantics::ExchangeLastTrade);
|
||||
assert_eq!(observation.request_started_at(), started);
|
||||
assert_eq!(observation.received_at(), received);
|
||||
assert_eq!(observation.provider_timestamp(), std::option::Option::None);
|
||||
@@ -42,25 +42,25 @@ fn observation_preserves_pair_exact_price_semantics_timestamps_and_safe_provenan
|
||||
|
||||
#[test]
|
||||
fn observation_rejects_reversed_ksp_timestamps_and_unsafe_provenance() -> ksp_core_lib::Result<()> {
|
||||
assert!(crate::PriceProvenance::new("line\nbreak").is_err());
|
||||
let provider_id = match crate::PriceProviderId::new("coingecko") {
|
||||
assert!(crate::MarketPriceProvenance::new("line\nbreak").is_err());
|
||||
let provider_id = match crate::MarketPriceProviderId::new("coingecko") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let price = match crate::PriceDecimal::parse("200") {
|
||||
let price = match crate::MarketPriceDecimal::parse("200") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let provenance = match crate::PriceProvenance::new("asset=solana") {
|
||||
let provenance = match crate::MarketPriceProvenance::new("asset=solana") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let result = crate::SolUsdPriceObservation::new(
|
||||
let result = crate::MarketPriceObservation::new(
|
||||
provider_id,
|
||||
price,
|
||||
crate::PriceSemantics::AggregatedMarket,
|
||||
crate::PriceTimestamp::from_unix_millis(2),
|
||||
crate::PriceTimestamp::from_unix_millis(1),
|
||||
crate::MarketPriceSemantics::AggregatedMarket,
|
||||
crate::MarketPriceTimestamp::from_unix_millis(2),
|
||||
crate::MarketPriceTimestamp::from_unix_millis(1),
|
||||
std::option::Option::None,
|
||||
provenance,
|
||||
);
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_provider.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[test]
|
||||
fn provider_id_is_opaque_bounded_and_stable() -> ksp_core_lib::Result<()> {
|
||||
let id = match crate::PriceProviderId::new("coingecko-main") {
|
||||
let id = match crate::MarketPriceProviderId::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());
|
||||
assert!(crate::MarketPriceProviderId::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") {
|
||||
let id = match crate::MarketPriceProviderId::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) {
|
||||
let rate_limit = match crate::MarketPriceProviderRateLimit::fixed(1, 2, std::option::Option::None, crate::MarketPriceProviderRateLimitScope::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) {
|
||||
let quota = match crate::MarketPriceProviderLongTermQuota::new(
|
||||
10_000,
|
||||
crate::MarketPriceProviderQuotaPeriod::Month,
|
||||
crate::MarketPriceProviderQuotaUnit::Credits,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let descriptor = match crate::PriceProviderDescriptor::new(
|
||||
let descriptor = match crate::MarketPriceProviderDescriptor::new(
|
||||
id,
|
||||
"Provider A",
|
||||
crate::PriceSemantics::AggregatedMarket,
|
||||
crate::PriceProviderAuthMode::OptionalApiKey,
|
||||
crate::MarketPriceSemantics::AggregatedMarket,
|
||||
crate::MarketPriceProviderAuthMode::OptionalApiKey,
|
||||
rate_limit,
|
||||
std::option::Option::Some(quota),
|
||||
true,
|
||||
@@ -42,8 +46,8 @@ fn provider_descriptor_preserves_semantics_auth_limits_and_informational_quota()
|
||||
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.semantics(), crate::MarketPriceSemantics::AggregatedMarket);
|
||||
assert_eq!(descriptor.auth_mode(), crate::MarketPriceProviderAuthMode::OptionalApiKey);
|
||||
assert_eq!(descriptor.rate_limit(), rate_limit);
|
||||
assert_eq!(descriptor.long_term_quota(), std::option::Option::Some(quota));
|
||||
assert!(descriptor.supports_sol_usd());
|
||||
@@ -52,12 +56,12 @@ fn provider_descriptor_preserves_semantics_auth_limits_and_informational_quota()
|
||||
|
||||
#[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!(crate::MarketPriceProviderRateLimit::fixed(0, 1, std::option::Option::None, crate::MarketPriceProviderRateLimitScope::Ip).is_err());
|
||||
assert!(crate::MarketPriceProviderRateLimit::fixed(1, 0, std::option::Option::None, crate::MarketPriceProviderRateLimitScope::Ip).is_err());
|
||||
assert!(crate::MarketPriceProviderRateLimit::fixed(1, 1, std::option::Option::Some(0), crate::MarketPriceProviderRateLimitScope::Ip).is_err());
|
||||
let dynamic = crate::MarketPriceProviderRateLimit::dynamic(crate::MarketPriceProviderRateLimitScope::Ip);
|
||||
assert_eq!(dynamic.kind(), crate::MarketPriceProviderRateLimitKind::Dynamic);
|
||||
assert_eq!(dynamic.scope(), crate::MarketPriceProviderRateLimitScope::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);
|
||||
@@ -65,13 +69,13 @@ fn provider_limit_descriptors_reject_zero_and_model_dynamic_scope() {
|
||||
|
||||
#[test]
|
||||
fn provider_availability_keeps_cooldown_and_outage_distinct() -> ksp_core_lib::Result<()> {
|
||||
let id = match crate::PriceProviderId::new("jupiter") {
|
||||
let id = match crate::MarketPriceProviderId::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) });
|
||||
let retry_at = crate::MarketPriceTimestamp::from_unix_millis(1_777_777_777_000);
|
||||
let state = crate::MarketPriceProviderState::new(id, crate::MarketPriceProviderAvailability::CoolingDown { retry_at });
|
||||
assert_eq!(state.availability(), crate::MarketPriceProviderAvailability::CoolingDown { retry_at });
|
||||
assert_ne!(state.availability(), crate::MarketPriceProviderAvailability::TemporarilyUnavailable { retry_at: std::option::Option::Some(retry_at) });
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_settings.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[test]
|
||||
fn common_settings_contain_only_generic_identity_and_enablement() -> ksp_core_lib::Result<()> {
|
||||
let provider_id = match crate::PriceProviderId::new("coinpaprika") {
|
||||
let provider_id = match crate::MarketPriceProviderId::new("coinpaprika") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let settings = crate::PriceProviderCommonSettings::new(provider_id, true);
|
||||
let settings = crate::MarketPriceProviderCommonSettings::new(provider_id, true);
|
||||
assert!(settings.enabled());
|
||||
assert_eq!(settings.provider_id().as_str(), "coinpaprika");
|
||||
let serialized = match serde_json::to_value(&settings) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_PROVIDER_SETTINGS_INVALID, "test serialization failed"));
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_MARKET_PRICE_PROVIDER_SETTINGS_INVALID, "test serialization failed"));
|
||||
},
|
||||
};
|
||||
assert!(serialized.get("api_key").is_none());
|
||||
|
||||
Reference in New Issue
Block a user