// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_subscribe.rs // version: 1 fn filter_name(value: &str) -> crate::YellowstoneSubscribeFilterName { return crate::YellowstoneSubscribeFilterName::new(value).expect("fixture filter name must validate"); } #[test] fn yellowstone_subscribe_filter_name_is_bounded_globally_unique_and_debug_redacted() { assert!(crate::YellowstoneSubscribeFilterName::new("").is_err()); assert!(crate::YellowstoneSubscribeFilterName::new(" leading").is_err()); assert!(crate::YellowstoneSubscribeFilterName::new("trailing ").is_err()); assert!(crate::YellowstoneSubscribeFilterName::new("line\nbreak").is_err()); assert!(crate::YellowstoneSubscribeFilterName::new("x".repeat(129)).is_err()); let name = filter_name("account-primary"); assert_eq!(name.as_str(), "account-primary"); assert!(!format!("{name:?}").contains("account-primary")); let mut request = crate::YellowstoneSubscribeRequest::new(); assert!(request.insert_account_filter(name.clone(), crate::YellowstoneSubscribeAccountFilter::new()).is_ok()); let duplicate = request.insert_slot_filter(name, crate::YellowstoneSubscribeSlotFilter::new()); assert!(duplicate.is_err()); } #[test] fn yellowstone_subscribe_empty_and_named_empty_maps_encode_exactly() { let empty = crate::YellowstoneSubscribeRequest::new().to_wire().expect("empty request must encode"); assert!(empty.accounts.is_empty()); assert!(empty.slots.is_empty()); assert!(empty.transactions.is_empty()); assert!(empty.transactions_status.is_empty()); assert!(empty.blocks.is_empty()); assert!(empty.blocks_meta.is_empty()); assert!(empty.entry.is_empty()); assert_eq!(empty.commitment, std::option::Option::None); assert!(empty.accounts_data_slice.is_empty()); assert_eq!(empty.ping, std::option::Option::None); assert_eq!(empty.from_slot, std::option::Option::None); let mut request = crate::YellowstoneSubscribeRequest::new(); assert!(request.insert_account_filter(filter_name("accounts"), crate::YellowstoneSubscribeAccountFilter::new()).is_ok()); assert!(request.insert_slot_filter(filter_name("slots"), crate::YellowstoneSubscribeSlotFilter::new()).is_ok()); assert!(request.insert_transaction_filter(filter_name("transactions"), crate::YellowstoneSubscribeTransactionFilter::new()).is_ok()); assert!(request.insert_transaction_status_filter(filter_name("transaction-status"), crate::YellowstoneSubscribeTransactionFilter::new()).is_ok()); assert!(request.insert_block_filter(filter_name("blocks"), crate::YellowstoneSubscribeBlockFilter::new()).is_ok()); assert!(request.insert_blocks_meta_filter(filter_name("blocks-meta"), crate::YellowstoneSubscribeBlocksMetaFilter::new()).is_ok()); assert!(request.insert_entry_filter(filter_name("entry"), crate::YellowstoneSubscribeEntryFilter::new()).is_ok()); let wire = request.to_wire().expect("named empty request must encode"); assert_eq!(wire.accounts.len(), 1); assert_eq!(wire.slots.len(), 1); assert_eq!(wire.transactions.len(), 1); assert_eq!(wire.transactions_status.len(), 1); assert_eq!(wire.blocks.len(), 1); assert_eq!(wire.blocks_meta.len(), 1); assert_eq!(wire.entry.len(), 1); assert_eq!(wire.accounts.get("accounts"), std::option::Option::Some(&yellowstone_grpc_proto::geyser::SubscribeRequestFilterAccounts::default())); assert_eq!(wire.slots.get("slots"), std::option::Option::Some(&yellowstone_grpc_proto::geyser::SubscribeRequestFilterSlots::default())); assert_eq!( wire.transactions.get("transactions"), std::option::Option::Some(&yellowstone_grpc_proto::geyser::SubscribeRequestFilterTransactions::default()) ); assert_eq!( wire.transactions_status.get("transaction-status"), std::option::Option::Some(&yellowstone_grpc_proto::geyser::SubscribeRequestFilterTransactions::default()) ); assert_eq!(wire.blocks.get("blocks"), std::option::Option::Some(&yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks::default())); assert!(wire.blocks_meta.contains_key("blocks-meta")); assert!(wire.entry.contains_key("entry")); } #[test] fn yellowstone_subscribe_common_fields_preserve_optional_and_ordered_wire_semantics() { let mut request = crate::YellowstoneSubscribeRequest::new(); request.set_commitment(std::option::Option::Some(crate::SolanaCommitment::Finalized)); let first = crate::YellowstoneAccountsDataSlice::new(8, 16).expect("first slice must validate"); let second = crate::YellowstoneAccountsDataSlice::new(64, 0).expect("zero-length slice remains representable"); assert!(request.push_accounts_data_slice(first).is_ok()); assert!(request.push_accounts_data_slice(second).is_ok()); request.set_ping(std::option::Option::Some(crate::YellowstoneSubscribePing::new(-7))); request.set_from_slot(std::option::Option::Some(42)); assert_eq!(request.commitment(), std::option::Option::Some(crate::SolanaCommitment::Finalized)); assert_eq!(request.accounts_data_slices(), &[first, second]); assert_eq!(request.ping(), std::option::Option::Some(crate::YellowstoneSubscribePing::new(-7))); assert_eq!(request.from_slot(), std::option::Option::Some(42)); let wire = request.to_wire().expect("common fields must encode"); assert_eq!(wire.commitment, std::option::Option::Some(yellowstone_grpc_proto::geyser::CommitmentLevel::Finalized as i32)); assert_eq!(wire.accounts_data_slice.len(), 2); assert_eq!(wire.accounts_data_slice[0].offset, 8); assert_eq!(wire.accounts_data_slice[0].length, 16); assert_eq!(wire.accounts_data_slice[1].offset, 64); assert_eq!(wire.accounts_data_slice[1].length, 0); assert_eq!(wire.ping.map(|ping| return ping.id), std::option::Option::Some(-7)); assert_eq!(wire.from_slot, std::option::Option::Some(42)); } #[test] fn yellowstone_subscribe_common_bounds_reject_before_wire_conversion() { assert!(crate::YellowstoneAccountsDataSlice::new(0, 64 * 1024 * 1024).is_ok()); assert!(crate::YellowstoneAccountsDataSlice::new(0, 64 * 1024 * 1024 + 1).is_err()); assert!(crate::YellowstoneAccountsDataSlice::new(u64::MAX, 1).is_err()); let mut slices = crate::YellowstoneSubscribeRequest::new(); for index in 0..128_u64 { let slice = crate::YellowstoneAccountsDataSlice::new(index, 1).expect("bounded fixture slice must validate"); assert!(slices.push_accounts_data_slice(slice).is_ok()); } let excess = crate::YellowstoneAccountsDataSlice::new(129, 1).expect("excess fixture slice itself must validate"); assert!(slices.push_accounts_data_slice(excess).is_err()); let mut filters = crate::YellowstoneSubscribeRequest::new(); for index in 0..1_024_u32 { let name = crate::YellowstoneSubscribeFilterName::new(format!("f{index}")).expect("bounded filter name must validate"); assert!(filters.insert_account_filter(name, crate::YellowstoneSubscribeAccountFilter::new()).is_ok()); } let excess_name = crate::YellowstoneSubscribeFilterName::new("excess").expect("excess filter name must validate independently"); assert!(filters.insert_account_filter(excess_name, crate::YellowstoneSubscribeAccountFilter::new()).is_err()); } #[test] fn yellowstone_subscribe_debug_omits_filter_names_and_future_payloads() { let mut request = crate::YellowstoneSubscribeRequest::new(); assert!(request.insert_account_filter(filter_name("sensitive-label"), crate::YellowstoneSubscribeAccountFilter::new()).is_ok()); request.set_ping(std::option::Option::Some(crate::YellowstoneSubscribePing::new(9))); request.set_from_slot(std::option::Option::Some(77)); let debug = format!("{request:?}"); assert!(!debug.contains("sensitive-label")); assert!(debug.contains("account_filter_count")); assert!(debug.contains("from_slot")); }