Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions crates/testutil/src/beaconmock/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ pub(crate) async fn mount_defaults(server: &MockServer, state: Arc<MockState>) {
})
.await;

// Polled once a minute by the readiness checker (`run_ready_checker`).
// `connected` must stay non-zero: zero peers is a readiness failure
// (`ReadinessError::BeaconNodeZeroPeers`). 80 matches charon's beaconmock.
mount_json(server, "GET", "/eth/v1/node/peer_count", |_| {
json!({
"data": {
"connected": "80",
"connecting": "0",
"disconnected": "0",
"disconnecting": "0"
Comment on lines +100 to +103

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Differs from charon. Charon's beaconmock sets only Connected: 80 and lets Go zero-value the rest. Here all four fields are required Strings on the generated GetPeerCountResponseResponseData, so omitting any one fails the decode rather than defaulting — which would trade this bug for a different warning on the same poll.

}
})
})
.await;

mount_json(server, "GET", "/eth/v1/beacon/headers/head", |_| {
json!({
"data": {
Expand Down Expand Up @@ -601,6 +616,8 @@ fn bellatrix_signed_block_response() -> Value {

json!({
"version": "bellatrix",
"execution_optimistic": false,
"finalized": false,
"data": {
"message": {
"slot": random_slot().to_string(),
Expand Down Expand Up @@ -778,6 +795,10 @@ pub(crate) fn default_genesis_time() -> DateTime<Utc> {
mod tests {
use super::*;
use crate::beaconmock::BeaconMock;
use pluto_eth2api::{
client::EthBeaconNodeApiClient,
types::{GetBlockV2Request, GetBlockV2Response, GetPeerCountRequest, GetPeerCountResponse},
};

#[test]
fn default_spec_contains_load_bearing_keys() {
Expand Down Expand Up @@ -837,4 +858,53 @@ mod tests {
.expect("blocks request (numeric)");
assert_eq!(resp.status(), 200);
}

/// The inclusion checker consumes this endpoint through the generated
/// client, which requires `execution_optimistic` and `finalized` — the
/// beacon-API spec marks both required and non-nullable. Asserting on the
/// raw body is not enough: only a decode proves the mock is consumable.
#[tokio::test]
async fn bellatrix_signed_block_decodes_through_the_generated_client() {
let mock = BeaconMock::builder()
.build()
.await
.expect("build beacon mock");

let client = EthBeaconNodeApiClient::with_base_url(mock.uri()).expect("client");
Comment thread
emlautarom1 marked this conversation as resolved.
Outdated
let request = GetBlockV2Request::builder()
.block_id("123".to_string())
.build()
.expect("block request");

let response = client.get_block_v2(request).await.expect("get_block_v2");
assert!(
matches!(response, GetBlockV2Response::Ok(_)),
"expected a decoded 200, got {response:?}"
);
}

/// The readiness checker polls this every minute and treats zero connected
/// peers as unready, so an unmounted route both warns and pins
/// `app_beacon_node_peers` to a misleading zero.
#[tokio::test]
async fn peer_count_reports_connected_peers() {
let mock = BeaconMock::builder()
.build()
.await
.expect("build beacon mock");

let client = EthBeaconNodeApiClient::with_base_url(mock.uri()).expect("client");
Comment thread
emlautarom1 marked this conversation as resolved.
Outdated

let response = client
.get_peer_count(GetPeerCountRequest {})
.await
.expect("get_peer_count");
let GetPeerCountResponse::Ok(peers) = response else {
panic!("expected a decoded 200, got {response:?}");
};
assert_ne!(
peers.data.connected, "0",
"zero connected peers fails the readiness check"
);
}
}
Loading