mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 09:16:00 +00:00
Progress with testing
This commit is contained in:
@@ -181,20 +181,20 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
/// The CLI arguments are parsed into this struct before running the application. This step of
|
/// The CLI arguments are parsed into this struct before running the application. This step of
|
||||||
/// indirection allows for testing the underlying logic without needing to parse CLI arguments.
|
/// indirection allows for testing the underlying logic without needing to parse CLI arguments.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CreateConfig {
|
pub struct CreateConfig {
|
||||||
output_path: PathBuf,
|
pub output_path: PathBuf,
|
||||||
first_index: u32,
|
pub first_index: u32,
|
||||||
count: u32,
|
pub count: u32,
|
||||||
deposit_gwei: u64,
|
pub deposit_gwei: u64,
|
||||||
mnemonic_path: Option<PathBuf>,
|
pub mnemonic_path: Option<PathBuf>,
|
||||||
stdin_inputs: bool,
|
pub stdin_inputs: bool,
|
||||||
disable_deposits: bool,
|
pub disable_deposits: bool,
|
||||||
specify_voting_keystore_password: bool,
|
pub specify_voting_keystore_password: bool,
|
||||||
eth1_withdrawal_address: Option<Address>,
|
pub eth1_withdrawal_address: Option<Address>,
|
||||||
builder_proposals: bool,
|
pub builder_proposals: bool,
|
||||||
fee_recipient: Option<Address>,
|
pub fee_recipient: Option<Address>,
|
||||||
gas_limit: Option<u64>,
|
pub gas_limit: Option<u64>,
|
||||||
bn_url: Option<SensitiveUrl>,
|
pub bn_url: Option<SensitiveUrl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateConfig {
|
impl CreateConfig {
|
||||||
@@ -518,7 +518,7 @@ fn write_to_json_file<P: AsRef<Path>, S: Serialize>(path: P, contents: &S) -> Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use eth2_network_config::Eth2NetworkConfig;
|
use eth2_network_config::Eth2NetworkConfig;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -530,7 +530,7 @@ mod tests {
|
|||||||
|
|
||||||
const TEST_VECTOR_DEPOSIT_CLI_VERSION: &str = "2.3.0";
|
const TEST_VECTOR_DEPOSIT_CLI_VERSION: &str = "2.3.0";
|
||||||
|
|
||||||
struct TestBuilder {
|
pub struct TestBuilder {
|
||||||
spec: ChainSpec,
|
spec: ChainSpec,
|
||||||
output_dir: TempDir,
|
output_dir: TempDir,
|
||||||
mnemonic_dir: TempDir,
|
mnemonic_dir: TempDir,
|
||||||
@@ -544,7 +544,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TestBuilder {
|
impl TestBuilder {
|
||||||
fn new(spec: ChainSpec) -> Self {
|
pub fn new(spec: ChainSpec) -> Self {
|
||||||
let output_dir = tempdir().unwrap();
|
let output_dir = tempdir().unwrap();
|
||||||
let mnemonic_dir = tempdir().unwrap();
|
let mnemonic_dir = tempdir().unwrap();
|
||||||
let mnemonic_path = mnemonic_dir.path().join("mnemonic");
|
let mnemonic_path = mnemonic_dir.path().join("mnemonic");
|
||||||
@@ -578,12 +578,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mutate_config<F: Fn(&mut CreateConfig)>(mut self, func: F) -> Self {
|
pub fn mutate_config<F: Fn(&mut CreateConfig)>(mut self, func: F) -> Self {
|
||||||
func(&mut self.config);
|
func(&mut self.config);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_test(self) -> TestResult {
|
pub async fn run_test(self) -> TestResult {
|
||||||
let Self {
|
let Self {
|
||||||
spec,
|
spec,
|
||||||
output_dir,
|
output_dir,
|
||||||
@@ -686,12 +686,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] // Use the `assert_ok` or `assert_err` fns to "use" this value.
|
#[must_use] // Use the `assert_ok` or `assert_err` fns to "use" this value.
|
||||||
struct TestResult {
|
pub struct TestResult {
|
||||||
result: Result<(), String>,
|
pub result: Result<(), String>,
|
||||||
output_dir: TempDir,
|
pub output_dir: TempDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestResult {
|
impl TestResult {
|
||||||
|
pub fn validators_file_path(&self) -> PathBuf {
|
||||||
|
self.output_dir.path().join(VALIDATORS_FILENAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validators(&self) -> Vec<ValidatorSpecification> {
|
||||||
|
let contents = fs::read_to_string(self.validators_file_path()).unwrap();
|
||||||
|
serde_json::from_str(&contents).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn assert_ok(self) {
|
fn assert_ok(self) {
|
||||||
assert_eq!(self.result, Ok(()))
|
assert_eq!(self.result, Ok(()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,17 +225,20 @@ async fn run<'a>(config: ImportConfig) -> Result<(), String> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::validators::create_validators::tests::TestBuilder as CreateTestBuilder;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
use validator_client::http_api::test_utils::ApiTester;
|
use validator_client::http_api::test_utils::ApiTester;
|
||||||
|
|
||||||
const VALIDATORS_FILE_NAME: &str = "validators.json";
|
|
||||||
const VC_TOKEN_FILE_NAME: &str = "vc_token.json";
|
const VC_TOKEN_FILE_NAME: &str = "vc_token.json";
|
||||||
|
|
||||||
struct TestBuilder {
|
struct TestBuilder {
|
||||||
config: ImportConfig,
|
import_config: ImportConfig,
|
||||||
vc: ApiTester,
|
vc: ApiTester,
|
||||||
dir: TempDir,
|
dir: TempDir,
|
||||||
|
/// Holds the temp directory owned by the `CreateTestBuilder` so it doesn't get cleaned-up
|
||||||
|
/// before we can read it.
|
||||||
|
create_dir: Option<TempDir>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestBuilder {
|
impl TestBuilder {
|
||||||
@@ -246,20 +249,128 @@ mod test {
|
|||||||
fs::write(&vc_token_path, &vc.api_token).unwrap();
|
fs::write(&vc_token_path, &vc.api_token).unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config: ImportConfig {
|
import_config: ImportConfig {
|
||||||
validators_file_path: dir.path().join(VALIDATORS_FILE_NAME),
|
// This field will be overwritten later on.
|
||||||
|
validators_file_path: dir.path().into(),
|
||||||
vc_url: vc.url.clone(),
|
vc_url: vc.url.clone(),
|
||||||
vc_token_path,
|
vc_token_path,
|
||||||
ignore_duplicates: false,
|
ignore_duplicates: false,
|
||||||
},
|
},
|
||||||
vc,
|
vc,
|
||||||
dir,
|
dir,
|
||||||
|
create_dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_validators(mut self, count: u32, first_index: u32) -> Self {
|
||||||
|
let create_result = CreateTestBuilder::default()
|
||||||
|
.mutate_config(|config| {
|
||||||
|
config.count = count;
|
||||||
|
config.first_index = first_index;
|
||||||
|
})
|
||||||
|
.run_test()
|
||||||
|
.await;
|
||||||
|
assert!(
|
||||||
|
create_result.result.is_ok(),
|
||||||
|
"precondition: validators are created"
|
||||||
|
);
|
||||||
|
self.import_config.validators_file_path = create_result.validators_file_path();
|
||||||
|
self.create_dir = Some(create_result.output_dir);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_test(self) -> TestResult {
|
||||||
|
let result = run(self.import_config.clone()).await;
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
let local_validators: Vec<ValidatorSpecification> = {
|
||||||
|
let contents =
|
||||||
|
fs::read_to_string(&self.import_config.validators_file_path).unwrap();
|
||||||
|
serde_json::from_str(&contents).unwrap()
|
||||||
|
};
|
||||||
|
let list_keystores_response = self.vc.client.get_keystores().await.unwrap().data;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
local_validators.len(),
|
||||||
|
list_keystores_response.len(),
|
||||||
|
"vc should have exactly the number of validators imported"
|
||||||
|
);
|
||||||
|
|
||||||
|
for local_validator in &local_validators {
|
||||||
|
let local_keystore = &local_validator.voting_keystore.0;
|
||||||
|
let local_pubkey = local_keystore.public_key().unwrap().into();
|
||||||
|
let remote_validator = list_keystores_response
|
||||||
|
.iter()
|
||||||
|
.find(|validator| validator.validating_pubkey == local_pubkey)
|
||||||
|
.expect("validator must exist on VC");
|
||||||
|
assert_eq!(&remote_validator.derivation_path, &local_keystore.path());
|
||||||
|
// It's not immediately clear why Lighthouse returns `None` rather than
|
||||||
|
// `Some(false)` here, I would expect the latter to be the most accurate.
|
||||||
|
// However, it doesn't seem like a big deal.
|
||||||
|
assert_eq!(remote_validator.readonly, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult { result }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use] // Use the `assert_ok` or `assert_err` fns to "use" this value.
|
||||||
|
struct TestResult {
|
||||||
|
result: Result<(), String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestResult {
|
||||||
|
fn assert_ok(self) {
|
||||||
|
assert_eq!(self.result, Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_err(self) {
|
||||||
|
assert!(self.result.is_err())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn blah() {
|
async fn create_one_validator() {
|
||||||
TestBuilder::new().await;
|
TestBuilder::new()
|
||||||
|
.await
|
||||||
|
.create_validators(1, 0)
|
||||||
|
.await
|
||||||
|
.run_test()
|
||||||
|
.await
|
||||||
|
.assert_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_three_validators() {
|
||||||
|
TestBuilder::new()
|
||||||
|
.await
|
||||||
|
.create_validators(3, 0)
|
||||||
|
.await
|
||||||
|
.run_test()
|
||||||
|
.await
|
||||||
|
.assert_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_one_validator_with_offset() {
|
||||||
|
TestBuilder::new()
|
||||||
|
.await
|
||||||
|
.create_validators(1, 42)
|
||||||
|
.await
|
||||||
|
.run_test()
|
||||||
|
.await
|
||||||
|
.assert_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_three_validators_with_offset() {
|
||||||
|
TestBuilder::new()
|
||||||
|
.await
|
||||||
|
.create_validators(3, 1337)
|
||||||
|
.await
|
||||||
|
.run_test()
|
||||||
|
.await
|
||||||
|
.assert_ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user