// file: crates/ksp-config-lib/unit_tests/offchain_transport.rs // version: 1 const TEST_PAIR: &str = "Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE"; #[test] fn committed_public_keyless_profile_maps_all_eight_providers_without_secret_environment() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new()); let resolved = engine.load_resolved_offchain_transport_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "committed public_keyless Off-chain Transport profile should map: {resolved:?}"); if let std::result::Result::Ok(resolved) = resolved { assert_eq!(resolved.file_id().as_str(), crate::FILE_ID_STD_OFFCHAIN_TRANSPORT); assert_eq!(resolved.profile_id(), "public_keyless"); assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::DefaultProfile); let registry = resolved.service().registry(); assert_eq!(registry.len(), 8); let ids: std::vec::Vec<&str> = registry.entries().iter().map(|entry| return entry.descriptor().id().as_str()).collect(); assert_eq!(ids, ["birdeye", "coinbase_exchange", "coingecko", "coinmarketcap", "coinpaprika", "dexscreener", "jupiter", "kraken"]); assert_provider_availability(®istry, "birdeye", ksp_offchain_transport_lib::MarketPriceProviderAvailability::Disabled); assert_provider_availability(®istry, "dexscreener", ksp_offchain_transport_lib::MarketPriceProviderAvailability::Disabled); for provider_id in ["coinbase_exchange", "coingecko", "coinmarketcap", "coinpaprika", "jupiter", "kraken"] { assert_provider_availability(®istry, provider_id, ksp_offchain_transport_lib::MarketPriceProviderAvailability::Ready); } let debug = format!("{resolved:?}"); assert!(!debug.contains("api_key"), "safe Debug should not expose credential field contents from the selected keyless profile"); } } #[test] fn committed_all_free_profile_requires_config_owned_secrets_and_public_pair_provenance() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let canaries = [ ("KSP_SECRET_BIRDEYE_API_KEY", "birdeye-secret-canary"), ("KSP_SECRET_COINGECKO_DEMO_API_KEY", "coingecko-secret-canary"), ("KSP_SECRET_COINMARKETCAP_API_KEY", "coinmarketcap-secret-canary"), ("KSP_SECRET_JUPITER_API_KEY", "jupiter-secret-canary"), ("KSP_PUBLIC_DEXSCREENER_SOL_USD_PAIR_ADDRESS", TEST_PAIR), ]; let mut process = std::collections::BTreeMap::::new(); for (name, value) in canaries { process.insert(name.to_owned(), value.to_owned()); } let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new()); let resolved = engine.load_resolved_offchain_transport_config(std::option::Option::Some("all_free"), &environment); assert!(resolved.is_ok(), "committed all_free Off-chain Transport profile should map from Config-owned environment: {resolved:?}"); if let std::result::Result::Ok(resolved) = resolved { assert_eq!(resolved.profile_id(), "all_free"); assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::Explicit); let registry = resolved.service().registry(); assert_eq!(registry.len(), 8); for entry in registry.entries() { assert_eq!(entry.state().availability(), ksp_offchain_transport_lib::MarketPriceProviderAvailability::Ready); } assert!(resolved.effective().sensitivity().is_secret()); let safe = resolved.effective().safe_value().to_string(); for secret in ["birdeye-secret-canary", "coingecko-secret-canary", "coinmarketcap-secret-canary", "jupiter-secret-canary"] { assert!(!safe.contains(secret), "safe effective Config must redact provider credential canary"); } assert!(safe.contains(TEST_PAIR), "public DexScreener pair should remain visible in the safe effective Config"); let debug = format!("{resolved:?}"); for secret in ["birdeye-secret-canary", "coingecko-secret-canary", "coinmarketcap-secret-canary", "jupiter-secret-canary"] { assert!(!debug.contains(secret), "ResolvedOffchainTransportConfig Debug must redact provider credential canary"); } } } #[test] fn literal_or_nonsecret_provider_credentials_are_rejected_by_effective_adapter() { let fixture = tempfile::tempdir(); assert!(fixture.is_ok(), "temporary Config root should be creatable: {fixture:?}"); let fixture = match fixture { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let source = committed_document_value(); let mut source = match source { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let profiles = source.get_mut("profiles").and_then(serde_json::Value::as_array_mut); assert!(profiles.is_some(), "fixture should expose profiles"); if let std::option::Option::Some(profiles) = profiles { let all_free = profiles .iter_mut() .find(|profile| return profile.get("profile_id").and_then(serde_json::Value::as_str) == std::option::Option::Some("all_free")); assert!(all_free.is_some(), "fixture should contain all_free profile"); if let std::option::Option::Some(all_free) = all_free { all_free["market_price"]["birdeye"]["api_key"] = serde_json::Value::String("literal-secret".to_owned()); } } let engine = fixture_engine_with_document(fixture.path(), &source); assert!(engine.is_ok(), "literal-secret fixture engine should be constructible: {engine:?}"); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let mut process = all_free_environment(); process.insert("KSP_SECRET_BIRDEYE_API_KEY".to_owned(), "unused-secret".to_owned()); let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new()); let resolved = engine.load_resolved_offchain_transport_config(std::option::Option::Some("all_free"), &environment); assert!(resolved.is_err(), "literal provider credential must be rejected even though the JSON Schema accepts a non-empty string"); if let std::result::Result::Err(error) = resolved { assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID); assert!(!format!("{error:?}").contains("literal-secret")); } } #[test] fn dexscreener_pair_environment_must_use_public_namespace_and_disabled_pair_may_be_absent() { let fixture = tempfile::tempdir(); assert!(fixture.is_ok(), "temporary Config root should be creatable: {fixture:?}"); let fixture = match fixture { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let source = committed_document_value(); let mut source = match source { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let profiles = source.get_mut("profiles").and_then(serde_json::Value::as_array_mut); if let std::option::Option::Some(profiles) = profiles { let all_free = profiles .iter_mut() .find(|profile| return profile.get("profile_id").and_then(serde_json::Value::as_str) == std::option::Option::Some("all_free")); if let std::option::Option::Some(all_free) = all_free { all_free["market_price"]["dexscreener"]["sol_usd_pair_address"] = serde_json::Value::String("${KSP_SECRET_DEXSCREENER_PAIR}".to_owned()); } } let engine = fixture_engine_with_document(fixture.path(), &source); assert!(engine.is_ok(), "secret-pair fixture engine should be constructible: {engine:?}"); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let mut process = all_free_environment(); process.remove("KSP_PUBLIC_DEXSCREENER_SOL_USD_PAIR_ADDRESS"); process.insert("KSP_SECRET_DEXSCREENER_PAIR".to_owned(), TEST_PAIR.to_owned()); let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new()); let resolved = engine.load_resolved_offchain_transport_config(std::option::Option::Some("all_free"), &environment); assert!(resolved.is_err(), "DexScreener pair environment must not use secret provenance"); if let std::result::Result::Err(error) = resolved { assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID); } let disabled = ksp_offchain_transport_lib::MarketPriceDexScreenerSettings::new(false, std::option::Option::None); assert!(disabled.is_ok(), "disabled DexScreener runtime settings should accept an absent pair after pre.009 capability reconciliation: {disabled:?}"); } fn assert_provider_availability( registry: &ksp_offchain_transport_lib::MarketPriceProviderRegistry, provider_id: &str, expected: ksp_offchain_transport_lib::MarketPriceProviderAvailability, ) { let provider_id = ksp_offchain_transport_lib::MarketPriceProviderId::new(provider_id); assert!(provider_id.is_ok(), "provider id fixture should be valid: {provider_id:?}"); if let std::result::Result::Ok(provider_id) = provider_id { let state = registry.state(&provider_id); assert!(state.is_some(), "provider should be present in Config-produced service registry"); if let std::option::Option::Some(state) = state { assert_eq!(state.availability(), expected); } } } fn committed_engine() -> ksp_core_lib::Result { let workspace = workspace_root(); let bootstrap = crate::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas")); let bootstrap = match bootstrap { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let registry = crate::ConfigFileRegistry::defaults(); let registry = match registry { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return std::result::Result::Ok(crate::ConfigDocumentEngine::new(bootstrap, registry)); } fn fixture_engine_with_document(root: &std::path::Path, document: &serde_json::Value) -> ksp_core_lib::Result { let config_root = root.join("config"); let create = std::fs::create_dir_all(config_root.as_path()); if let std::result::Result::Err(error) = create { return std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_JSON_FILE_READ_FAILED, "test Config root cannot be created").with_source(error), ); } let bytes = serde_json::to_vec_pretty(document); let bytes = match bytes { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_JSON_SYNTAX_INVALID, "test Config cannot be encoded").with_source(error), ); }, }; let path = config_root.join(crate::DEFAULT_STD_OFFCHAIN_TRANSPORT_FILENAME); if let std::result::Result::Err(error) = std::fs::write(path.as_path(), bytes) { return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_JSON_FILE_READ_FAILED, "test Config cannot be written").with_source(error)); } let bootstrap = crate::ConfigBootstrapOptions::from_paths(config_root, workspace_root().join("config/schemas")); let bootstrap = match bootstrap { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let registry = crate::ConfigFileRegistry::defaults(); let registry = match registry { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return std::result::Result::Ok(crate::ConfigDocumentEngine::new(bootstrap, registry)); } fn committed_document_value() -> std::result::Result { return serde_json::from_str(include_str!("../../../config/std.offchain_transport.json")); } fn all_free_environment() -> std::collections::BTreeMap { let mut process = std::collections::BTreeMap::::new(); process.insert("KSP_SECRET_BIRDEYE_API_KEY".to_owned(), "birdeye-test".to_owned()); process.insert("KSP_SECRET_COINGECKO_DEMO_API_KEY".to_owned(), "coingecko-test".to_owned()); process.insert("KSP_SECRET_COINMARKETCAP_API_KEY".to_owned(), "coinmarketcap-test".to_owned()); process.insert("KSP_SECRET_JUPITER_API_KEY".to_owned(), "jupiter-test".to_owned()); process.insert("KSP_PUBLIC_DEXSCREENER_SOL_USD_PAIR_ADDRESS".to_owned(), TEST_PAIR.to_owned()); return process; } fn workspace_root() -> std::path::PathBuf { return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); }