// file: crates/ksp-onchain-transport-lib/tests/release_completeness.rs // version: 43 //! Release-level completeness canaries for staged HTTP and WebSocket Transport coverage. #[test] fn release_registry_partition_matches_the_audited_http_plan() { let mut foundation = 0_usize; let mut accounts_tokens_cluster = 0_usize; let mut transactions = 0_usize; let mut blocks_economics = 0_usize; let mut historical_in_current = 0_usize; for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { match descriptor.coverage_release() { ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_1 => foundation += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 => accounts_tokens_cluster += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 => transactions += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 => blocks_economics += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::Historical => historical_in_current += 1, } } assert_eq!(ksp_onchain_transport_lib::current_http_rpc_methods().len(), 52); assert_eq!(ksp_onchain_transport_lib::historical_http_rpc_methods().len(), 14); assert_eq!(foundation, 4); assert_eq!(accounts_tokens_cluster, 22); assert_eq!(transactions, 11); assert_eq!(blocks_economics, 15); assert_eq!(historical_in_current, 0); } #[test] fn release_foundation_canaries_and_historical_statuses_are_exact() { let mut foundation_names = std::vec::Vec::<&str>::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_1 { foundation_names.push(descriptor.method()); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Supported); } } foundation_names.sort_unstable(); assert_eq!(foundation_names, std::vec!["getBalance", "getGenesisHash", "getHealth", "getVersion"]); for descriptor in ksp_onchain_transport_lib::historical_http_rpc_methods() { assert_eq!(descriptor.documentation_status(), ksp_onchain_transport_lib::RpcDocumentationStatus::Deprecated); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Removed); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::Historical); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NotApplicable); } } #[test] fn release_pre_003_accounts_subset_is_exact_without_advancing_other_v0_2_2_families() { let mut account_names = std::vec::Vec::<&str>::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 && descriptor.category() == ksp_onchain_transport_lib::HttpRpcCategory::Accounts { account_names.push(descriptor.method()); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } } account_names.sort_unstable(); assert_eq!( account_names, std::vec!["getAccountInfo", "getLargestAccounts", "getMinimumBalanceForRentExemption", "getMultipleAccounts", "getProgramAccounts",], ); } #[test] fn release_pre_004_tokens_subset_is_exact_and_retry_safe() { let mut token_names = std::vec::Vec::<&str>::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 && descriptor.category() == ksp_onchain_transport_lib::HttpRpcCategory::Tokens { token_names.push(descriptor.method()); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } } token_names.sort_unstable(); assert_eq!( token_names, std::vec!["getTokenAccountBalance", "getTokenAccountsByDelegate", "getTokenAccountsByOwner", "getTokenLargestAccounts", "getTokenSupply",], ); } #[test] fn release_pre_005_simple_cluster_subset_is_exact_and_retry_safe() { let expected = std::vec![ "getClusterNodes", "getEpochInfo", "getEpochSchedule", "getHighestSnapshotSlot", "getIdentity", "getMaxRetransmitSlot", "getMaxShredInsertSlot", ]; let deferred = std::vec!["getLeaderSchedule", "getSlot", "getSlotLeader", "getSlotLeaders", "getVoteAccounts"]; for method_name in &expected { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.005 cluster descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Cluster); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } for method_name in &deferred { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.006 cluster descriptor must remain registered"); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2); } assert_eq!(expected.len(), 7); assert_eq!(deferred.len(), 5); } #[test] fn release_pre_006_completes_cluster_subset_exactly_and_retry_safe() { let mut expected = std::vec![ "getClusterNodes", "getEpochInfo", "getEpochSchedule", "getHighestSnapshotSlot", "getIdentity", "getLeaderSchedule", "getMaxRetransmitSlot", "getMaxShredInsertSlot", "getSlot", "getSlotLeader", "getSlotLeaders", "getVoteAccounts", ]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 && descriptor.category() == ksp_onchain_transport_lib::HttpRpcCategory::Cluster { actual.push(descriptor.method()); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 12); } #[test] fn release_pre_007_v0_2_2_exact_set_is_complete_without_advancing_future_releases() { let mut expected = std::vec![ "getAccountInfo", "getClusterNodes", "getEpochInfo", "getEpochSchedule", "getHighestSnapshotSlot", "getIdentity", "getLargestAccounts", "getLeaderSchedule", "getMaxRetransmitSlot", "getMaxShredInsertSlot", "getMinimumBalanceForRentExemption", "getMultipleAccounts", "getProgramAccounts", "getSlot", "getSlotLeader", "getSlotLeaders", "getTokenAccountBalance", "getTokenAccountsByDelegate", "getTokenAccountsByOwner", "getTokenLargestAccounts", "getTokenSupply", "getVoteAccounts", ]; let mut actual = std::vec::Vec::new(); let mut future_transactions = 0_usize; let mut future_blocks_economics = 0_usize; for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { match descriptor.coverage_release() { ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 => { actual.push(descriptor.method()); assert_eq!(descriptor.documentation_status(), ksp_onchain_transport_lib::RpcDocumentationStatus::Stable); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Supported); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); }, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 => future_transactions += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 => future_blocks_economics += 1, _ => {}, } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 22); assert_eq!(future_transactions, 11); assert_eq!(future_blocks_economics, 15); } #[test] fn release_v0_2_3_descriptor_set_and_safety_classes_are_exact() { let mut expected = std::vec![ "getFeeForMessage", "getLatestBlockhash", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getTransaction", "getTransactionCount", "isBlockhashValid", "requestAirdrop", "sendTransaction", "simulateTransaction", ]; let mut actual = std::vec::Vec::new(); let mut reads = 0_usize; let mut writes = 0_usize; let mut simulations = 0_usize; for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() != ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 { continue; } actual.push(descriptor.method()); match descriptor.operation_kind() { ksp_onchain_transport_lib::RpcOperationKind::Read => { reads += 1; assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); }, ksp_onchain_transport_lib::RpcOperationKind::WriteSubmission => { writes += 1; assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NeverAfterDispatch); }, ksp_onchain_transport_lib::RpcOperationKind::Simulation => { simulations += 1; assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); }, } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(reads, 8); assert_eq!(writes, 2); assert_eq!(simulations, 1); } #[test] fn release_pre_003_transaction_read_subset_is_exact_and_retry_safe() { let mut expected = std::vec!["getFeeForMessage", "getLatestBlockhash", "getTransactionCount", "isBlockhashValid"]; let mut actual = std::vec::Vec::new(); for method_name in &expected { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.003 Transaction descriptor must exist"); actual.push(descriptor.method()); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 4); } #[test] fn release_pre_004_transaction_read_subset_is_exact_and_retry_safe() { let mut expected = std::vec![ "getFeeForMessage", "getLatestBlockhash", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getTransactionCount", "isBlockhashValid", ]; let deferred = std::vec!["getTransaction", "requestAirdrop", "sendTransaction", "simulateTransaction"]; let mut actual = std::vec::Vec::new(); for method_name in &expected { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.004 Transaction descriptor must exist"); actual.push(descriptor.method()); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } for method_name in &deferred { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("deferred Transaction descriptor must remain registered"); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 7); assert_eq!(deferred.len(), 4); } #[test] fn release_pre_005_transaction_read_subset_adds_complete_get_transaction_without_advancing_writes_or_simulation() { let mut expected = std::vec![ "getFeeForMessage", "getLatestBlockhash", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getTransaction", "getTransactionCount", "isBlockhashValid", ]; let deferred = std::vec!["requestAirdrop", "sendTransaction", "simulateTransaction"]; let mut actual = std::vec::Vec::new(); for method_name in &expected { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.005 Transaction descriptor must exist"); actual.push(descriptor.method()); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } let get_transaction = ksp_onchain_transport_lib::find_http_rpc_method("getTransaction").expect("getTransaction descriptor must exist"); assert!(get_transaction.request_form_status().has_deprecated_legacy()); for method_name in &deferred { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("deferred Transaction descriptor must remain registered"); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 8); assert_eq!(deferred.len(), 3); } #[test] fn release_pre_006_transaction_subset_adds_both_write_submissions_without_advancing_simulation() { let reads = std::vec![ "getFeeForMessage", "getLatestBlockhash", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getTransaction", "getTransactionCount", "isBlockhashValid", ]; let writes = std::vec!["requestAirdrop", "sendTransaction"]; for method_name in &reads { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.006 read descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } for method_name in &writes { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.006 write descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::WriteSubmission); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NeverAfterDispatch); } let simulation = ksp_onchain_transport_lib::find_http_rpc_method("simulateTransaction").expect("simulation descriptor must remain registered"); assert_eq!(simulation.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Simulation); assert_eq!(reads.len(), 8); assert_eq!(writes.len(), 2); } #[test] fn release_pre_007_completes_all_v0_2_3_transaction_wrappers_with_exact_safety_classes() { let reads = std::vec![ "getFeeForMessage", "getLatestBlockhash", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getTransaction", "getTransactionCount", "isBlockhashValid", ]; let writes = std::vec!["requestAirdrop", "sendTransaction"]; let simulations = std::vec!["simulateTransaction"]; for method_name in &reads { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.007 read descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } for method_name in &writes { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.007 write descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::WriteSubmission); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NeverAfterDispatch); } for method_name in &simulations { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name).expect("pre.007 simulation descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Transactions); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Simulation); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } assert_eq!(reads.len(), 8); assert_eq!(writes.len(), 2); assert_eq!(simulations.len(), 1); assert_eq!(reads.len() + writes.len() + simulations.len(), 11); } #[test] fn release_pre_008_ksp_transport_007_retro_audit_covers_all_typed_current_methods() { let mut audited = std::vec::Vec::<&str>::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if matches!( descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_1 | ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 | ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 ) { audited.push(descriptor.method()); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Supported); match descriptor.operation_kind() { ksp_onchain_transport_lib::RpcOperationKind::Read | ksp_onchain_transport_lib::RpcOperationKind::Simulation => { assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); }, ksp_onchain_transport_lib::RpcOperationKind::WriteSubmission => { assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NeverAfterDispatch); }, } } } audited.sort_unstable(); assert_eq!(audited.len(), 37); assert_eq!(audited.first(), std::option::Option::Some(&"getAccountInfo")); assert_eq!(audited.last(), std::option::Option::Some(&"simulateTransaction")); let get_transaction = ksp_onchain_transport_lib::find_http_rpc_method("getTransaction").expect("getTransaction descriptor must exist"); assert!(get_transaction.request_form_status().has_deprecated_legacy()); } #[test] fn release_v0_2_4_descriptor_set_is_exact_and_remains_read_retry_safe_through_wrapper_completion() { let mut expected_blocks = std::vec![ "getBlock", "getBlockCommitment", "getBlockHeight", "getBlockProduction", "getBlocks", "getBlocksWithLimit", "getBlockTime", "getFirstAvailableBlock", "getRecentPerformanceSamples", "minimumLedgerSlot", ]; let mut expected_economics = std::vec!["getInflationGovernor", "getInflationRate", "getInflationReward", "getStakeMinimumDelegation", "getSupply",]; let mut actual_blocks = std::vec::Vec::new(); let mut actual_economics = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() != ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 { continue; } assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); match descriptor.category() { ksp_onchain_transport_lib::HttpRpcCategory::Blocks => actual_blocks.push(descriptor.method()), ksp_onchain_transport_lib::HttpRpcCategory::Economics => actual_economics.push(descriptor.method()), _ => {}, } } actual_blocks.sort_unstable(); actual_economics.sort_unstable(); expected_blocks.sort_unstable(); expected_economics.sort_unstable(); assert_eq!(actual_blocks, expected_blocks); assert_eq!(actual_economics, expected_economics); assert_eq!(actual_blocks.len(), 10); assert_eq!(actual_economics.len(), 5); let get_block = ksp_onchain_transport_lib::find_http_rpc_method("getBlock").expect("getBlock descriptor must exist"); assert!(get_block.request_form_status().has_deprecated_legacy()); } #[test] fn release_v0_2_4_pre_003_simple_blocks_subset_is_exact_and_retry_safe() { let mut expected = std::vec!["getBlockCommitment", "getBlockHeight", "getBlockTime", "getFirstAvailableBlock", "minimumLedgerSlot"]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() != ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 || descriptor.category() != ksp_onchain_transport_lib::HttpRpcCategory::Blocks { continue; } match descriptor.method() { "getBlockCommitment" | "getBlockHeight" | "getBlockTime" | "getFirstAvailableBlock" | "minimumLedgerSlot" => { assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); actual.push(descriptor.method()); }, _ => {}, } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 5); } #[test] fn release_v0_2_4_pre_004_range_performance_subset_is_exact_and_retry_safe() { let mut expected = std::vec!["getBlocks", "getBlocksWithLimit", "getRecentPerformanceSamples"]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() != ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 || descriptor.category() != ksp_onchain_transport_lib::HttpRpcCategory::Blocks { continue; } match descriptor.method() { "getBlocks" | "getBlocksWithLimit" | "getRecentPerformanceSamples" => { assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); actual.push(descriptor.method()); }, _ => {}, } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 3); } #[test] fn release_v0_2_4_pre_005_block_production_subset_is_exact_and_retry_safe() { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method("getBlockProduction").expect("getBlockProduction descriptor must exist"); assert_eq!(descriptor.category(), ksp_onchain_transport_lib::HttpRpcCategory::Blocks); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } #[test] fn release_v0_2_4_pre_006_get_block_completes_all_ten_block_wrappers_with_legacy_marker() { let expected = std::vec![ "getBlock", "getBlockCommitment", "getBlockHeight", "getBlockProduction", "getBlocks", "getBlocksWithLimit", "getBlockTime", "getFirstAvailableBlock", "getRecentPerformanceSamples", "minimumLedgerSlot", ]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 && descriptor.category() == ksp_onchain_transport_lib::HttpRpcCategory::Blocks { assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); actual.push(descriptor.method()); } } actual.sort_unstable(); let mut expected = expected; expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 10); let get_block = ksp_onchain_transport_lib::find_http_rpc_method("getBlock").expect("getBlock descriptor must exist"); assert!(get_block.request_form_status().has_deprecated_legacy()); } #[test] fn release_v0_2_4_pre_007_simple_economics_subset_is_exact_and_retry_safe() { let mut expected = std::vec!["getInflationGovernor", "getInflationRate", "getStakeMinimumDelegation", "getSupply"]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() != ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 || descriptor.category() != ksp_onchain_transport_lib::HttpRpcCategory::Economics { continue; } assert!(matches!( descriptor.method(), "getInflationGovernor" | "getInflationRate" | "getInflationReward" | "getStakeMinimumDelegation" | "getSupply" )); if descriptor.method() != "getInflationReward" { assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); actual.push(descriptor.method()); } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 4); } #[test] fn release_v0_2_4_pre_008_completes_all_five_economics_wrappers_exactly_and_retry_safe() { let mut expected = std::vec!["getInflationGovernor", "getInflationRate", "getInflationReward", "getStakeMinimumDelegation", "getSupply"]; let mut actual = std::vec::Vec::new(); for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() { if descriptor.coverage_release() == ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 && descriptor.category() == ksp_onchain_transport_lib::HttpRpcCategory::Economics { actual.push(descriptor.method()); assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); } } actual.sort_unstable(); expected.sort_unstable(); assert_eq!(actual, expected); assert_eq!(actual.len(), 5); } #[test] fn release_v0_2_4_pre_009_final_http_inventory_and_coverage_partition_are_exact() { let expected_current = std::vec![ "getAccountInfo", "getBalance", "getBlock", "getBlockCommitment", "getBlockHeight", "getBlockProduction", "getBlocks", "getBlocksWithLimit", "getBlockTime", "getClusterNodes", "getEpochInfo", "getEpochSchedule", "getFeeForMessage", "getFirstAvailableBlock", "getGenesisHash", "getHealth", "getHighestSnapshotSlot", "getIdentity", "getInflationGovernor", "getInflationRate", "getInflationReward", "getLargestAccounts", "getLatestBlockhash", "getLeaderSchedule", "getMaxRetransmitSlot", "getMaxShredInsertSlot", "getMinimumBalanceForRentExemption", "getMultipleAccounts", "getProgramAccounts", "getRecentPerformanceSamples", "getRecentPrioritizationFees", "getSignaturesForAddress", "getSignatureStatuses", "getSlot", "getSlotLeader", "getSlotLeaders", "getStakeMinimumDelegation", "getSupply", "getTokenAccountBalance", "getTokenAccountsByDelegate", "getTokenAccountsByOwner", "getTokenLargestAccounts", "getTokenSupply", "getTransaction", "getTransactionCount", "getVersion", "getVoteAccounts", "isBlockhashValid", "minimumLedgerSlot", "requestAirdrop", "sendTransaction", "simulateTransaction", ]; let expected_historical = std::vec![ "confirmTransaction", "getConfirmedBlock", "getConfirmedBlocks", "getConfirmedBlocksWithLimit", "getConfirmedSignaturesForAddress2", "getConfirmedTransaction", "getFeeCalculatorForBlockhash", "getFeeRateGovernor", "getFees", "getRecentBlockhash", "getSignatureConfirmation", "getSignatureStatus", "getSnapshotSlot", "getStakeActivation", ]; let current = ksp_onchain_transport_lib::current_http_rpc_methods(); let historical = ksp_onchain_transport_lib::historical_http_rpc_methods(); let mut actual_current = std::vec::Vec::with_capacity(current.len()); let mut actual_historical = std::vec::Vec::with_capacity(historical.len()); let mut v0_2_1 = 0_usize; let mut v0_2_2 = 0_usize; let mut v0_2_3 = 0_usize; let mut v0_2_4 = 0_usize; let mut historical_current = 0_usize; for descriptor in current { actual_current.push(descriptor.method()); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Supported); assert_ne!(descriptor.documentation_status(), ksp_onchain_transport_lib::RpcDocumentationStatus::Deprecated); match descriptor.coverage_release() { ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_1 => v0_2_1 += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 => v0_2_2 += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 => v0_2_3 += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 => v0_2_4 += 1, ksp_onchain_transport_lib::HttpRpcCoverageRelease::Historical => historical_current += 1, } match descriptor.operation_kind() { ksp_onchain_transport_lib::RpcOperationKind::Read | ksp_onchain_transport_lib::RpcOperationKind::Simulation => { assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe); }, ksp_onchain_transport_lib::RpcOperationKind::WriteSubmission => { assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NeverAfterDispatch); }, } } for descriptor in historical { actual_historical.push(descriptor.method()); assert_eq!(descriptor.documentation_status(), ksp_onchain_transport_lib::RpcDocumentationStatus::Deprecated); assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Removed); assert_eq!(descriptor.coverage_release(), ksp_onchain_transport_lib::HttpRpcCoverageRelease::Historical); assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::NotApplicable); } actual_current.sort_unstable(); actual_historical.sort_unstable(); let mut expected_current = expected_current; let mut expected_historical = expected_historical; expected_current.sort_unstable(); expected_historical.sort_unstable(); assert_eq!(actual_current, expected_current); assert_eq!(actual_historical, expected_historical); assert_eq!(actual_current.len(), 52); assert_eq!(actual_historical.len(), 14); assert_eq!((v0_2_1, v0_2_2, v0_2_3, v0_2_4), (4, 22, 11, 15)); assert_eq!(historical_current, 0); } #[test] fn release_v0_2_7_pre_012_websocket_surface_accounts_for_all_nine_standard_pairs() { let kinds = [ ksp_onchain_transport_lib::WsSubscriptionKind::Account, ksp_onchain_transport_lib::WsSubscriptionKind::Block, ksp_onchain_transport_lib::WsSubscriptionKind::Logs, ksp_onchain_transport_lib::WsSubscriptionKind::Program, ksp_onchain_transport_lib::WsSubscriptionKind::Root, ksp_onchain_transport_lib::WsSubscriptionKind::Signature, ksp_onchain_transport_lib::WsSubscriptionKind::Slot, ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates, ksp_onchain_transport_lib::WsSubscriptionKind::Vote, ]; let expected_names = ["account", "block", "logs", "program", "root", "signature", "slot", "slots_updates", "vote"]; let actual_names = kinds.map(ksp_onchain_transport_lib::WsSubscriptionKind::as_str); assert_eq!(actual_names, expected_names); assert_eq!(kinds.len() * 2, 18); let _account = ksp_onchain_transport_lib::WsSession::account_subscribe; let _block = ksp_onchain_transport_lib::WsSession::block_subscribe; let _logs = ksp_onchain_transport_lib::WsSession::logs_subscribe; let _program = ksp_onchain_transport_lib::WsSession::program_subscribe; let _root = ksp_onchain_transport_lib::WsSession::root_subscribe; let _signature = ksp_onchain_transport_lib::WsSession::signature_subscribe; let _slot = ksp_onchain_transport_lib::WsSession::slot_subscribe; let _slots_updates = ksp_onchain_transport_lib::WsSession::slots_updates_subscribe; let _vote = ksp_onchain_transport_lib::WsSession::vote_subscribe; let _unsubscribe = ksp_onchain_transport_lib::WsSubscription::::unsubscribe; } #[test] fn release_v0_2_7_pre_012_http_inventory_remains_52_current_plus_14_historical() { let current = ksp_onchain_transport_lib::current_http_rpc_methods(); let historical = ksp_onchain_transport_lib::historical_http_rpc_methods(); assert_eq!(current.len(), 52); assert_eq!(historical.len(), 14); assert!(current.iter().all(|descriptor| return descriptor.runtime_status() == ksp_onchain_transport_lib::RpcRuntimeStatus::Supported)); assert!(historical.iter().all(|descriptor| return descriptor.runtime_status() == ksp_onchain_transport_lib::RpcRuntimeStatus::Removed)); } #[test] fn release_v0_2_8_pre_002_protocol_facades_preserve_the_standard_partition() { assert_eq!(ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard.as_str(), "solana_standard"); assert_eq!(ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream.as_str(), "helius_laserstream"); let standard_kinds = [ ksp_onchain_transport_lib::WsSubscriptionKind::Account, ksp_onchain_transport_lib::WsSubscriptionKind::Block, ksp_onchain_transport_lib::WsSubscriptionKind::Logs, ksp_onchain_transport_lib::WsSubscriptionKind::Program, ksp_onchain_transport_lib::WsSubscriptionKind::Root, ksp_onchain_transport_lib::WsSubscriptionKind::Signature, ksp_onchain_transport_lib::WsSubscriptionKind::Slot, ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates, ksp_onchain_transport_lib::WsSubscriptionKind::Vote, ]; assert_eq!(standard_kinds.len(), 9); assert_eq!( std::any::type_name::().rsplit("::").next(), std::option::Option::Some("SolanaStandardWsSession") ); assert_eq!( std::any::type_name::().rsplit("::").next(), std::option::Option::Some("HeliusLaserStreamWsSession") ); } #[test] fn release_v0_2_8_pre_003_original_six_standard_families_remain_available_after_provider_evolution() { let _account = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::account_subscribe; let _program = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::program_subscribe; let _logs = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::logs_subscribe; let _signature = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::signature_subscribe; let _slot = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::slot_subscribe; let _root = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::root_subscribe; let source = include_str!("../src/ws_protocol_session.rs"); assert!(source.contains("unsupported_block")); assert!(source.contains("unsupported_vote")); assert!(!source.contains("pub async fn transaction_subscribe")); assert_eq!(ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream.as_str(), "helius_laserstream"); } #[test] fn release_v0_2_8_pre_005_helius_transaction_request_surface_is_typed_before_actor_integration() { let filter = ksp_onchain_transport_lib::HeliusTransactionSubscribeFilter::new( std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::Some(ksp_onchain_transport_lib::HeliusTokenAccountsFilter::All), ); let options = ksp_onchain_transport_lib::HeliusTransactionSubscribeOptions::new( std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Processed), std::option::Option::Some(ksp_onchain_transport_lib::HeliusTransactionSubscribeEncoding::Base64), std::option::Option::Some(ksp_onchain_transport_lib::SolanaTransactionDetails::Signatures), std::option::Option::Some(true), std::option::Option::None, ); let request = ksp_onchain_transport_lib::HeliusTransactionSubscribeRequest::new(filter, std::option::Option::Some(options)); assert!(request.validate().is_ok()); let facade_source = include_str!("../src/ws_protocol_session.rs"); assert!(!facade_source.contains("pub async fn transaction_subscribe")); assert_eq!(ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream.as_str(), "helius_laserstream"); } #[test] fn release_v0_2_8_pre_006_helius_transaction_lifecycle_is_actor_integrated_without_advancing_heartbeat() { let _transaction = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::transaction_subscribe; assert_eq!(ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction.as_str(), "helius_transaction"); let source = include_str!("../src/ws_helius_transactions.rs"); assert!(source.contains("transactionNotification")); assert!(source.contains("WsSubscriptionKind::HeliusTransaction")); assert!(!source.contains("tokio_tungstenite::connect_async")); let protocol_source = include_str!("../src/ws_protocol_session.rs"); assert!(protocol_source.contains("unsupported_block")); assert!(protocol_source.contains("unsupported_vote")); } #[test] fn release_v0_2_8_pre_007_helius_heartbeat_is_provider_owned_by_shared_actor_only() { let actor_source = include_str!("../src/ws_session.rs"); assert!(actor_source.contains("HELIUS_WS_HEARTBEAT_INTERVAL")); assert!(actor_source.contains("std::time::Duration::from_secs(60)")); assert!(actor_source.contains("WsProtocolKind::HeliusLaserStream")); assert!(actor_source.contains("tungstenite::Message::Ping")); assert!(actor_source.contains("send_helius_heartbeat")); let settings_source = include_str!("../src/ws_settings.rs"); assert!(!settings_source.contains("heartbeat_interval")); assert!(!settings_source.contains("heartbeat_enabled")); let protocol_source = include_str!("../src/ws_protocol_session.rs"); assert!(!protocol_source.contains("heartbeat_interval")); } #[test] fn release_v0_2_8_pre_008_adversarial_guards_preserve_provider_isolation_and_safe_diagnostics() { let protocol_source = include_str!("../src/ws_protocol_session.rs"); assert!(protocol_source.contains("unsupported_helius_transaction")); assert!(protocol_source.contains("unsupported_block")); assert!(protocol_source.contains("unsupported_vote")); let helius_source = include_str!("../src/ws_helius_transactions.rs"); assert!(helius_source.contains("impl std::fmt::Debug for HeliusFullTransactionNotification")); assert!(helius_source.contains("impl std::fmt::Debug for HeliusTransactionSignatureNotification")); assert!(helius_source.contains("impl std::fmt::Debug for HeliusTransactionNotification")); assert!(helius_source.contains("Self::Unknown(_)")); assert!(helius_source.contains("")); let actor_source = include_str!("../src/ws_session.rs"); assert!(actor_source.contains("max_message_size_bytes")); assert!(actor_source.contains("max_frame_size_bytes")); assert!(actor_source.contains("WsNotificationDispatchOutcome::QueueFull")); assert!(actor_source.contains("ERROR_CODE_WS_BACKPRESSURE_OVERFLOW")); assert!(actor_source.contains("remote_to_local.remove")); } #[test] fn release_v0_2_8_pre_009_http_and_standard_websocket_inventories_remain_exact() { 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 standard = [ ksp_onchain_transport_lib::WsSubscriptionKind::Account, ksp_onchain_transport_lib::WsSubscriptionKind::Block, ksp_onchain_transport_lib::WsSubscriptionKind::Logs, ksp_onchain_transport_lib::WsSubscriptionKind::Program, ksp_onchain_transport_lib::WsSubscriptionKind::Root, ksp_onchain_transport_lib::WsSubscriptionKind::Signature, ksp_onchain_transport_lib::WsSubscriptionKind::Slot, ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates, ksp_onchain_transport_lib::WsSubscriptionKind::Vote, ]; assert_eq!(standard.len(), 9); let lifecycle_source = include_str!("../src/ws_lifecycle.rs"); for method in [ "accountSubscribe", "accountUnsubscribe", "blockSubscribe", "blockUnsubscribe", "logsSubscribe", "logsUnsubscribe", "programSubscribe", "programUnsubscribe", "rootSubscribe", "rootUnsubscribe", "signatureSubscribe", "signatureUnsubscribe", "slotSubscribe", "slotUnsubscribe", "slotsUpdatesSubscribe", "slotsUpdatesUnsubscribe", "voteSubscribe", "voteUnsubscribe", ] { assert!(lifecycle_source.contains(method), "missing standard WebSocket method mapping: {method}"); } let _account = ksp_onchain_transport_lib::SolanaStandardWsSession::account_subscribe; let _block = ksp_onchain_transport_lib::SolanaStandardWsSession::block_subscribe; let _logs = ksp_onchain_transport_lib::SolanaStandardWsSession::logs_subscribe; let _program = ksp_onchain_transport_lib::SolanaStandardWsSession::program_subscribe; let _root = ksp_onchain_transport_lib::SolanaStandardWsSession::root_subscribe; let _signature = ksp_onchain_transport_lib::SolanaStandardWsSession::signature_subscribe; let _slot = ksp_onchain_transport_lib::SolanaStandardWsSession::slot_subscribe; let _slots_updates = ksp_onchain_transport_lib::SolanaStandardWsSession::slots_updates_subscribe; let _vote = ksp_onchain_transport_lib::SolanaStandardWsSession::vote_subscribe; } #[test] fn release_v0_2_8_pre_009_helius_surface_is_seven_standard_families_plus_transaction() { let _account = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::account_subscribe; let _program = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::program_subscribe; let _logs = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::logs_subscribe; let _signature = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::signature_subscribe; let _slot = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::slot_subscribe; let _root = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::root_subscribe; let _slots_updates = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::slots_updates_subscribe; let _transaction = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::transaction_subscribe; let protocol_source = include_str!("../src/ws_protocol_session.rs"); assert!(protocol_source.contains("unsupported_block")); assert!(protocol_source.contains("unsupported_vote")); assert!(!protocol_source.contains("unsupported_slots_updates")); let cluster_source = include_str!("../src/ws_cluster.rs"); assert!(cluster_source.contains("impl crate::HeliusLaserStreamWsSession")); assert!(cluster_source.contains("pub async fn slots_updates_subscribe")); assert_eq!(ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates.as_str(), "slots_updates"); assert_eq!(ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction.as_str(), "helius_transaction"); } #[test] fn release_v0_2_8_pre_009_config_secret_and_dependency_boundaries_remain_wired() { let manifest_directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let workspace = manifest_directory.parent().and_then(std::path::Path::parent).expect("Transport integration test must resolve the workspace root"); let transport_manifest = std::fs::read_to_string(manifest_directory.join("Cargo.toml")).expect("Transport manifest must be readable"); for forbidden in ["ksp-config-lib", "ksp-store-api", "ksp-store-lib", "ksp-program-api", "ksp-program-lib", "tracing =", "tracing."] { assert!(!transport_manifest.contains(forbidden), "forbidden direct Transport dependency detected: {forbidden}"); } let config_manifest = std::fs::read_to_string(workspace.join("crates/ksp-config-lib/Cargo.toml")).expect("Config manifest must be readable"); assert!(config_manifest.contains("ksp-onchain-transport-lib")); let config_transport = std::fs::read_to_string(workspace.join("crates/ksp-config-lib/src/transport.rs")).expect("Config Transport adapter source must be readable"); assert!(config_transport.contains("WsProtocolKind::HeliusLaserStream")); let transport_example = std::fs::read_to_string(workspace.join("config/examples/std.transport.example.json")).expect("Transport example must be readable"); assert!(transport_example.contains("\"kind\": \"helius_laserstream\"")); assert!(transport_example.contains("${KSP_SECRET_HELIUS_API_KEY")); let env_example = std::fs::read_to_string(workspace.join(".env.example")).expect(".env.example must be readable"); assert!(env_example.contains("KSP_SECRET_HELIUS_API_KEY")); } #[test] fn release_v0_2_8_pre_010_live_smoke_policy_preserves_secret_and_dependency_ownership() { let manifest_directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let workspace = manifest_directory.parent().and_then(std::path::Path::parent).expect("Transport integration test must resolve the workspace root"); let transport_manifest = std::fs::read_to_string(manifest_directory.join("Cargo.toml")).expect("Transport manifest must be readable"); assert!(!transport_manifest.contains("ksp-config-lib")); let transport_ws_smoke = std::fs::read_to_string(manifest_directory.join("tests/websocket_devnet_smoke.rs")).expect("Transport WebSocket smoke must be readable"); assert!(!transport_ws_smoke.contains("KSP_SECRET_HELIUS_API_KEY")); assert!(!transport_ws_smoke.contains("ConfigEnvironment")); assert!(!workspace.join("crates/ksp-config-lib/tests/helius_websocket_smoke.rs").exists()); let readme = std::fs::read_to_string(manifest_directory.join("README.md")).expect("Transport README must be readable"); assert!(readme.contains("Aucun smoke Helius live supplémentaire n’est committé en `0.2.8-pre.010`")); assert!(readme.contains("KSP_SECRET_HELIUS_API_KEY")); let usage = std::fs::read_to_string(manifest_directory.join("USAGE.md")).expect("Transport USAGE must be readable"); assert!(usage.contains("### Smoke Helius live")); assert!(usage.contains("surface KSP d’intégration/orchestration dédiée")); assert!(usage.contains("cargo tree -p ksp-onchain-transport-lib")); let env_example = std::fs::read_to_string(workspace.join(".env.example")).expect(".env.example must be readable"); assert!(env_example.contains("KSP_SECRET_HELIUS_API_KEY")); } #[test] fn release_v0_2_9_pre_002_materializes_minimal_yellowstone_engine_without_provider_or_ws_coupling() { let manifest_directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let transport_manifest = std::fs::read_to_string(manifest_directory.join("Cargo.toml")).expect("Transport manifest must be readable"); let settings_source = include_str!("../src/grpc_settings.rs"); let channel_source = include_str!("../src/grpc_channel.rs"); let crate_root = include_str!("../src/lib.rs"); assert!(transport_manifest.contains("tonic = { workspace = true, features = [\"channel\"")); assert!(transport_manifest.contains("yellowstone-grpc-proto.workspace = true")); assert!(!transport_manifest.contains("yellowstone-grpc-client")); assert!(!transport_manifest.contains("ksp-config-lib")); assert!(settings_source.contains("YellowstoneGrpcEndpointUrl")); assert!(settings_source.contains("YellowstoneGrpcSessionSettings")); assert!(settings_source.contains("YellowstoneGrpcTransportSettings")); assert!(!settings_source.contains("WsProtocolKind")); assert!(!settings_source.contains("KSP_SECRET_")); assert!(channel_source.contains("tonic::transport::Endpoint::from_shared")); assert!(channel_source.contains("connect_lazy")); assert!(!channel_source.contains("WsSession")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _published_wire = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_003_adds_tls_metadata_and_exactly_seven_standard_unary_methods_without_subscribe() { let manifest_directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); let transport_manifest = std::fs::read_to_string(manifest_directory.join("Cargo.toml")).expect("Transport manifest must be readable"); let settings_source = include_str!("../src/grpc_settings.rs"); let channel_source = include_str!("../src/grpc_channel.rs"); let unary_source = include_str!("../src/grpc_unary.rs"); let crate_root = include_str!("../src/lib.rs"); assert!(transport_manifest.contains("tonic = { workspace = true, features = [\"channel\", \"tls-aws-lc\", \"tls-webpki-roots\"] }")); assert!(transport_manifest.contains("tonic = { workspace = true, features = [\"codegen\", \"server\"] }")); assert!(transport_manifest.contains("tonic-prost.workspace = true")); assert!(transport_manifest.contains("yellowstone-grpc-proto.workspace = true")); assert!(transport_manifest.contains("yellowstone-grpc-proto = { workspace = true, features = [\"tonic\"] }")); assert!(!transport_manifest.contains("yellowstone-grpc-client")); assert!(settings_source.contains("YellowstoneGrpcMetadataEntry")); assert!(settings_source.contains("set_sensitive")); assert!(channel_source.contains("ClientTlsConfig")); assert!(channel_source.contains("with_webpki_roots")); assert!(channel_source.contains("pub async fn connect")); for path in [ "/geyser.Geyser/SubscribeReplayInfo", "/geyser.Geyser/Ping", "/geyser.Geyser/GetLatestBlockhash", "/geyser.Geyser/GetBlockHeight", "/geyser.Geyser/GetSlot", "/geyser.Geyser/IsBlockhashValid", "/geyser.Geyser/GetVersion", ] { assert!(unary_source.contains(path), "missing standard Yellowstone unary path: {path}"); } assert!(!unary_source.contains("const PATH_SUBSCRIBE: ")); assert!(!unary_source.contains("SubscribeDeshred")); assert!(!unary_source.contains("PublicNode")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _client = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_004_materializes_only_standard_subscribe_common_contract() { let source = include_str!("../src/grpc_subscribe.rs"); let crate_root = include_str!("../src/lib.rs"); for wire_field in [ "accounts:", "slots:", "transactions:", "transactions_status:", "blocks:", "blocks_meta:", "entry:", "commitment:", "accounts_data_slice:", "ping:", "from_slot:", ] { assert!(source.contains(wire_field), "missing standard Yellowstone SubscribeRequest field: {wire_field}"); } assert!(source.contains("MAX_GRPC_SUBSCRIBE_FILTER_GROUP_COUNT")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_FILTER_NAME_LENGTH_BYTES")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_DATA_SLICE_COUNT")); assert!(source.contains("offset.checked_add(length)")); assert!(source.contains("globally unique")); assert!(source.contains("SubscribeRequestFilterBlocksMeta")); assert!(source.contains("SubscribeRequestFilterEntry")); assert!(source.contains("YellowstoneSubscribeAccountFilter")); assert!(source.contains("YellowstoneSubscribeSlotFilter")); assert!(source.contains("YellowstoneSubscribeTransactionFilter")); assert!(source.contains("YellowstoneSubscribeBlockFilter")); assert!(!source.contains("SubscribeDeshred")); assert!(!source.contains("PublicNode")); assert!(!source.contains("OrbitFlare")); assert!(!source.contains("Helius")); assert!(!source.contains("pub fn to_wire")); assert!(!source.contains("pub(crate) fn to_wire")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _request = std::any::type_name::(); let _account = std::any::type_name::(); let _slot = std::any::type_name::(); let _transaction = std::any::type_name::(); let _block = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_005_accounts_and_slots_contract_remains_complete() { let source = include_str!("../src/grpc_subscribe.rs"); let crate_root = include_str!("../src/lib.rs"); for required in [ "account: self.accounts", "owner: self.owners", "filters: self.filters", "nonempty_txn_signature", "cuckoo_accounts_filter", "SubscribeRequestFilterAccountsFilterMemcmp", "DataSize", "TokenAccountState", "YellowstoneAccountLamportsFilter", "filter_by_commitment", "interslot_updates", "YellowstoneAccountUpdate", "YellowstoneSlotUpdate", "YellowstoneTransactionSignature", "SlotFirstShredReceived", "SlotCompleted", "SlotCreatedBank", "SlotDead", "dead_error", ] { assert!(source.contains(required), "missing pre.005 Accounts/Slots contract token: {required}"); } assert!(source.contains("MAX_GRPC_SUBSCRIBE_ACCOUNT_SELECTOR_COUNT")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_ACCOUNT_PREDICATE_COUNT")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_CUCKOO_DATA_LENGTH_BYTES")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_ACCOUNT_DATA_LENGTH_BYTES")); assert!(source.contains("MAX_GRPC_SUBSCRIBE_SLOT_DEAD_ERROR_LENGTH_BYTES")); assert!(source.contains("fn decode_account_update")); assert!(source.contains("fn decode_slot_update")); assert!(!source.contains("SubscribeDeshred")); assert!(!source.contains("PublicNode")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _account = std::any::type_name::(); let _slot = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_006_namespaces_unambiguously_http_owned_private_modules() { let crate_root = include_str!("../src/lib.rs"); let http_client = include_str!("../src/http_client.rs"); let http_executor = include_str!("../src/http_executor.rs"); let http_pool = include_str!("../src/http_pool.rs"); let http_resilience = include_str!("../src/http_resilience.rs"); let http_settings = include_str!("../src/http_settings.rs"); for declaration in ["mod http_client;", "mod http_executor;", "mod http_pool;", "mod http_resilience;", "mod http_settings;"] { assert!(crate_root.contains(declaration), "missing HTTP-owned private module declaration: {declaration}"); } for historical in ["mod client;", "mod executor;", "mod pool;", "mod resilience;", "mod settings;"] { assert!(!crate_root.contains(historical), "historical ambiguous private module declaration remains: {historical}"); } assert!(http_client.contains("HttpEndpointClient")); assert!(http_executor.contains("execute_standard_rpc")); assert!(http_pool.contains("HttpTransportPool")); assert!(http_resilience.contains("HttpRetryDecision")); assert!(http_settings.contains("HttpTransportSettings")); // Shared/protocol-oriented modules deliberately keep their existing names. for shared in ["mod constants;", "mod error;", "mod json_rpc;", "mod rpc_common;", "mod rpc_accounts;", "mod rpc_blocks;", "mod rpc_transactions;"] { assert!(crate_root.contains(shared), "shared/protocol module was incorrectly HTTP-prefixed: {shared}"); } } #[test] fn release_v0_2_9_pre_007_transactions_contract_remains_complete() { let source = include_str!("../src/grpc_subscribe.rs"); let crate_root = include_str!("../src/lib.rs"); for required in [ "YellowstoneSubscribeTransactionFilter", "YellowstoneTransactionSignatureSelector", "YellowstoneTokenAccountExpansion", "account_include", "account_exclude", "account_required", "cuckoo_account_include", "token_accounts", "YellowstoneTransactionUpdate", "YellowstoneTransactionStatusUpdate", "YellowstoneTransactionStatusMeta", "YellowstoneTransactionMessage", "YellowstoneMessageAddressTableLookup", "YellowstoneTransactionConfig", "compute_units_consumed", "cost_units", "inner_instructions_none", "log_messages_none", "return_data_none", "decode_transaction_update", "decode_transaction_status_update", ] { assert!(source.contains(required), "missing pre.007 Transactions contract token: {required}"); } assert!(source.contains("base58_decoded_length")); assert!(source.contains("YELLOWSTONE_TRANSACTION_SIGNATURE_WIRE_LENGTH_BYTES")); assert!(!source.contains("pub async fn subscribe(")); assert!(!source.contains("SubscribeDeshred")); assert!(!source.contains("PublicNode")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _filter = std::any::type_name::(); let _update = std::any::type_name::(); let _status = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_008_standard_blocks_contract_remains_complete() { let source = include_str!("../src/grpc_subscribe.rs"); let crate_root = include_str!("../src/lib.rs"); for required in [ "YellowstoneSubscribeBlockFilter", "account_include", "include_transactions", "include_accounts", "include_entries", "cuckoo_account_include", "YellowstoneBlockRewards", "YellowstoneBlockUpdate", "YellowstoneBlockMetaUpdate", "YellowstoneEntryInfo", "YellowstoneEntryUpdate", "executed_transaction_count", "updated_account_count", "entries_count", "starting_transaction_index", "decode_block_update", "decode_block_meta_update", "decode_entry_update", "decode_block_rewards", ] { assert!(source.contains(required), "missing pre.008 Blocks contract token: {required}"); } assert!(source.contains("MAX_GRPC_BLOCK_VECTOR_COUNT")); assert!(source.contains("base58_decoded_length(value.as_str())")); assert!(source.contains("std::vec::Vec")); assert!(source.contains("std::vec::Vec")); assert!(source.contains("std::vec::Vec")); assert!(source.contains("fn decode_block_update")); assert!(!source.contains("pub async fn subscribe(")); assert!(!source.contains("SubscribeDeshred")); assert!(!source.contains("PublicNode")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _block = std::any::type_name::(); let _meta = std::any::type_name::(); let _entry = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_009_bounded_standard_bidi_session_contract_remains_complete() { let stream_source = include_str!("../src/grpc_stream.rs"); let subscribe_source = include_str!("../src/grpc_subscribe.rs"); let channel_source = include_str!("../src/grpc_channel.rs"); let crate_root = include_str!("../src/lib.rs"); for required in [ "const PATH_SUBSCRIBE: &str = \"/geyser.Geyser/Subscribe\"", ".streaming(", "tokio::sync::mpsc::channel", "tokio::sync::watch::channel", "try_send", "AUTO_SUBSCRIBE_PING_ID", "ping_request_wire", "finish_client_half_close", "timeout_at", "max_decoding_message_size", "max_encoding_message_size", "YellowstoneGrpcSubscribeState", "SolanaYellowstoneGrpcSubscribeSession", ] { assert!(stream_source.contains(required), "missing pre.009 bidi runtime token: {required}"); } for required in [ "YellowstoneSubscribeUpdate", "YellowstoneSubscribePingUpdate", "YellowstoneSubscribePongUpdate", "yellowstone_subscribe_request_to_wire", "yellowstone_subscribe_update_from_wire", ] { assert!(subscribe_source.contains(required), "missing pre.009 runtime wire bridge token: {required}"); } assert!(channel_source.contains("pub async fn open_standard_subscribe")); assert!(!stream_source.contains("unbounded_channel")); assert!(!stream_source.contains("SubscribeDeshred")); assert!(!stream_source.contains("PublicNode")); assert!(!stream_source.contains("OrbitFlare")); assert!(!stream_source.contains("Helius")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _session = std::any::type_name::(); let _state = ksp_onchain_transport_lib::YellowstoneGrpcSubscribeState::Active; let _update = std::any::type_name::(); } #[test] fn release_v0_2_9_pre_010_adds_bounded_reconnect_replay_and_conservative_continuity_observability() { let stream_source = include_str!("../src/grpc_stream.rs"); let crate_root = include_str!("../src/lib.rs"); for required in [ "YellowstoneGrpcSubscribeState::Reconnecting", "YellowstoneGrpcSubscribeSnapshot", "YellowstoneGrpcSubscribeSnapshotSource", "snapshot_source", "wait_for_change", "reconnect_subscribe_stream", "replay_first_available", "SubscribeReplayInfo", "last_requested_from_slot", "last_observed_slot", "reconnect_count", "replay_attempt_count", "continuity_gap_count", "duplicate_update_count", "MAX_RECENT_UPDATE_IDENTITIES", "first_available > requested", "max_optional_slot", "reconnect budget is exhausted", "request mutation", ] { assert!(stream_source.contains(required), "missing pre.010 reconnect/replay contract token: {required}"); } assert!(stream_source.contains("still delivered to the caller")); assert!(stream_source.contains("does not claim exactly-once delivery")); assert!(!stream_source.contains("unbounded_channel")); assert!(!stream_source.contains("SubscribeDeshred")); assert!(!stream_source.contains("PublicNode")); assert!(!stream_source.contains("OrbitFlare")); assert!(!stream_source.contains("Helius")); assert!(!crate_root.contains("pub use tonic")); assert!(!crate_root.contains("pub use yellowstone_grpc_proto")); let _snapshot = std::any::type_name::(); let _state = ksp_onchain_transport_lib::YellowstoneGrpcSubscribeState::Reconnecting; }