Files
khadhroony-solana-project/crates/ksp-app-solprices-desk/unit_tests/market_price_runtime.rs
2026-08-27 10:43:01 +02:00

233 lines
12 KiB
Rust

// file: crates/ksp-app-solprices-desk/unit_tests/market_price_runtime.rs
// version: 2
fn descriptor() -> std::option::Option<ksp_offchain_transport_lib::MarketPriceProviderDescriptor> {
let provider_id = ksp_offchain_transport_lib::MarketPriceProviderId::new("test-provider");
let provider_id = match provider_id {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let rate_limit = ksp_offchain_transport_lib::MarketPriceProviderRateLimit::fixed(
10,
60,
std::option::Option::None,
ksp_offchain_transport_lib::MarketPriceProviderRateLimitScope::Unspecified,
);
let rate_limit = match rate_limit {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let descriptor = ksp_offchain_transport_lib::MarketPriceProviderDescriptor::new(
provider_id,
"Test Provider",
ksp_offchain_transport_lib::MarketPriceSemantics::AggregatedMarket,
ksp_offchain_transport_lib::MarketPriceProviderAuthMode::None,
rate_limit,
std::option::Option::None,
true,
);
return match descriptor {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[test]
fn generic_codes_cover_current_provider_neutral_enums() {
assert_eq!(super::auth_mode_code(ksp_offchain_transport_lib::MarketPriceProviderAuthMode::None), "none");
assert_eq!(super::auth_mode_code(ksp_offchain_transport_lib::MarketPriceProviderAuthMode::OptionalApiKey), "optional_api_key");
assert_eq!(super::auth_mode_code(ksp_offchain_transport_lib::MarketPriceProviderAuthMode::RequiredApiKey), "required_api_key");
assert_eq!(
super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::AuthenticationUnavailable),
"authentication_unavailable"
);
assert_eq!(
super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::CoolingDown {
retry_at: ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(123),
}),
"cooling_down"
);
assert_eq!(super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::Disabled), "disabled");
assert_eq!(super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::Misconfigured), "misconfigured");
assert_eq!(super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::QuotaUnavailable), "quota_unavailable");
assert_eq!(super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::Ready), "ready");
assert_eq!(
super::availability_code(ksp_offchain_transport_lib::MarketPriceProviderAvailability::TemporarilyUnavailable { retry_at: std::option::Option::None }),
"temporarily_unavailable"
);
assert_eq!(super::semantics_code(ksp_offchain_transport_lib::MarketPriceSemantics::AggregatedMarket), "aggregated_market");
assert_eq!(super::semantics_code(ksp_offchain_transport_lib::MarketPriceSemantics::DexPairUsd), "dex_pair_usd");
assert_eq!(super::semantics_code(ksp_offchain_transport_lib::MarketPriceSemantics::ExchangeLastTrade), "exchange_last_trade");
assert_eq!(super::semantics_code(ksp_offchain_transport_lib::MarketPriceSemantics::SolanaHeuristic), "solana_heuristic");
assert_eq!(super::semantics_code(ksp_offchain_transport_lib::MarketPriceSemantics::SolanaSpot), "solana_spot");
}
#[test]
fn initial_registry_row_contains_no_observation_or_loading_state() {
let descriptor = descriptor();
assert!(descriptor.is_some());
let descriptor = match descriptor {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let entry =
ksp_offchain_transport_lib::MarketPriceProviderRegistryEntry::new(descriptor, ksp_offchain_transport_lib::MarketPriceProviderAvailability::Ready);
let presentation = super::MarketPricePresentationEntry { loading: false, observation: std::option::Option::None };
let row = super::project_registry_entry(&entry, &presentation);
assert_eq!(row.provider_id, "test-provider");
assert_eq!(row.display_name, "Test Provider");
assert_eq!(row.pair, "SOL/USD");
assert_eq!(row.auth_mode, "none");
assert_eq!(row.availability, "ready");
assert_eq!(row.semantics, "aggregated_market");
assert!(!row.loading);
assert!(!row.refreshed);
assert_eq!(row.price, std::option::Option::None);
assert_eq!(row.provider_timestamp_unix_millis, std::option::Option::None);
assert_eq!(row.received_at_unix_millis, std::option::Option::None);
assert_eq!(row.retry_at_unix_millis, std::option::Option::None);
}
#[test]
fn observation_projection_keeps_exact_decimal_and_distinct_timestamps() {
let descriptor = descriptor();
assert!(descriptor.is_some());
let descriptor = match descriptor {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let provider_id = descriptor.id().clone();
let entry = ksp_offchain_transport_lib::MarketPriceProviderRegistryEntry::new(
descriptor,
ksp_offchain_transport_lib::MarketPriceProviderAvailability::CoolingDown {
retry_at: ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(400),
},
);
let price = ksp_offchain_transport_lib::MarketPriceDecimal::parse("123.4500");
assert!(price.is_ok());
let price = match price {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let provenance = ksp_offchain_transport_lib::MarketPriceProvenance::new("unit-test");
assert!(provenance.is_ok());
let provenance = match provenance {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let observation = ksp_offchain_transport_lib::MarketPriceObservation::new(
provider_id,
price,
ksp_offchain_transport_lib::MarketPriceSemantics::AggregatedMarket,
ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(100),
ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(300),
std::option::Option::Some(ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(200)),
provenance,
);
assert!(observation.is_ok());
let observation = match observation {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let presentation = super::MarketPricePresentationEntry { loading: true, observation: std::option::Option::Some(observation) };
let row = super::project_registry_entry(&entry, &presentation);
assert_eq!(row.price.as_deref(), std::option::Option::Some("123.45"));
assert_eq!(row.provider_timestamp_unix_millis.as_deref(), std::option::Option::Some("200"));
assert_eq!(row.received_at_unix_millis.as_deref(), std::option::Option::Some("300"));
assert_eq!(row.retry_at_unix_millis.as_deref(), std::option::Option::Some("400"));
assert!(row.loading);
assert!(row.refreshed);
}
#[tokio::test]
async fn disabled_refresh_clears_loading_without_inventing_observation() -> ksp_core_lib::Result<()> {
let settings = match ksp_offchain_transport_lib::MarketPriceCoinPaprikaSettings::new(false) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let service =
match ksp_offchain_transport_lib::MarketPriceService::new(std::vec![ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinPaprika(settings),]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let provider_id = match ksp_offchain_transport_lib::MarketPriceProviderId::new("coinpaprika") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut entries = std::collections::BTreeMap::new();
entries.insert(provider_id.as_str().to_owned(), super::MarketPricePresentationEntry { loading: true, observation: std::option::Option::None });
let mut presentation = super::MarketPricePresentationState { entries };
let outcome = match service.refresh(&provider_id).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert!(!outcome.refreshed());
let applied = super::apply_outcome_to_state(&mut presentation, &outcome);
if let std::result::Result::Err(error) = applied {
return std::result::Result::Err(error);
}
let entry = presentation.entries.get(provider_id.as_str());
assert!(entry.is_some());
if let std::option::Option::Some(entry) = entry {
assert!(!entry.loading);
assert!(entry.observation.is_none());
}
return std::result::Result::Ok(());
}
#[tokio::test]
async fn non_refresh_outcome_preserves_last_successful_observation() -> ksp_core_lib::Result<()> {
let settings = match ksp_offchain_transport_lib::MarketPriceCoinPaprikaSettings::new(false) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let service =
match ksp_offchain_transport_lib::MarketPriceService::new(std::vec![ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinPaprika(settings),]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let provider_id = match ksp_offchain_transport_lib::MarketPriceProviderId::new("coinpaprika") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let price = match ksp_offchain_transport_lib::MarketPriceDecimal::parse("101.2500") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let provenance = match ksp_offchain_transport_lib::MarketPriceProvenance::new("unit-test") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let observation = match ksp_offchain_transport_lib::MarketPriceObservation::new(
provider_id.clone(),
price,
ksp_offchain_transport_lib::MarketPriceSemantics::AggregatedMarket,
ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(10),
ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(30),
std::option::Option::Some(ksp_offchain_transport_lib::MarketPriceTimestamp::from_unix_millis(20)),
provenance,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut entries = std::collections::BTreeMap::new();
entries.insert(provider_id.as_str().to_owned(), super::MarketPricePresentationEntry { loading: true, observation: std::option::Option::Some(observation) });
let mut presentation = super::MarketPricePresentationState { entries };
let outcome = match service.refresh(&provider_id).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert!(!outcome.refreshed());
let applied = super::apply_outcome_to_state(&mut presentation, &outcome);
if let std::result::Result::Err(error) = applied {
return std::result::Result::Err(error);
}
let entry = presentation.entries.get(provider_id.as_str());
assert!(entry.is_some());
if let std::option::Option::Some(entry) = entry {
assert!(!entry.loading);
assert_eq!(entry.observation.as_ref().map(|value| return value.price().to_canonical_string()).as_deref(), std::option::Option::Some("101.25"));
}
return std::result::Result::Ok(());
}