test(lightwalletd): add a lightwalletd integration test (#3619)

* test(lightwalletd): add a lightwalletd integration test

* fix(lightwalletd): ignore the lightwalletd integration test by default
This commit is contained in:
teor 2022-02-23 21:52:30 +10:00 committed by GitHub
parent 970f88ffcb
commit 78a05bcaf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 152 additions and 1 deletions

View File

@ -27,7 +27,10 @@ use color_eyre::{
};
use tempfile::TempDir;
use std::{collections::HashSet, convert::TryInto, env, path::Path, path::PathBuf, time::Duration};
use std::{
collections::HashSet, convert::TryInto, env, net::SocketAddr, path::Path, path::PathBuf,
time::Duration,
};
use zebra_chain::{
block::Height,
@ -1452,6 +1455,154 @@ async fn tracing_endpoint() -> Result<()> {
Ok(())
}
// TODO: RPC endpoint and port conflict tests (#3165)
/// Launch `zebrad` with an RPC port, and make sure `lightwalletd` works with Zebra.
///
/// This test doesn't work on Windows, and it is ignored by default on other platforms.
#[test]
#[ignore]
#[cfg(not(target_os = "windows"))]
fn lightwalletd_integration() -> Result<()> {
zebra_test::init();
// Launch zebrad
// [Note on port conflict](#Note on port conflict)
let listen_port = random_known_port();
let listen_ip = "127.0.0.1".parse().expect("hard-coded IP is valid");
let listen_addr = SocketAddr::new(listen_ip, listen_port);
// Write a configuration that has the rpc listen_addr option set
// TODO: split this config into another function?
let mut config = default_test_config()?;
config.rpc.listen_addr = Some(listen_addr);
let dir = testdir()?.with_config(&mut config)?;
let mut zebrad = dir.spawn_child(&["start"])?.with_timeout(LAUNCH_DELAY);
// Wait until `zebrad` has opened the RPC endpoint
zebrad
.expect_stdout_line_matches(format!("Opened RPC endpoint at {}", listen_addr).as_str())?;
// Launch lightwalletd
// TODO: split this into another function
// Write a fake zcashd configuration that has the rpcbind and rpcport options set
// TODO: split this into another function
let dir = testdir()?;
let lightwalletd_config = format!(
"\
rpcbind={}\n\
rpcport={}\n\
",
listen_ip, listen_port
);
use std::fs;
use std::io::Write;
let path = dir.path().to_owned();
// TODO: split this into another function
if !config.state.ephemeral {
let cache_dir = path.join("state");
fs::create_dir_all(&cache_dir)?;
} else {
fs::create_dir_all(&path)?;
}
let config_file = path.join("lightwalletd-zcash.conf");
fs::File::create(config_file)?.write_all(lightwalletd_config.as_bytes())?;
let result = {
let default_config_path = path.join("lightwalletd-zcash.conf");
assert!(
default_config_path.exists(),
"lightwalletd requires a config"
);
dir.spawn_child_with_command(
"lightwalletd",
&[
// the fake zcashd conf we just wrote
"--zcash-conf-path",
default_config_path
.as_path()
.to_str()
.expect("Path is valid Unicode"),
// the lightwalletd cache directory
//
// TODO: create a sub-directory for lightwalletd
"--data-dir",
path.to_str().expect("Path is valid Unicode"),
// log to standard output
"--log-file",
"/dev/stdout",
// randomise wallet client ports
"--grpc-bind-addr",
"127.0.0.1:0",
"--http-bind-addr",
"127.0.0.1:0",
// don't require a TLS certificate
"--no-tls-very-insecure",
],
)
};
let (lightwalletd, zebrad) = zebrad.kill_on_error(result)?;
let mut lightwalletd = lightwalletd.with_timeout(LAUNCH_DELAY);
// Wait until `lightwalletd` has launched
let result = lightwalletd.expect_stdout_line_matches("Starting gRPC server");
let (_, zebrad) = zebrad.kill_on_error(result)?;
// Check that `lightwalletd` is calling the expected Zebra RPCs
//
// TODO: add extra checks when we add new Zebra RPCs
// get_blockchain_info
let result = lightwalletd.expect_stdout_line_matches("Got sapling height");
let (_, zebrad) = zebrad.kill_on_error(result)?;
let result = lightwalletd.expect_stdout_line_matches("Found 0 blocks in cache");
let (_, zebrad) = zebrad.kill_on_error(result)?;
// Check that `lightwalletd` got to the first unimplemented Zebra RPC
//
// TODO: update the missing method name when we add a new Zebra RPC
let result = lightwalletd
.expect_stdout_line_matches("Method not found.*error zcashd getbestblockhash rpc");
let (_, zebrad) = zebrad.kill_on_error(result)?;
let result = lightwalletd.expect_stdout_line_matches(
"Lightwalletd died with a Fatal error. Check logfile for details",
);
let (_, zebrad) = zebrad.kill_on_error(result)?;
// Cleanup both processes
let result = lightwalletd.kill();
let (_, mut zebrad) = zebrad.kill_on_error(result)?;
zebrad.kill()?;
let lightwalletd_output = lightwalletd.wait_with_output()?.assert_failure()?;
let zebrad_output = zebrad.wait_with_output()?.assert_failure()?;
// If the test fails here, see the [note on port conflict](#Note on port conflict)
//
// TODO: change lightwalletd to `assert_was_killed` when enough RPCs are implemented
lightwalletd_output
.assert_was_not_killed()
.wrap_err("Possible port conflict. Are there other acceptance tests running?")?;
zebrad_output
.assert_was_killed()
.wrap_err("Possible port conflict. Are there other acceptance tests running?")?;
Ok(())
}
/// Test will start 2 zebrad nodes one after the other using the same Zcash listener.
/// It is expected that the first node spawned will get exclusive use of the port.
/// The second node will panic with the Zcash listener conflict hint added in #1535.