Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests(scanner): Move zebra scanner tests to binary #8659

Merged
merged 19 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6055,7 +6055,10 @@ dependencies = [
"serde",
"serde_json",
"structopt",
"tempfile",
"tokio",
"toml 0.8.14",
"tonic 0.11.0",
"tower",
"tracing",
"tracing-subscriber",
Expand All @@ -6070,6 +6073,7 @@ dependencies = [
"zebra-rpc",
"zebra-state",
"zebra-test",
"zebrad",
]

[[package]]
Expand Down
5 changes: 5 additions & 0 deletions zebra-scan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ ff = "0.13.0"
group = "0.13.0"
jubjub = "0.10.0"
rand = "0.8.5"
tempfile = "3.10.1"
zcash_note_encryption = "0.4.0"
toml = "0.8.13"
tonic = "0.11.0"

zebra-state = { path = "../zebra-state", version = "1.0.0-beta.38", features = ["proptest-impl"] }
zebra-test = { path = "../zebra-test", version = "1.0.0-beta.38" }
zebrad = { path = "../zebrad", version = "1.8.0" }

257 changes: 257 additions & 0 deletions zebra-scan/tests/scanner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
//! `zebra-scanner` binary tests.
use tempfile::TempDir;

use zebra_grpc::scanner::{scanner_client::ScannerClient, Empty};

Check warning on line 4 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Test beta on windows-latest

unused imports: `Empty` and `scanner_client::ScannerClient`

Check warning on line 4 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Test stable on windows-latest

unused imports: `Empty`, `scanner_client::ScannerClient`
use zebra_test::{
args,
command::{Arguments, TestDirExt},
prelude::*,
};

use std::{io::Write, path::Path};

Check warning on line 11 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Test beta on windows-latest

unused import: `io::Write`

Check warning on line 11 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Test stable on windows-latest

unused import: `io::Write`

/// Test the scanner binary with the `--help` flag.
#[test]
fn scanner_help() -> eyre::Result<()> {
let _init_guard = zebra_test::init();

let testdir = testdir()?;

let child = testdir.spawn_child(args!["--help"], false)?;
let output = child.wait_with_output()?;
let output = output.assert_success()?;

output.stdout_line_contains("USAGE:")?;

Ok(())
}

/// Test that the scanner binary starts the scan task.
///
/// This test creates a new zebrad cache dir by using a `zebrad` binary specified in the `CARGO_BIN_EXE_zebrad` env variable.
///
/// To run it locally, one way is:
/// ```
/// export CARGO_BIN_EXE_zebrad="/path/to/zebrad_binary"
/// cargo test scan_binary_starts -- --ignored --nocapture
/// ```
#[ignore]
#[tokio::test]
#[cfg(not(target_os = "windows"))]
async fn scan_binary_starts() -> Result<()> {
arya2 marked this conversation as resolved.
Show resolved Hide resolved
use zebra_scan::tests::ZECPAGES_SAPLING_VIEWING_KEY;

Check failure on line 42 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Build zebra-scan crate

unresolved import `zebra_scan::tests`

let _init_guard = zebra_test::init();

// Create a directory to dump test data into it.
let test_dir = testdir()?;

// Create a config for zebrad with a cache directory inside the test directory.
let mut config = zebrad::config::ZebradConfig::default();
config.state.cache_dir = test_dir.path().join("zebra").to_path_buf();
let config_file = test_dir.path().join("zebrad.toml");
std::fs::File::create(config_file.clone())?.write_all(toml::to_string(&config)?.as_bytes())?;

// Start the node just to make sure a cache is created.
let mut zebrad = test_dir.spawn_child(
args!["-c", config_file.clone().to_str().unwrap(), "start"],
true,
)?;
zebrad.expect_stdout_line_matches("Opened Zebra state cache at .*")?;

// Kill the node now that we have a valid zebra node cache.
zebrad.kill(false)?;
let output = zebrad.wait_with_output()?;

// Make sure the command was killed
output.assert_was_killed()?;

// Create a new directory for the scanner
let test_dir = testdir()?;

// Create arguments for the scanner
let key = serde_json::json!({"key": ZECPAGES_SAPLING_VIEWING_KEY}).to_string();
let listen_addr = "127.0.0.1:8231";
let rpc_listen_addr = "127.0.0.1:8232";
let zebrad_cache_dir = config.state.cache_dir.clone();
let scanning_cache_dir = test_dir.path().join("scanner").to_path_buf();
let args = args![
"--zebrad-cache-dir",
zebrad_cache_dir.to_str().unwrap(),
"--scanning-cache-dir",
scanning_cache_dir.to_str().unwrap(),
"--sapling-keys-to-scan",
key,
"--listen-addr",
listen_addr,
"--zebra-rpc-listen-addr",
rpc_listen_addr
];

// Start the scaner using another test directory.
let mut zebra_scanner = test_dir.spawn_child(args, false)?;

// Check scanner was started.
zebra_scanner.expect_stdout_line_matches("loaded Zebra scanner cache")?;

// Check if the grpc server is up
let mut client = ScannerClient::connect(format!("http://{listen_addr}")).await?;
let request = tonic::Request::new(Empty {});
client.get_info(request).await?;

// Look for 2 scanner notices indicating we are below sapling activation.
zebra_scanner.expect_stdout_line_matches("scanner is waiting for Sapling activation. Current tip: [0-9]{1,4}, Sapling activation: 419200")?;
zebra_scanner.expect_stdout_line_matches("scanner is waiting for Sapling activation. Current tip: [0-9]{1,4}, Sapling activation: 419200")?;

// Kill the scanner.
zebra_scanner.kill(true)?;

// Check that scan task started and the scanning is done.
let output = zebra_scanner.wait_with_output()?;

// Make sure the command was killed
output.assert_was_killed()?;
output.assert_failure()?;

Ok(())
}

/// Test that the scanner can continue scanning where it was left when zebrad restarts.
///
/// Needs a cache state close to the tip. A possible way to run it locally is:
///
/// export ZEBRA_CACHED_STATE_DIR="/path/to/zebra/state"
/// cargo test scan_start_where_left -- --ignored --nocapture
///
/// The test will run zebrad with a key to scan, scan the first few blocks after sapling and then stops.
/// Then it will restart zebrad and check that it resumes scanning where it was left.
#[ignore]
#[tokio::test]
#[cfg(not(target_os = "windows"))]
async fn scan_start_where_left() -> Result<()> {
arya2 marked this conversation as resolved.
Show resolved Hide resolved
use zebra_scan::tests::ZECPAGES_SAPLING_VIEWING_KEY;

Check failure on line 132 in zebra-scan/tests/scanner.rs

View workflow job for this annotation

GitHub Actions / Build zebra-scan crate

unresolved import `zebra_scan::tests`

let _init_guard = zebra_test::init();

let zebrad_cachedir = std::env::var("ZEBRA_CACHED_STATE_DIR")
.expect("please set a ZEBRA_CACHED_STATE_DIR env var with a populated and valid path");
let scanning_cache_dir = testdir()?.path().join("scanner").to_path_buf();

// Create arguments for the scanner
let key = serde_json::json!({"key": ZECPAGES_SAPLING_VIEWING_KEY}).to_string();
let listen_addr = "127.0.0.1:18231";
let rpc_listen_addr = "127.0.0.1:18232";
let args = args![
"--zebrad-cache-dir",
zebrad_cachedir,
"--scanning-cache-dir",
scanning_cache_dir.to_str().unwrap(),
"--sapling-keys-to-scan",
key,
"--listen-addr",
listen_addr,
"--zebra-rpc-listen-addr",
rpc_listen_addr
];

// Start the scaner using another test directory.
let mut zebra_scanner: TestChild<TempDir> = testdir()?.spawn_child(args.clone(), false)?;

// Check scanner was started.
zebra_scanner.expect_stdout_line_matches("loaded Zebra scanner cache")?;

// The first time
zebra_scanner.expect_stdout_line_matches(
r"Scanning the blockchain for key 0, started at block 419200, now at block 420000",
)?;

// Make sure scanner scans a few blocks.
zebra_scanner.expect_stdout_line_matches(
r"Scanning the blockchain for key 0, started at block 419200, now at block 430000",
)?;
zebra_scanner.expect_stdout_line_matches(
r"Scanning the blockchain for key 0, started at block 419200, now at block 440000",
)?;

// Kill the scanner.
zebra_scanner.kill(true)?;

// Check that scan task started and the first scanning is done.
let output = zebra_scanner.wait_with_output()?;

// Make sure the command was killed
output.assert_was_killed()?;
output.assert_failure()?;

// Start the node again with the same arguments.
let mut zebra_scanner: TestChild<TempDir> = testdir()?.spawn_child(args, false)?;

// Resuming message.
zebra_scanner.expect_stdout_line_matches(
"Last scanned height for key number 0 is 439000, resuming at 439001",
)?;
zebra_scanner.expect_stdout_line_matches("loaded Zebra scanner cache")?;

// Start scanning where it was left.
zebra_scanner.expect_stdout_line_matches(
r"Scanning the blockchain for key 0, started at block 439001, now at block 440000",
)?;
zebra_scanner.expect_stdout_line_matches(
r"Scanning the blockchain for key 0, started at block 439001, now at block 450000",
)?;

// Kill the scanner.
zebra_scanner.kill(true)?;

// Check that scan task started and the second scanning is done.
let output = zebra_scanner.wait_with_output()?;

// Make sure the command was killed
output.assert_was_killed()?;
output.assert_failure()?;

Ok(())
}

/// Create a temporary directory for testing `zebra-scanner`.
pub fn testdir() -> eyre::Result<TempDir> {
tempfile::Builder::new()
.prefix("zebra_scanner_tests")
.tempdir()
.map_err(Into::into)
}

/// Extension trait for methods on `tempfile::TempDir` for using it as a test
/// directory for `zebra-scanner`.
pub trait ZebradTestDirExt
where
Self: AsRef<Path> + Sized,
{
// Zebra-scanner methods

/// Spawn `zebra-scanner` (`zebrad` flag = `false`) or `zebrad` (`zebrad` flag = `true`)
/// with `args` as a child process in this test directory.
fn spawn_child(self, args: Arguments, zebrad: bool) -> Result<TestChild<Self>>;
arya2 marked this conversation as resolved.
Show resolved Hide resolved
}

impl<T> ZebradTestDirExt for T
where
Self: TestDirExt + AsRef<Path> + Sized,
{
#[allow(clippy::unwrap_in_result)]
fn spawn_child(self, extra_args: Arguments, zebrad: bool) -> Result<TestChild<Self>> {
let mut args = Arguments::new();

args.merge_with(extra_args);

if zebrad {
self.spawn_child_with_command(
&std::env::var("CARGO_BIN_EXE_zebrad")
.expect("please set CARGO_BIN_EXE_zebrad env var to a zebrad path"),
args,
)
} else {
self.spawn_child_with_command(env!("CARGO_BIN_EXE_zebra-scanner"), args)
}
}
}
Loading
Loading