tests(rpc): Add some RPC acceptance tests (#3641)
* tests(rpc): add an rpc endpoint test * tests(rpc): add an rpc port conflict test * tests(rpc): refactor some imports * tests(rpc): fix failures, make test more complete * tests(rpc): parse json response for better coverage * tests(rpc): change request * tests(rpc): wait until port is open in rpc_endpoint test * tests(rpc): add a delay between launching 2 nodes * tests(rpc): try 5 seconds * refactor(rpc): open rpc server faster * tests(rpc): extend `LAUNCH_DELAY` to 15 seconds * fix(rpc): disable rpc_conflict test for windows * fix(ci): skip the RPC tests if the network is disabled * rustfmt * fix(zebrad/test): test function return type * tests(rpc): print server output in assert * fix(rpc): fix acceptance test looking for string in `build` field * fix(rpc): reduce the number of acceptable characters in version output Co-authored-by: teor <teor@riseup.net> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
c176e2a423
commit
675fa3621d
|
|
@ -5915,6 +5915,7 @@ dependencies = [
|
||||||
"sentry",
|
"sentry",
|
||||||
"sentry-tracing",
|
"sentry-tracing",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ abscissa_core = { version = "0.5", features = ["testing"] }
|
||||||
once_cell = "1.9"
|
once_cell = "1.9"
|
||||||
regex = "1.5.4"
|
regex = "1.5.4"
|
||||||
semver = "1.0.6"
|
semver = "1.0.6"
|
||||||
|
serde_json = "1.0"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
tokio = { version = "1.16.1", features = ["full", "test-util"] }
|
tokio = { version = "1.16.1", features = ["full", "test-util"] }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,9 @@ impl StartCmd {
|
||||||
.buffer(mempool::downloads::MAX_INBOUND_CONCURRENCY)
|
.buffer(mempool::downloads::MAX_INBOUND_CONCURRENCY)
|
||||||
.service(mempool);
|
.service(mempool);
|
||||||
|
|
||||||
|
// Launch RPC server
|
||||||
|
let rpc_task_handle = RpcServer::spawn(config.rpc, app_version().to_string());
|
||||||
|
|
||||||
let setup_data = InboundSetupData {
|
let setup_data = InboundSetupData {
|
||||||
address_book,
|
address_book,
|
||||||
block_download_peer_set: peer_set.clone(),
|
block_download_peer_set: peer_set.clone(),
|
||||||
|
|
@ -197,19 +200,17 @@ impl StartCmd {
|
||||||
.in_current_span(),
|
.in_current_span(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rpc_task_handle = RpcServer::spawn(config.rpc, app_version().to_string());
|
|
||||||
|
|
||||||
info!("spawned initial Zebra tasks");
|
info!("spawned initial Zebra tasks");
|
||||||
|
|
||||||
// TODO: put tasks into an ongoing FuturesUnordered and a startup FuturesUnordered?
|
// TODO: put tasks into an ongoing FuturesUnordered and a startup FuturesUnordered?
|
||||||
|
|
||||||
// ongoing tasks
|
// ongoing tasks
|
||||||
|
pin!(rpc_task_handle);
|
||||||
pin!(syncer_task_handle);
|
pin!(syncer_task_handle);
|
||||||
pin!(mempool_crawler_task_handle);
|
pin!(mempool_crawler_task_handle);
|
||||||
pin!(mempool_queue_checker_task_handle);
|
pin!(mempool_queue_checker_task_handle);
|
||||||
pin!(tx_gossip_task_handle);
|
pin!(tx_gossip_task_handle);
|
||||||
pin!(progress_task_handle);
|
pin!(progress_task_handle);
|
||||||
pin!(rpc_task_handle);
|
|
||||||
|
|
||||||
// startup tasks
|
// startup tasks
|
||||||
let groth16_download_handle_fused = (&mut groth16_download_handle).fuse();
|
let groth16_download_handle_fused = (&mut groth16_download_handle).fuse();
|
||||||
|
|
@ -220,6 +221,13 @@ impl StartCmd {
|
||||||
let mut exit_when_task_finishes = true;
|
let mut exit_when_task_finishes = true;
|
||||||
|
|
||||||
let result = select! {
|
let result = select! {
|
||||||
|
rpc_result = &mut rpc_task_handle => {
|
||||||
|
rpc_result
|
||||||
|
.expect("unexpected panic in the rpc task");
|
||||||
|
info!("rpc task exited");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
sync_result = &mut syncer_task_handle => sync_result
|
sync_result = &mut syncer_task_handle => sync_result
|
||||||
.expect("unexpected panic in the syncer task")
|
.expect("unexpected panic in the syncer task")
|
||||||
.map(|_| info!("syncer task exited")),
|
.map(|_| info!("syncer task exited")),
|
||||||
|
|
@ -251,13 +259,6 @@ impl StartCmd {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc_result = &mut rpc_task_handle => {
|
|
||||||
rpc_result
|
|
||||||
.expect("unexpected panic in the rpc task");
|
|
||||||
info!("rpc task exited");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlike other tasks, we expect the download task to finish while Zebra is running.
|
// Unlike other tasks, we expect the download task to finish while Zebra is running.
|
||||||
groth16_download_result = &mut groth16_download_handle_fused => {
|
groth16_download_result = &mut groth16_download_handle_fused => {
|
||||||
groth16_download_result
|
groth16_download_result
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,11 @@ use zebrad::{
|
||||||
///
|
///
|
||||||
/// Previously, this value was 3 seconds, which caused rare
|
/// Previously, this value was 3 seconds, which caused rare
|
||||||
/// metrics or tracing test failures in Windows CI.
|
/// metrics or tracing test failures in Windows CI.
|
||||||
const LAUNCH_DELAY: Duration = Duration::from_secs(10);
|
const LAUNCH_DELAY: Duration = Duration::from_secs(15);
|
||||||
|
|
||||||
|
/// The amount of time we wait between launching two
|
||||||
|
/// conflicting nodes.
|
||||||
|
const BETWEEN_NODES_DELAY: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
/// Returns a config with:
|
/// Returns a config with:
|
||||||
/// - a Zcash listener on an unused port on IPv4 localhost, and
|
/// - a Zcash listener on an unused port on IPv4 localhost, and
|
||||||
|
|
@ -1540,7 +1544,74 @@ async fn tracing_endpoint() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: RPC endpoint and port conflict tests (#3165)
|
#[tokio::test]
|
||||||
|
async fn rpc_endpoint() -> Result<()> {
|
||||||
|
use hyper::{body::to_bytes, Body, Client, Method, Request};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
zebra_test::init();
|
||||||
|
if zebra_test::net::zebra_skip_network_tests() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// [Note on port conflict](#Note on port conflict)
|
||||||
|
let port = random_known_port();
|
||||||
|
let endpoint = format!("127.0.0.1:{}", port);
|
||||||
|
let url = format!("http://{}", endpoint);
|
||||||
|
|
||||||
|
// Write a configuration that has RPC listen_addr set
|
||||||
|
let mut config = default_test_config()?;
|
||||||
|
config.rpc.listen_addr = Some(endpoint.parse().unwrap());
|
||||||
|
|
||||||
|
let dir = testdir()?.with_config(&mut config)?;
|
||||||
|
let mut child = dir.spawn_child(&["start"])?;
|
||||||
|
|
||||||
|
// Wait until port is open.
|
||||||
|
child.expect_stdout_line_matches(format!("Opened RPC endpoint at {}", endpoint).as_str())?;
|
||||||
|
|
||||||
|
// Create an http client
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
// Create a request to call `getinfo` RPC method
|
||||||
|
let req = Request::builder()
|
||||||
|
.method(Method::POST)
|
||||||
|
.uri(url)
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.body(Body::from(
|
||||||
|
r#"{"jsonrpc":"1.0","method":"getinfo","params":[],"id":123}"#,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// Make the call to the RPC endpoint
|
||||||
|
let res = client.request(req).await?;
|
||||||
|
|
||||||
|
// Test rpc endpoint response
|
||||||
|
assert!(res.status().is_success());
|
||||||
|
|
||||||
|
let body = to_bytes(res).await;
|
||||||
|
let (body, mut child) = child.kill_on_error(body)?;
|
||||||
|
|
||||||
|
let parsed: Value = serde_json::from_slice(&body)?;
|
||||||
|
|
||||||
|
// Check that we have at least 4 characters in the `build` field.
|
||||||
|
let build = parsed["result"]["build"].as_str().unwrap();
|
||||||
|
assert!(build.len() > 4, "Got {}", build);
|
||||||
|
|
||||||
|
// Check that the `subversion` field has "Zebra" in it.
|
||||||
|
let subversion = parsed["result"]["subversion"].as_str().unwrap();
|
||||||
|
assert!(subversion.contains("Zebra"), "Got {}", subversion);
|
||||||
|
|
||||||
|
child.kill()?;
|
||||||
|
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
let output = output.assert_failure()?;
|
||||||
|
|
||||||
|
// [Note on port conflict](#Note on port conflict)
|
||||||
|
output
|
||||||
|
.assert_was_killed()
|
||||||
|
.wrap_err("Possible port conflict. Are there other acceptance tests running?")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Launch `zebrad` with an RPC port, and make sure `lightwalletd` works with Zebra.
|
/// Launch `zebrad` with an RPC port, and make sure `lightwalletd` works with Zebra.
|
||||||
///
|
///
|
||||||
|
|
@ -1736,6 +1807,34 @@ fn zebra_tracing_conflict() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start 2 zebrad nodes using the same RPC listener port, but different
|
||||||
|
/// state directories and Zcash listener ports. The first node should get
|
||||||
|
/// exclusive use of the port. The second node will panic.
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn zebra_rpc_conflict() -> Result<()> {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
// [Note on port conflict](#Note on port conflict)
|
||||||
|
let port = random_known_port();
|
||||||
|
let listen_addr = format!("127.0.0.1:{}", port);
|
||||||
|
|
||||||
|
// Write a configuration that has our created RPC listen_addr
|
||||||
|
let mut config = default_test_config()?;
|
||||||
|
config.rpc.listen_addr = Some(listen_addr.parse().unwrap());
|
||||||
|
let dir1 = testdir()?.with_config(&mut config)?;
|
||||||
|
let regex1 = regex::escape(&format!(r"Opened RPC endpoint at {}", listen_addr));
|
||||||
|
|
||||||
|
// From another folder create a configuration with the same endpoint.
|
||||||
|
// `rpc.listen_addr` will be the same in the 2 nodes.
|
||||||
|
// But they will have different Zcash listeners (auto port) and states (ephemeral)
|
||||||
|
let dir2 = testdir()?.with_config(&mut config)?;
|
||||||
|
|
||||||
|
check_config_conflict(dir1, regex1.as_str(), dir2, "Unable to start RPC server")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Start 2 zebrad nodes using the same state directory, but different Zcash
|
/// Start 2 zebrad nodes using the same state directory, but different Zcash
|
||||||
/// listener ports. The first node should get exclusive access to the database.
|
/// listener ports. The first node should get exclusive access to the database.
|
||||||
/// The second node will panic with the Zcash state conflict hint added in #1535.
|
/// The second node will panic with the Zcash state conflict hint added in #1535.
|
||||||
|
|
@ -1798,6 +1897,9 @@ where
|
||||||
// Wait until node1 has used the conflicting resource.
|
// Wait until node1 has used the conflicting resource.
|
||||||
node1.expect_stdout_line_matches(first_stdout_regex)?;
|
node1.expect_stdout_line_matches(first_stdout_regex)?;
|
||||||
|
|
||||||
|
// Wait a bit before launching the second node.
|
||||||
|
std::thread::sleep(BETWEEN_NODES_DELAY);
|
||||||
|
|
||||||
// Spawn the second node
|
// Spawn the second node
|
||||||
let node2 = second_dir.spawn_child(&["start"]);
|
let node2 = second_dir.spawn_child(&["start"]);
|
||||||
let (node2, mut node1) = node1.kill_on_error(node2)?;
|
let (node2, mut node1) = node1.kill_on_error(node2)?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue