Add PeerSet readiness and request future cancel-safety tests (#3252)

* add a PeerSet drop test

* replace all `now_or_never()` in test

* add a drop of the ready future to test

* make sure requests always go to client

* fix imports and runtime

* add a peer sent hang test

* replace `std::mem::discriminant` calls

* replace `unreachable` calls

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* move comments

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* add `yield_now` call

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* increase timeout but make the test fast pausing the runtime

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* apply last fixes

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Alfredo Garcia 2022-02-02 15:18:45 -03:00 committed by GitHub
parent 6ddbe7972d
commit 4d2b3768c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 169 additions and 0 deletions

View File

@ -31,6 +31,9 @@ use crate::{
#[cfg(test)] #[cfg(test)]
mod prop; mod prop;
#[cfg(test)]
mod vectors;
/// The maximum number of arbitrary peers to generate in [`PeerVersions`]. /// The maximum number of arbitrary peers to generate in [`PeerVersions`].
/// ///
/// This affects the maximum number of peer connections added to the [`PeerSet`] during the tests. /// This affects the maximum number of peer connections added to the [`PeerSet`] during the tests.

View File

@ -0,0 +1,166 @@
use std::time::Duration;
use tokio::time::timeout;
use tower::{Service, ServiceExt};
use zebra_chain::parameters::{Network, NetworkUpgrade};
use super::{PeerSetBuilder, PeerVersions};
use crate::{
peer::{ClientRequest, MinimumPeerVersion},
protocol::external::types::Version,
Request,
};
#[test]
fn peer_set_ready_single_connection() {
// We are going to use just one peer version in this test
let peer_versions = PeerVersions {
peer_versions: vec![Version::min_specified_for_upgrade(
Network::Mainnet,
NetworkUpgrade::Canopy,
)],
};
// Start the runtime
let runtime = zebra_test::init_async();
let _guard = runtime.enter();
// Get peers and client handles of them
let (discovered_peers, handles) = peer_versions.mock_peer_discovery();
let (minimum_peer_version, _best_tip_height) =
MinimumPeerVersion::with_mock_chain_tip(Network::Mainnet);
// We will just use the first peer handle
let mut client_handle = handles
.into_iter()
.next()
.expect("we always have at least one client");
// Client did not received anything yet
assert!(client_handle
.try_to_receive_outbound_client_request()
.is_empty());
runtime.block_on(async move {
// Build a peerset
let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.build();
// Get a ready future
let peer_ready_future = peer_set.ready();
// Drop the future
std::mem::drop(peer_ready_future);
// Peer set will remain ready for requests
let peer_ready1 = peer_set
.ready()
.await
.expect("peer set service is always ready");
// Make sure the client did not received anything yet
assert!(client_handle
.try_to_receive_outbound_client_request()
.is_empty());
// Make a call to the peer set that returns a future
let fut = peer_ready1.call(Request::Peers);
// Client received the request
assert!(matches!(
client_handle
.try_to_receive_outbound_client_request()
.request(),
Some(ClientRequest {
request: Request::Peers,
..
})
));
// Drop the future
std::mem::drop(fut);
// Peer set will remain ready for requests
let peer_ready2 = peer_set
.ready()
.await
.expect("peer set service is always ready");
// Get a new future calling a different request than before
let _fut = peer_ready2.call(Request::MempoolTransactionIds);
// Client received the request
assert!(matches!(
client_handle
.try_to_receive_outbound_client_request()
.request(),
Some(ClientRequest {
request: Request::MempoolTransactionIds,
..
})
));
});
}
#[test]
fn peer_set_ready_multiple_connections() {
// Use three peers with the same version
let peer_version = Version::min_specified_for_upgrade(Network::Mainnet, NetworkUpgrade::Canopy);
let peer_versions = PeerVersions {
peer_versions: vec![peer_version, peer_version, peer_version],
};
// Start the runtime
let runtime = zebra_test::init_async();
let _guard = runtime.enter();
// Pause the runtime's timer so that it advances automatically.
//
// CORRECTNESS: This test does not depend on external resources that could really timeout, like
// real network connections.
tokio::time::pause();
// Get peers and client handles of them
let (discovered_peers, handles) = peer_versions.mock_peer_discovery();
let (minimum_peer_version, _best_tip_height) =
MinimumPeerVersion::with_mock_chain_tip(Network::Mainnet);
// Make sure we have the right number of peers
assert_eq!(handles.len(), 3);
runtime.block_on(async move {
// Build a peerset
let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.build();
// Get peerset ready
let peer_ready = peer_set
.ready()
.await
.expect("peer set service is always ready");
// Check we have the right amount of ready services
assert_eq!(peer_ready.ready_services.len(), 3);
// Stop some peer connections but not all
handles[0].stop_connection_task().await;
handles[1].stop_connection_task().await;
// We can still make the peer set ready
peer_set
.ready()
.await
.expect("peer set service is always ready");
// Stop the connection of the last peer
handles[2].stop_connection_task().await;
// Peer set hangs when no more connections are present
let peer_ready = peer_set.ready();
assert!(timeout(Duration::from_secs(10), peer_ready).await.is_err());
});
}