v0.3.14-pre.004

This commit is contained in:
2026-09-11 22:22:51 +02:00
parent 6721a4142a
commit 1006079654
11 changed files with 624 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_stream.rs
// version: 5
// version: 6
#[derive(Clone, Copy)]
enum FixtureMode {
@@ -13,6 +13,7 @@ enum FixtureMode {
Idle,
ReconnectReplay,
ReplayGap,
ReplayAcceptedWithoutBoundary,
ReconnectExhausted,
}
@@ -153,6 +154,22 @@ impl yellowstone_grpc_proto::geyser::geyser_server::Geyser for FixtureGeyser {
}
}
},
FixtureMode::ReplayAcceptedWithoutBoundary => {
if subscribe_call == 1 {
assert_eq!(initial.from_slot, std::option::Option::None);
let _ = outbound_tx.send(std::result::Result::Ok(slot_update(800))).await;
} else {
assert_eq!(subscribe_call, 2);
assert_eq!(initial.from_slot, std::option::Option::Some(800));
if outbound_tx.send(std::result::Result::Ok(slot_update(805))).await.is_err() {
return;
}
let half_close = inbound.message().await;
if matches!(half_close, std::result::Result::Ok(std::option::Option::None)) {
half_close_seen.store(true, std::sync::atomic::Ordering::SeqCst);
}
}
},
FixtureMode::ReconnectExhausted => {
assert_eq!(subscribe_call, 1);
let _ = outbound_tx.send(std::result::Result::Ok(slot_update(700))).await;
@@ -191,7 +208,7 @@ impl yellowstone_grpc_proto::geyser::geyser_server::Geyser for FixtureGeyser {
return std::result::Result::Err(error);
}
let first_available = match self.mode {
FixtureMode::ReconnectReplay | FixtureMode::ReconnectExhausted => std::option::Option::Some(400),
FixtureMode::ReconnectReplay | FixtureMode::ReconnectExhausted | FixtureMode::ReplayAcceptedWithoutBoundary => std::option::Option::Some(400),
FixtureMode::ReplayGap => std::option::Option::Some(505),
_ => return std::result::Result::Err(tonic::Status::unimplemented("replay info is outside this fixture mode")),
};
@@ -595,6 +612,8 @@ async fn yellowstone_reconnect_replays_from_last_observed_slot_and_counts_duplic
assert_eq!(snapshot.state(), crate::YellowstoneGrpcSubscribeState::Active);
assert_eq!(snapshot.reconnect_count(), 1);
assert_eq!(snapshot.replay_attempt_count(), 1);
assert_eq!(snapshot.replay_delivery_count(), 1);
assert_eq!(snapshot.replay_coverage_unproven_count(), 1);
assert_eq!(snapshot.continuity_gap_count(), 0);
assert_eq!(snapshot.duplicate_update_count(), 1);
assert_eq!(snapshot.last_requested_from_slot(), std::option::Option::Some(500));
@@ -627,6 +646,8 @@ async fn yellowstone_replay_info_proves_and_clamps_retention_gap_without_lossles
let snapshot = session.snapshot();
assert_eq!(snapshot.reconnect_count(), 1);
assert_eq!(snapshot.replay_attempt_count(), 1);
assert_eq!(snapshot.replay_delivery_count(), 1);
assert_eq!(snapshot.replay_coverage_unproven_count(), 1);
assert_eq!(snapshot.continuity_gap_count(), 1);
assert_eq!(snapshot.duplicate_update_count(), 0);
assert_eq!(snapshot.last_requested_from_slot(), std::option::Option::Some(505));
@@ -635,6 +656,42 @@ async fn yellowstone_replay_info_proves_and_clamps_retention_gap_without_lossles
server.stop().await;
}
#[tokio::test(flavor = "current_thread")]
async fn v0_3_14_pre_004_replay_acceptance_without_boundary_delivery_never_claims_coverage() {
let server = FixtureServer::start(FixtureMode::ReplayAcceptedWithoutBoundary).await;
let defaults = crate::YellowstoneGrpcSessionSettings::default();
let settings = fixture_settings_with_reconnect(
server.endpoint_url.as_str(),
8,
8,
defaults.max_inbound_message_size_bytes(),
defaults.max_outbound_message_size_bytes(),
crate::YellowstoneGrpcReconnectSettings::new(3, std::time::Duration::from_millis(5), std::time::Duration::from_millis(20)),
);
let channel = crate::YellowstoneGrpcChannel::connect(&settings).await.expect("fixture channel must connect");
let mut session = channel.open_standard_subscribe(initial_request()).await.expect("fixture Subscribe stream must open");
let first = session.next_update().await.expect("first slot must decode").expect("first slot must be present");
match first {
crate::YellowstoneSubscribeUpdate::Slot(value) => assert_eq!(value.slot(), 800),
_ => panic!("fixture must return first Slot"),
}
let resumed = session.next_update().await.expect("post-reconnect slot must decode").expect("post-reconnect slot must be present");
match resumed {
crate::YellowstoneSubscribeUpdate::Slot(value) => assert_eq!(value.slot(), 805),
_ => panic!("fixture must return post-reconnect Slot"),
}
let snapshot = session.snapshot();
assert_eq!(snapshot.reconnect_count(), 1);
assert_eq!(snapshot.replay_attempt_count(), 1);
assert_eq!(snapshot.replay_delivery_count(), 0);
assert_eq!(snapshot.replay_coverage_unproven_count(), 1);
assert_eq!(snapshot.continuity_gap_count(), 0);
assert_eq!(snapshot.last_requested_from_slot(), std::option::Option::Some(800));
assert_eq!(snapshot.last_observed_slot(), std::option::Option::Some(805));
session.close().await.expect("coverage-unproven fixture must close cleanly");
server.stop().await;
}
#[tokio::test(flavor = "current_thread")]
async fn yellowstone_reconnect_budget_exhaustion_is_terminal_and_safe() {
let server = FixtureServer::start(FixtureMode::ReconnectExhausted).await;
@@ -656,6 +713,8 @@ async fn yellowstone_reconnect_budget_exhaustion_is_terminal_and_safe() {
assert_eq!(snapshot.state(), crate::YellowstoneGrpcSubscribeState::Failed);
assert_eq!(snapshot.reconnect_count(), 0);
assert_eq!(snapshot.replay_attempt_count(), 2);
assert_eq!(snapshot.replay_delivery_count(), 0);
assert_eq!(snapshot.replay_coverage_unproven_count(), 0);
assert_eq!(snapshot.terminal_error_code(), std::option::Option::Some(crate::ERROR_CODE_GRPC_CHANNEL_FAILED));
let rendered = format!("{error:?} {session:?}");
assert!(!rendered.contains("GRPC-RECONNECT-SECRET-CANARY"));