diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 6625a05968..5d37e7a928 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -10735,6 +10735,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "staking-miner" version = "0.9.17" dependencies = [ + "assert_cmd", "clap", "frame-election-provider-support", "frame-support", diff --git a/polkadot/utils/staking-miner/Cargo.toml b/polkadot/utils/staking-miner/Cargo.toml index 811772e265..ce936af930 100644 --- a/polkadot/utils/staking-miner/Cargo.toml +++ b/polkadot/utils/staking-miner/Cargo.toml @@ -44,4 +44,4 @@ westend-runtime = { path = "../../runtime/westend" } sub-tokens = { git = "https://github.com/paritytech/substrate-debug-kit", branch = "master" } [dev-dependencies] -sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } +assert_cmd = "2.0.2" diff --git a/polkadot/utils/staking-miner/src/main.rs b/polkadot/utils/staking-miner/src/main.rs index 449a643e22..f7974c4d8b 100644 --- a/polkadot/utils/staking-miner/src/main.rs +++ b/polkadot/utils/staking-miner/src/main.rs @@ -277,6 +277,7 @@ impl std::fmt::Display for Error { } #[derive(Debug, Clone, Parser)] +#[cfg_attr(test, derive(PartialEq))] enum Command { /// Monitor for the phase being signed, then compute. Monitor(MonitorConfig), @@ -287,7 +288,8 @@ enum Command { } #[derive(Debug, Clone, Parser)] -enum Solvers { +#[cfg_attr(test, derive(PartialEq))] +enum Solver { SeqPhragmen { #[clap(long, default_value = "10")] iterations: usize, @@ -306,6 +308,7 @@ frame_support::parameter_types! { } #[derive(Debug, Clone, Parser)] +#[cfg_attr(test, derive(PartialEq))] struct MonitorConfig { /// They type of event to listen to. /// @@ -317,10 +320,11 @@ struct MonitorConfig { /// The solver algorithm to use. #[clap(subcommand)] - solver: Solvers, + solver: Solver, } #[derive(Debug, Clone, Parser)] +#[cfg_attr(test, derive(PartialEq))] struct EmergencySolutionConfig { /// The block hash at which scraping happens. If none is provided, the latest head is used. #[clap(long)] @@ -328,13 +332,14 @@ struct EmergencySolutionConfig { /// The solver algorithm to use. #[clap(subcommand)] - solver: Solvers, + solver: Solver, /// The number of top backed winners to take. All are taken, if not provided. take: Option, } #[derive(Debug, Clone, Parser)] +#[cfg_attr(test, derive(PartialEq))] struct DryRunConfig { /// The block hash at which scraping happens. If none is provided, the latest head is used. #[clap(long)] @@ -342,7 +347,7 @@ struct DryRunConfig { /// The solver algorithm to use. #[clap(subcommand)] - solver: Solvers, + solver: Solver, /// Force create a new snapshot, else expect one to exist onchain. #[clap(long)] @@ -350,7 +355,9 @@ struct DryRunConfig { } #[derive(Debug, Clone, Parser)] -struct SharedConfig { +#[cfg_attr(test, derive(PartialEq))] +#[clap(author, version, about)] +struct Opt { /// The `ws` node to connect to. #[clap(long, short, default_value = DEFAULT_URI, env = "URI")] uri: String, @@ -364,13 +371,6 @@ struct SharedConfig { /// configured, it might re-try and lose funds through transaction fees/deposits. #[clap(long, short, env = "SEED")] seed_or_path: String, -} - -#[derive(Debug, Clone, Parser)] -struct Opt { - /// The `ws` node to connect to. - #[clap(flatten)] - shared: SharedConfig, #[clap(subcommand)] command: Command, @@ -433,7 +433,7 @@ where /// Mine a solution with the given `solver`. fn mine_with( - solver: &Solvers, + solver: &Solver, ext: &mut Ext, do_feasibility: bool, ) -> Result<(EPM::RawSolution>, u32), Error> @@ -444,7 +444,7 @@ where use frame_election_provider_support::{PhragMMS, SequentialPhragmen}; match solver { - Solvers::SeqPhragmen { iterations } => { + Solver::SeqPhragmen { iterations } => { BalanceIterations::set(*iterations); mine_solution::< T, @@ -455,7 +455,7 @@ where >, >(ext, do_feasibility) }, - Solvers::PhragMMS { iterations } => { + Solver::PhragMMS { iterations } => { BalanceIterations::set(*iterations); mine_solution::< T, @@ -530,11 +530,11 @@ pub(crate) async fn check_versions( async fn main() { fmt().with_env_filter(EnvFilter::from_default_env()).init(); - let Opt { shared, command } = Opt::parse(); - log::debug!(target: LOG_TARGET, "attempting to connect to {:?}", shared.uri); + let Opt { uri, seed_or_path, command } = Opt::parse(); + log::debug!(target: LOG_TARGET, "attempting to connect to {:?}", uri); let rpc = loop { - match SharedRpcClient::new(&shared.uri).await { + match SharedRpcClient::new(&uri).await { Ok(client) => break client, Err(why) => { log::warn!( @@ -597,7 +597,7 @@ async fn main() { }; let signer_account = any_runtime! { - signer::signer_uri_from_string::(&shared.seed_or_path, &rpc) + signer::signer_uri_from_string::(&seed_or_path, &rpc) .await .expect("Provided account is invalid, terminating.") }; @@ -644,4 +644,89 @@ mod tests { assert_eq!(polkadot_version.spec_name, "polkadot".into()); assert_eq!(kusama_version.spec_name, "kusama".into()); } + + #[test] + fn cli_monitor_works() { + let opt = Opt::try_parse_from([ + env!("CARGO_PKG_NAME"), + "--uri", + "hi", + "--seed-or-path", + "//Alice", + "monitor", + "--listen", + "head", + "seq-phragmen", + ]) + .unwrap(); + + assert_eq!( + opt, + Opt { + uri: "hi".to_string(), + seed_or_path: "//Alice".to_string(), + command: Command::Monitor(MonitorConfig { + listen: "head".to_string(), + solver: Solver::SeqPhragmen { iterations: 10 } + }), + } + ); + } + + #[test] + fn cli_dry_run_works() { + let opt = Opt::try_parse_from([ + env!("CARGO_PKG_NAME"), + "--uri", + "hi", + "--seed-or-path", + "//Alice", + "dry-run", + "phrag-mms", + ]) + .unwrap(); + + assert_eq!( + opt, + Opt { + uri: "hi".to_string(), + seed_or_path: "//Alice".to_string(), + command: Command::DryRun(DryRunConfig { + at: None, + solver: Solver::PhragMMS { iterations: 10 }, + force_snapshot: false, + }), + } + ); + } + + #[test] + fn cli_emergency_works() { + let opt = Opt::try_parse_from([ + env!("CARGO_PKG_NAME"), + "--uri", + "hi", + "--seed-or-path", + "//Alice", + "emergency-solution", + "99", + "phrag-mms", + "--iterations", + "1337", + ]) + .unwrap(); + + assert_eq!( + opt, + Opt { + uri: "hi".to_string(), + seed_or_path: "//Alice".to_string(), + command: Command::EmergencySolution(EmergencySolutionConfig { + take: Some(99), + at: None, + solver: Solver::PhragMMS { iterations: 1337 } + }), + } + ); + } } diff --git a/polkadot/utils/staking-miner/tests/cli.rs b/polkadot/utils/staking-miner/tests/cli.rs new file mode 100644 index 0000000000..46f5cf0cb0 --- /dev/null +++ b/polkadot/utils/staking-miner/tests/cli.rs @@ -0,0 +1,12 @@ +use assert_cmd::{cargo::cargo_bin, Command}; + +#[test] +fn cli_version_works() { + let crate_name = env!("CARGO_PKG_NAME"); + let output = Command::new(cargo_bin(crate_name)).arg("--version").output().unwrap(); + + assert!(output.status.success(), "command returned with non-success exit code"); + let version = String::from_utf8_lossy(&output.stdout).trim().to_owned(); + + assert_eq!(version, format!("{} {}", crate_name, env!("CARGO_PKG_VERSION"))); +}