Add regression tests for boot_node (#2749)

## Issue Addressed
Resolves #2602

## Proposed Changes

*Note: For a review it might help to look at the individual commits.*

### `boot_node`
Add support for the flags `dump-config` and `immediate-shutdown`. For `immediate-shutdown` the actual behavior could be described as `dump-config-and-exit`.

Both flags are handled in `boot_node::main`, which appears to be the simplest approach.

### `boot_node` regression tests
Added in `lighthouse/tests/boot_node.rs`.

### `CommandLineTestExec`
Factors out boilerplate related to CLI tests. It's used in the regression tests for `boot_node`, `beacon_node` and `validator_client`.

## Open TODO
Add tests for `boot_node` flags `enable-enr-auto-update` and `disable-packet-filter`. They end up in [`Discv5Config`](9ed2cba6bc/boot_node/src/config.rs (L29)), which doesn't support serde (de)serialization.

I haven't found a workaround - guidance would be appreciated.
This commit is contained in:
mooori
2021-11-08 01:37:58 +00:00
parent fbafe416d1
commit d01fe02824
11 changed files with 473 additions and 292 deletions

View File

@@ -1,22 +1,16 @@
use validator_client::Config;
use crate::exec::CommandLineTestExec;
use bls::{Keypair, PublicKeyBytes};
use serde_json::from_reader;
use std::fs::File;
use std::io::Write;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::str::from_utf8;
use std::process::Command;
use std::string::ToString;
use tempfile::TempDir;
const VALIDATOR_CMD: &str = "validator_client";
const CONFIG_NAME: &str = "vc_dump.json";
const DUMP_CONFIG_CMD: &str = "dump-config";
const IMMEDIATE_SHUTDOWN_CMD: &str = "immediate-shutdown";
/// Returns the `lighthouse validator_client --immediate-shutdown` command.
/// Returns the `lighthouse validator_client` command.
fn base_cmd() -> Command {
let lighthouse_bin = env!("CARGO_BIN_EXE_lighthouse");
let path = lighthouse_bin
@@ -24,25 +18,10 @@ fn base_cmd() -> Command {
.expect("should parse CARGO_TARGET_DIR");
let mut cmd = Command::new(path);
cmd.arg(VALIDATOR_CMD)
.arg(format!("--{}", IMMEDIATE_SHUTDOWN_CMD));
cmd.arg("validator_client");
cmd
}
/// Executes a `Command`, returning a `Result` based upon the success exit code of the command.
fn output_result(cmd: &mut Command) -> Result<Output, String> {
let output = cmd.output().expect("should run command");
if output.status.success() {
Ok(output)
} else {
Err(from_utf8(&output.stderr)
.expect("stderr is not utf8")
.to_string())
}
}
// Wrapper around `Command` for easier Command Line Testing.
struct CommandLineTest {
cmd: Command,
@@ -52,76 +31,13 @@ impl CommandLineTest {
let base_cmd = base_cmd();
CommandLineTest { cmd: base_cmd }
}
fn flag(mut self, flag: &str, value: Option<&str>) -> Self {
// Build the command by adding the flag and any values.
self.cmd.arg(format!("--{}", flag));
if let Some(value) = value {
self.cmd.arg(value);
}
self
}
fn run(&mut self) -> CompletedTest {
// Setup temp directories.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --datadir <temp_dir> --dump-config <temp_path> to cmd.
self.cmd
.arg("--datadir")
.arg(tmp_dir.path().as_os_str())
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str());
// Run the command.
let _output = output_result(&mut self.cmd).expect("Unable to run command");
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
}
// In order to test custom validator and secrets directory flags,
// datadir cannot be defined.
fn run_with_no_datadir(&mut self) -> CompletedTest {
// Setup temp directories
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --dump-config <temp_path> to cmd.
self.cmd
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str());
// Run the command.
let _output = output_result(&mut self.cmd).expect("Unable to run command");
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
}
}
struct CompletedTest {
config: Config,
dir: TempDir,
}
impl CompletedTest {
fn with_config<F: Fn(&Config)>(self, func: F) {
func(&self.config);
}
fn with_config_and_dir<F: Fn(&Config, &TempDir)>(self, func: F) {
func(&self.config, &self.dir);
impl CommandLineTestExec for CommandLineTest {
type Config = Config;
fn cmd_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}