// file: crates/ksp-onchain-transport-lib/tests/public_api.rs // version: 6 //! Integration tests for the public `ksp-onchain-transport-lib` consumer contract. #[test] fn public_settings_contract_is_constructible_without_config_dependency() { let url = ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://api.devnet.solana.com").expect("public URL parser must accept Devnet endpoint"); let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new( ksp_onchain_transport_lib::HttpRoleName::new("default"), true, std::vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()], 100, ksp_onchain_transport_lib::HttpRoleLimits::new( std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None, ), ); let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new( "solana_devnet_public", true, ksp_onchain_transport_lib::HttpProviderName::new("solana-public"), ksp_onchain_transport_lib::HttpClusterName::new("devnet"), url, std::time::Duration::from_secs(5), std::time::Duration::from_secs(15), std::option::Option::Some(8), std::vec![role], ); let settings = ksp_onchain_transport_lib::HttpTransportSettings::new( std::vec![endpoint], ksp_onchain_transport_lib::HttpRetrySettings::new(2, std::time::Duration::from_millis(100), std::time::Duration::from_secs(2)), ); assert!(settings.validate().is_ok()); } #[test] fn public_json_rpc_contract_round_trips_foundation_shape() { let request = ksp_onchain_transport_lib::JsonRpcRequest::new(1, "getHealth", std::vec![]).expect("public request constructor must succeed"); let encoded = request.to_json_string().expect("public request must serialize"); assert!(encoded.contains("\"jsonrpc\":\"2.0\"")); let response = ksp_onchain_transport_lib::parse_json_rpc_response_text(r#"{"jsonrpc":"2.0","result":"ok","id":1}"#, 1).expect("public parser must validate response"); let result = response.into_result().expect("success response must return result"); assert_eq!(result, serde_json::json!("ok")); } #[test] fn public_method_registry_exposes_current_and_historical_surfaces() { assert_eq!(ksp_onchain_transport_lib::current_http_rpc_methods().len(), 52); assert_eq!(ksp_onchain_transport_lib::historical_http_rpc_methods().len(), 14); let removed = ksp_onchain_transport_lib::find_http_rpc_method("confirmTransaction").expect("historical method must be discoverable"); assert_eq!(removed.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Removed); assert_eq!(removed.ensure_runtime_supported().expect_err("removed method must fail").code(), ksp_onchain_transport_lib::ERROR_CODE_METHOD_REMOVED); } #[test] fn public_error_codes_share_the_core_error_domain() { assert_eq!(ksp_onchain_transport_lib::ERROR_CODE_INVALID_SETTINGS.domain(), "onchain_transport"); assert_eq!(ksp_onchain_transport_lib::ERROR_CODE_RPC_APPLICATION_ERROR.domain(), "onchain_transport"); } #[test] fn public_pool_contract_selects_a_standard_method_without_exposing_url() { let url = ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://provider.invalid/rpc?token=SECRET-CANARY") .expect("public URL parser must accept HTTPS endpoint"); let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new( ksp_onchain_transport_lib::HttpRoleName::new("default"), true, std::vec![ksp_onchain_transport_lib::HttpRequestKind::new("get_balance")], 10, ksp_onchain_transport_lib::HttpRoleLimits::new( std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None, ), ); let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new( "primary", true, ksp_onchain_transport_lib::HttpProviderName::new("provider"), ksp_onchain_transport_lib::HttpClusterName::new("devnet"), url, std::time::Duration::from_secs(2), std::time::Duration::from_secs(10), std::option::Option::Some(8), std::vec![role], ); let settings = ksp_onchain_transport_lib::HttpTransportSettings::new( std::vec![endpoint], ksp_onchain_transport_lib::HttpRetrySettings::new(1, std::time::Duration::from_millis(10), std::time::Duration::from_millis(50)), ); let pool = ksp_onchain_transport_lib::HttpTransportPool::new(settings).expect("public pool constructor must succeed"); let method = ksp_onchain_transport_lib::find_http_rpc_method("getBalance").expect("audited method must exist"); let selected = pool.select_for_method(&ksp_onchain_transport_lib::HttpRoleName::new("default"), method).expect("public pool must route audited method"); assert_eq!(selected.endpoint_name(), "primary"); let rendered = format!("{pool:?}"); assert!(!rendered.contains("SECRET-CANARY")); assert!(!rendered.contains("provider.invalid")); } #[test] fn public_retry_policy_preserves_no_resend_after_ambiguous_write_dispatch() { let method = ksp_onchain_transport_lib::find_http_rpc_method("sendTransaction").expect("sendTransaction must be audited"); let settings = ksp_onchain_transport_lib::HttpRetrySettings::new(2, std::time::Duration::from_millis(100), std::time::Duration::from_secs(1)); let decision = ksp_onchain_transport_lib::evaluate_transport_retry( method, &settings, ksp_onchain_transport_lib::HttpRetryCause::Timeout, ksp_onchain_transport_lib::HttpDispatchState::DispatchedAmbiguous, 0, std::option::Option::None, ); assert_eq!(decision, ksp_onchain_transport_lib::HttpRetryDecision::Stop); } #[tokio::test] async fn public_async_admission_exposes_bounded_permit_without_endpoint_url() { let url = ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://provider.invalid/rpc?token=ASYNC-SECRET-CANARY") .expect("public URL parser must accept HTTPS endpoint"); let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new( ksp_onchain_transport_lib::HttpRoleName::new("default"), true, std::vec![ksp_onchain_transport_lib::HttpRequestKind::new("get_balance")], 10, ksp_onchain_transport_lib::HttpRoleLimits::new( std::option::Option::None, std::option::Option::None, std::num::NonZeroU32::new(1), std::option::Option::None, ), ); let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new( "primary", true, ksp_onchain_transport_lib::HttpProviderName::new("provider"), ksp_onchain_transport_lib::HttpClusterName::new("devnet"), url, std::time::Duration::from_secs(2), std::time::Duration::from_secs(10), std::option::Option::Some(8), std::vec![role], ); let settings = ksp_onchain_transport_lib::HttpTransportSettings::new( std::vec![endpoint], ksp_onchain_transport_lib::HttpRetrySettings::new(1, std::time::Duration::from_millis(10), std::time::Duration::from_millis(50)), ); let pool = ksp_onchain_transport_lib::HttpTransportPool::new(settings).expect("public pool constructor must succeed"); let method = ksp_onchain_transport_lib::find_http_rpc_method("getBalance").expect("audited method must exist"); let permit = pool .acquire_for_method(&ksp_onchain_transport_lib::HttpRoleName::new("default"), method) .await .expect("public async admission must acquire capacity"); assert_eq!(permit.selection().endpoint_name(), "primary"); assert!(permit.remaining_timeout() > std::time::Duration::ZERO); let rendered = format!("{permit:?}"); assert!(!rendered.contains("ASYNC-SECRET-CANARY")); assert!(!rendered.contains("provider.invalid")); } #[test] fn public_typed_canary_contracts_are_available_from_crate_root() { let config = ksp_onchain_transport_lib::GetBalanceConfig::new( std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed), std::option::Option::Some(42), ); assert_eq!(config.commitment(), std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)); assert_eq!(config.min_context_slot(), std::option::Option::Some(42)); assert_eq!(ksp_onchain_transport_lib::SolanaNodeHealth::Healthy, ksp_onchain_transport_lib::SolanaNodeHealth::Healthy); } #[test] fn public_pre_002_shared_rpc_types_are_constructible_from_crate_root() { let context = ksp_onchain_transport_lib::SolanaContextConfig::new( std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized), std::option::Option::Some(123), ); assert_eq!(context.commitment(), std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized)); assert_eq!(context.min_context_slot(), std::option::Option::Some(123)); let account = ksp_onchain_transport_lib::SolanaAccountInfoConfig::new( std::option::Option::Some(ksp_onchain_transport_lib::SolanaAccountEncoding::JsonParsed), std::option::Option::None, std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed), std::option::Option::None, ); assert_eq!(account.encoding(), std::option::Option::Some(ksp_onchain_transport_lib::SolanaAccountEncoding::JsonParsed)); let pubkey = "11111111111111111111111111111111".parse::().expect("fixture pubkey must parse"); let selector = ksp_onchain_transport_lib::SolanaTokenAccountSelector::Mint(pubkey); assert!(matches!(selector, ksp_onchain_transport_lib::SolanaTokenAccountSelector::Mint(_))); let vote = ksp_onchain_transport_lib::SolanaVoteAccountsConfig::new( std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized), std::option::Option::Some(pubkey), std::option::Option::Some(true), std::option::Option::Some(128), ); assert_eq!(vote.vote_pubkey(), std::option::Option::Some(&pubkey)); }