v0.2.2-pre.006

This commit is contained in:
2026-08-18 08:27:42 +02:00
parent 83cb861e54
commit e14ce1c36f
16 changed files with 660 additions and 33 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/unit_tests/rpc_cluster.rs
// version: 3
// version: 4
#[test]
fn cluster_node_fixture_preserves_v4_client_id_and_optional_fields() {
@@ -52,7 +52,7 @@ fn leader_schedule_request_encodes_current_epoch_and_slot_overloads_without_ambi
}
#[test]
fn staged_epoch_snapshot_and_leader_helpers_preserve_wire_shapes() {
fn epoch_snapshot_and_leader_helpers_preserve_wire_shapes() {
let epoch = crate::SolanaEpochInfo::decode_wire(
"getEpochInfo",
serde_json::json!({"absoluteSlot":10,"blockHeight":9,"epoch":2,"slotIndex":3,"slotsInEpoch":32,"transactionCount":null}),
@@ -78,7 +78,7 @@ fn staged_epoch_snapshot_and_leader_helpers_preserve_wire_shapes() {
}
#[test]
fn staged_vote_status_and_config_helpers_preserve_wire_shapes() {
fn vote_status_and_config_helpers_preserve_wire_shapes() {
let vote_pubkey = "11111111111111111111111111111111".parse::<ksp_core_lib::Pubkey>().expect("fixture pubkey must parse");
let config = crate::SolanaVoteAccountsConfig::new(
std::option::Option::Some(crate::SolanaCommitment::Finalized),
@@ -321,3 +321,168 @@ async fn typed_get_max_cluster_slots_decode_u64_without_params() {
assert_eq!(shred_body["method"], serde_json::json!("getMaxShredInsertSlot"));
assert_eq!(shred_body["params"], serde_json::json!([]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_leader_schedule_serializes_slot_config_and_decodes_map() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_leader_schedule.success.json"));
let pool = pool_for_url(url.as_str());
let identity = "11111111111111111111111111111111".parse::<ksp_core_lib::Pubkey>().expect("fixture pubkey must parse");
let config =
crate::SolanaLeaderScheduleConfig::new(std::option::Option::Some(identity.clone()), std::option::Option::Some(crate::SolanaCommitment::Finalized));
let request = crate::SolanaLeaderScheduleRequest::Slot { slot: 430_000_000, config: std::option::Option::Some(config) };
let schedule = pool
.get_leader_schedule(&crate::HttpRoleName::new("default"), &request)
.await
.expect("leader schedule fixture must succeed")
.expect("leader schedule fixture must not be null");
assert_eq!(schedule.entries().get(&identity), std::option::Option::Some(&std::vec![0, 2, 4]));
let request = handle.join().expect("fixture server must join");
let body = request_body(request.as_str());
assert_eq!(body["method"], serde_json::json!("getLeaderSchedule"));
assert_eq!(body["params"], serde_json::json!([430000000,{"identity":"11111111111111111111111111111111","commitment":"finalized"}]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_leader_schedule_preserves_null_result() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_leader_schedule.null.json"));
let pool = pool_for_url(url.as_str());
let schedule = pool
.get_leader_schedule(&crate::HttpRoleName::new("default"), &crate::SolanaLeaderScheduleRequest::default())
.await
.expect("nullable leader schedule fixture must succeed");
assert_eq!(schedule, std::option::Option::None);
let request = handle.join().expect("fixture server must join");
assert_eq!(request_body(request.as_str())["params"], serde_json::json!([]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_serializes_context_config_and_omits_empty_config() {
let (configured_url, configured_handle) = serve_once(include_str!("../fixtures/http/get_slot.success.json"));
let configured_pool = pool_for_url(configured_url.as_str());
let config = crate::SolanaContextConfig::new(std::option::Option::Some(crate::SolanaCommitment::Confirmed), std::option::Option::Some(429_999_999));
let slot = configured_pool.get_slot(&crate::HttpRoleName::new("default"), std::option::Option::Some(&config)).await.expect("slot fixture must succeed");
assert_eq!(slot, 430_000_020);
let request = configured_handle.join().expect("fixture server must join");
assert_eq!(request_body(request.as_str())["params"], serde_json::json!([{"commitment":"confirmed","minContextSlot":429999999}]));
let (empty_url, empty_handle) = serve_once(include_str!("../fixtures/http/get_slot.success.json"));
let empty_pool = pool_for_url(empty_url.as_str());
let empty = crate::SolanaContextConfig::default();
let slot = empty_pool
.get_slot(&crate::HttpRoleName::new("default"), std::option::Option::Some(&empty))
.await
.expect("slot fixture with empty config must succeed");
assert_eq!(slot, 430_000_020);
let request = empty_handle.join().expect("fixture server must join");
assert_eq!(request_body(request.as_str())["params"], serde_json::json!([]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_leader_serializes_context_config_and_decodes_pubkey() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_slot_leader.success.json"));
let pool = pool_for_url(url.as_str());
let config = crate::SolanaContextConfig::new(std::option::Option::Some(crate::SolanaCommitment::Finalized), std::option::Option::Some(430_000_000));
let leader = pool
.get_slot_leader(&crate::HttpRoleName::new("default"), std::option::Option::Some(&config))
.await
.expect("slot leader fixture must succeed");
assert_eq!(leader.to_string(), "ComputeBudget111111111111111111111111111111");
let request = handle.join().expect("fixture server must join");
assert_eq!(request_body(request.as_str())["params"], serde_json::json!([{"commitment":"finalized","minContextSlot":430000000}]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_leader_rejects_invalid_wire_pubkey() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_slot_leader.invalid_pubkey.json"));
let pool = pool_for_url(url.as_str());
let result = pool.get_slot_leader(&crate::HttpRoleName::new("default"), std::option::Option::None).await;
let error = result.expect_err("invalid slot leader must reject typed response");
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_RESPONSE);
handle.join().expect("fixture server must join");
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_leaders_preserves_order_and_serializes_range() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_slot_leaders.success.json"));
let pool = pool_for_url(url.as_str());
let leaders = pool.get_slot_leaders(&crate::HttpRoleName::new("default"), 430_000_020, 2).await.expect("slot leaders fixture must succeed");
assert_eq!(leaders.len(), 2);
assert_eq!(leaders[0].to_string(), "11111111111111111111111111111111");
assert_eq!(leaders[1].to_string(), "ComputeBudget111111111111111111111111111111");
let request = handle.join().expect("fixture server must join");
let body = request_body(request.as_str());
assert_eq!(body["method"], serde_json::json!("getSlotLeaders"));
assert_eq!(body["params"], serde_json::json!([430000020, 2]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_leaders_rejects_out_of_range_limit_before_io() {
let pool = pool_for_url("http://127.0.0.1:9");
let zero = pool.get_slot_leaders(&crate::HttpRoleName::new("default"), 1, 0).await;
let zero_error = zero.expect_err("zero slot-leader limit must reject before I/O");
assert_eq!(zero_error.code(), crate::ERROR_CODE_INVALID_RPC_PARAMETERS);
let above = pool.get_slot_leaders(&crate::HttpRoleName::new("default"), 1, 5_001).await;
let above_error = above.expect_err("slot-leader limit above 5000 must reject before I/O");
assert_eq!(above_error.code(), crate::ERROR_CODE_INVALID_RPC_PARAMETERS);
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_slot_leaders_rejects_invalid_wire_pubkey() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_slot_leaders.invalid_pubkey.json"));
let pool = pool_for_url(url.as_str());
let result = pool.get_slot_leaders(&crate::HttpRoleName::new("default"), 1, 2).await;
let error = result.expect_err("invalid slot leader list item must reject typed response");
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_RESPONSE);
handle.join().expect("fixture server must join");
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_vote_accounts_serializes_full_config_and_preserves_optional_bps() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_vote_accounts.success.json"));
let pool = pool_for_url(url.as_str());
let vote_pubkey = "11111111111111111111111111111111".parse::<ksp_core_lib::Pubkey>().expect("fixture pubkey must parse");
let config = crate::SolanaVoteAccountsConfig::new(
std::option::Option::Some(crate::SolanaCommitment::Finalized),
std::option::Option::Some(vote_pubkey),
std::option::Option::Some(true),
std::option::Option::Some(128),
);
let status = pool
.get_vote_accounts(&crate::HttpRoleName::new("default"), std::option::Option::Some(&config))
.await
.expect("vote accounts fixture must succeed");
assert_eq!(status.current().len(), 1);
assert_eq!(status.delinquent().len(), 1);
assert_eq!(status.current()[0].inflation_rewards_commission_bps(), std::option::Option::Some(750));
assert_eq!(status.current()[0].epoch_credits().len(), 2);
assert_eq!(status.delinquent()[0].inflation_rewards_commission_bps(), std::option::Option::None);
let request = handle.join().expect("fixture server must join");
assert_eq!(
request_body(request.as_str())["params"],
serde_json::json!([{"commitment":"finalized","votePubkey":"11111111111111111111111111111111","keepUnstakedDelinquents":true,"delinquentSlotDistance":128}])
);
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_vote_accounts_omits_explicitly_empty_config() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_vote_accounts.success.json"));
let pool = pool_for_url(url.as_str());
let config = crate::SolanaVoteAccountsConfig::default();
let status = pool
.get_vote_accounts(&crate::HttpRoleName::new("default"), std::option::Option::Some(&config))
.await
.expect("vote accounts fixture with empty config must succeed");
assert_eq!(status.current().len(), 1);
let request = handle.join().expect("fixture server must join");
assert_eq!(request_body(request.as_str())["params"], serde_json::json!([]));
}
#[tokio::test(flavor = "current_thread")]
async fn typed_get_vote_accounts_rejects_invalid_wire_pubkey() {
let (url, handle) = serve_once(include_str!("../fixtures/http/get_vote_accounts.invalid_pubkey.json"));
let pool = pool_for_url(url.as_str());
let result = pool.get_vote_accounts(&crate::HttpRoleName::new("default"), std::option::Option::None).await;
let error = result.expect_err("invalid vote-account pubkey must reject typed response");
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_RESPONSE);
handle.join().expect("fixture server must join");
}