diff --git a/Cargo.lock b/Cargo.lock
index 4fb67215546..94bc21f2d72 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3875,6 +3875,7 @@ name = "gcli"
version = "1.0.3"
dependencies = [
"anyhow",
+ "async-trait",
"base64 0.21.5",
"clap 4.4.10",
"color-eyre",
diff --git a/gcli/Cargo.toml b/gcli/Cargo.toml
index 13cd71cc71e..7dd19f3665c 100644
--- a/gcli/Cargo.toml
+++ b/gcli/Cargo.toml
@@ -11,13 +11,10 @@ license.workspace = true
homepage.workspace = true
repository.workspace = true
-[[bin]]
-path = "bin/gcli.rs"
-name = "gcli"
-
[dependencies]
gsdk.workspace = true
anyhow.workspace = true
+async-trait.workspace = true
base64.workspace = true
color-eyre.workspace = true
dirs.workspace = true
diff --git a/gcli/examples/mycli.rs b/gcli/examples/mycli.rs
new file mode 100644
index 00000000000..9b049342ae2
--- /dev/null
+++ b/gcli/examples/mycli.rs
@@ -0,0 +1,54 @@
+// This file is part of Gear.
+//
+// Copyright (C) 2021-2023 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use gcli::{async_trait, App, Command, Parser};
+
+/// My customized sub commands.
+#[derive(Debug, Parser)]
+pub enum SubCommand {
+ /// GCli preset commands.
+ #[clap(flatten)]
+ GCliCommands(Command),
+ /// My customized ping command.
+ Ping,
+}
+
+/// My customized gcli.
+#[derive(Debug, Parser)]
+pub struct MyGCli {
+ #[clap(subcommand)]
+ command: SubCommand,
+}
+
+#[async_trait]
+impl App for MyGCli {
+ async fn exec(&self) -> anyhow::Result<()> {
+ match &self.command {
+ SubCommand::GCliCommands(command) => command.exec(self).await,
+ SubCommand::Ping => {
+ println!("pong");
+ Ok(())
+ }
+ }
+ }
+}
+
+#[tokio::main]
+async fn main() -> color_eyre::Result<()> {
+ MyGCli::parse().run().await
+}
diff --git a/gcli/src/app.rs b/gcli/src/app.rs
new file mode 100644
index 00000000000..0946db8cff2
--- /dev/null
+++ b/gcli/src/app.rs
@@ -0,0 +1,135 @@
+// This file is part of Gear.
+//
+// Copyright (C) 2021-2023 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+//! Command line application abstraction
+
+use crate::keystore;
+use clap::Parser;
+use color_eyre::{eyre::eyre, Result};
+use env_logger::{Builder, Env};
+use gsdk::{signer::Signer, Api};
+
+/// Command line gear program application abstraction.
+///
+/// ```ignore
+/// use gcli::{async_trait, App, Command, Parser};
+///
+/// /// My customized sub commands.
+/// #[derive(Debug, Parser)]
+/// pub enum SubCommand {
+/// /// GCli preset commands.
+/// #[clap(flatten)]
+/// GCliCommands(Command),
+/// /// My customized ping command.
+/// Ping,
+/// }
+///
+/// /// My customized gcli.
+/// #[derive(Debug, Parser)]
+/// pub struct MyGCli {
+/// #[clap(subcommand)]
+/// command: SubCommand,
+/// }
+///
+/// #[async_trait]
+/// impl App for MyGCli {
+/// async fn exec(&self) -> anyhow::Result<()> {
+/// match &self.command {
+/// SubCommand::GCliCommands(command) => command.exec(self).await,
+/// SubCommand::Ping => {
+/// println!("pong");
+/// Ok(())
+/// }
+/// }
+/// }
+/// }
+///
+/// #[tokio::main]
+/// async fn main() -> color_eyre::Result<()> {
+/// MyGCli::parse().run().await
+/// }
+/// ```
+#[async_trait::async_trait]
+pub trait App: Parser + Sync {
+ /// Timeout of rpc requests.
+ fn timeout(&self) -> u64 {
+ 60000
+ }
+
+ /// The verbosity logging level.
+ fn verbose(&self) -> u16 {
+ 0
+ }
+
+ /// The endpoint of the gear node.
+ fn endpoint(&self) -> Option {
+ None
+ }
+
+ /// Password of the signer account.
+ fn passwd(&self) -> Option {
+ None
+ }
+
+ /// Exec program from the parsed arguments.
+ async fn exec(&self) -> anyhow::Result<()>;
+
+ /// Get signer.
+ async fn signer(&self) -> anyhow::Result {
+ let endpoint = self.endpoint().clone();
+ let timeout = self.timeout();
+ let passwd = self.passwd();
+
+ let api = Api::new_with_timeout(endpoint.as_deref(), Some(timeout)).await?;
+ let pair = if let Ok(s) = keystore::cache(passwd.as_deref()) {
+ s
+ } else {
+ keystore::keyring(passwd.as_deref())?
+ };
+
+ Ok((api, pair).into())
+ }
+
+ /// Run application.
+ ///
+ /// This is a wrapper of [`Self::exec`] with preset retry
+ /// and verbose level.
+ async fn run(&self) -> Result<()> {
+ color_eyre::install()?;
+ sp_core::crypto::set_default_ss58_version(crate::VARA_SS58_PREFIX.into());
+
+ let name = Self::command().get_name().to_string();
+ let filter = match self.verbose() {
+ 0 => format!("{name}=info,gsdk=info"),
+ 1 => format!("{name}=debug,gsdk=debug"),
+ 2 => "debug".into(),
+ _ => "trace".into(),
+ };
+
+ let mut builder = Builder::from_env(Env::default().default_filter_or(filter));
+ builder
+ .format_target(false)
+ .format_module_path(false)
+ .format_timestamp(None);
+ builder.try_init()?;
+
+ self.exec()
+ .await
+ .map_err(|e| eyre!("Failed to run app, {e}"))
+ }
+}
diff --git a/gcli/bin/gcli.rs b/gcli/src/bin/gcli.rs
similarity index 73%
rename from gcli/bin/gcli.rs
rename to gcli/src/bin/gcli.rs
index 178aa739dea..1ed0e8eefb0 100644
--- a/gcli/bin/gcli.rs
+++ b/gcli/src/bin/gcli.rs
@@ -16,18 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-//! gear command entry
-use color_eyre::eyre::Result;
+use gcli::{cmd::Opt, App, Parser};
#[tokio::main]
-async fn main() -> Result<()> {
- color_eyre::install()?;
-
- sp_core::crypto::set_default_ss58_version(gcli::VARA_SS58_PREFIX.into());
-
- if let Err(e) = gcli::cmd::Opt::run().await {
- log::error!("{}", e);
- }
-
- Ok(())
+async fn main() -> color_eyre::Result<()> {
+ Opt::parse().run().await
}
diff --git a/gcli/src/cmd/key.rs b/gcli/src/cmd/key.rs
index 1a5f85d0ac1..68c1bf3bc00 100644
--- a/gcli/src/cmd/key.rs
+++ b/gcli/src/cmd/key.rs
@@ -96,6 +96,9 @@ pub struct Key {
/// Key actions
#[command(subcommand)]
action: Action,
+ /// Passphrase override
+ #[arg(short, long)]
+ passwd: Option,
}
macro_rules! match_scheme {
@@ -121,15 +124,15 @@ impl Key {
/// # NOTE
///
/// Reserved the `passwd` for getting suri from cache.
- pub fn exec(&self, passwd: Option<&str>) -> Result<()> {
+ pub fn exec(&self) -> Result<()> {
match &self.action {
- Action::Generate => self.generate(passwd)?,
+ Action::Generate => self.generate()?,
#[cfg(feature = "node-key")]
Action::GenerateNodeKey => Self::generate_node_key(),
- Action::Inspect { suri } => self.inspect(suri, passwd)?,
+ Action::Inspect { suri } => self.inspect(suri)?,
#[cfg(feature = "node-key")]
Action::InspectNodeKey { secret } => Self::inspect_node_key(secret)?,
- Action::Sign { suri, message } => self.sign(suri, message, passwd)?,
+ Action::Sign { suri, message } => self.sign(suri, message)?,
Action::Verify {
signature,
message,
@@ -140,7 +143,8 @@ impl Key {
Ok(())
}
- fn generate(&self, passwd: Option<&str>) -> Result<()> {
+ fn generate(&self) -> Result<()> {
+ let passwd = self.passwd.as_deref();
match_scheme!(self.scheme, generate_with_phrase(passwd), res, {
let (pair, phrase, seed) = res;
let signer = pair.signer();
@@ -181,9 +185,10 @@ impl Key {
println!(" SS58 Address: {}", signer.public().into_account());
}
- fn inspect(&self, suri: &str, passwd: Option<&str>) -> Result<()> {
+ fn inspect(&self, suri: &str) -> Result<()> {
let key = KeyT::from_string(suri);
let key_ref = &key;
+ let passwd = self.passwd.as_deref();
match_scheme!(self.scheme, pair(key_ref, passwd), pair, {
Self::info(&format!("Secret Key URI `{suri}`"), pair.0.signer(), pair.1)
});
@@ -209,9 +214,10 @@ impl Key {
Ok(())
}
- fn sign(&self, suri: &str, message: &str, passwd: Option<&str>) -> Result<()> {
+ fn sign(&self, suri: &str, message: &str) -> Result<()> {
let key = KeyT::from_string(suri);
let key_ref = &key;
+ let passwd = self.passwd.as_deref();
match_scheme!(self.scheme, pair(key_ref, passwd), pair, {
let signer = pair.0.signer();
diff --git a/gcli/src/cmd/mod.rs b/gcli/src/cmd/mod.rs
index 6f22dbb1588..29ea7ef4c41 100644
--- a/gcli/src/cmd/mod.rs
+++ b/gcli/src/cmd/mod.rs
@@ -17,11 +17,8 @@
// along with this program. If not, see .
//! commands
-use crate::{keystore, result::Result};
+use crate::App;
use clap::Parser;
-use env_logger::{Builder, Env};
-use gsdk::Api;
-use log::LevelFilter;
pub mod claim;
pub mod create;
@@ -36,7 +33,7 @@ pub mod transfer;
pub mod update;
pub mod upload;
-/// Commands of cli `gear`
+/// All SubCommands of gear command line interface.
#[derive(Debug, Parser)]
pub enum Command {
Claim(claim::Claim),
@@ -54,26 +51,42 @@ pub enum Command {
Update(update::Update),
}
-/// gear command-line tools
-/// ___ ___ _ ___
-/// / __| / __| | | |_ _|
-/// | (_ | | (__ | |__ | |
-/// \___| \___| |____| |___|
-/// _|"""""|_|"""""|_|"""""|_|"""""|
-/// "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
+impl Command {
+ /// Execute the command.
+ pub async fn exec(&self, app: &impl App) -> anyhow::Result<()> {
+ match self {
+ Command::Key(key) => key.exec()?,
+ Command::Login(login) => login.exec()?,
+ Command::New(new) => new.exec().await?,
+ Command::Program(program) => program.exec(app).await?,
+ Command::Update(update) => update.exec().await?,
+ Command::Claim(claim) => claim.exec(app.signer().await?).await?,
+ Command::Create(create) => create.exec(app.signer().await?).await?,
+ Command::Info(info) => info.exec(app.signer().await?).await?,
+ Command::Send(send) => send.exec(app.signer().await?).await?,
+ Command::Upload(upload) => upload.exec(app.signer().await?).await?,
+ Command::Transfer(transfer) => transfer.exec(app.signer().await?).await?,
+ Command::Reply(reply) => reply.exec(app.signer().await?).await?,
+ }
+
+ Ok(())
+ }
+}
+
+/// Gear command-line interface.
#[derive(Debug, Parser)]
-#[clap(author, version, verbatim_doc_comment)]
+#[clap(author, version)]
#[command(name = "gcli")]
pub struct Opt {
/// Commands.
#[command(subcommand)]
pub command: Command,
- /// How many times we'll retry when RPC requests failed.
- #[arg(short, long, default_value = "5")]
- pub retry: u16,
+ /// Timeout for rpc requests.
+ #[arg(short, long, default_value = "60000")]
+ pub timeout: u64,
/// Enable verbose logs.
- #[arg(short, long)]
- pub verbose: bool,
+ #[clap(short, long, action = clap::ArgAction::Count)]
+ pub verbose: u16,
/// Gear node rpc endpoint.
#[arg(short, long)]
pub endpoint: Option,
@@ -82,87 +95,33 @@ pub struct Opt {
pub passwd: Option,
}
-impl Opt {
- /// setup logs
- fn setup_logs(&self) -> Result<()> {
- let mut builder = if self.verbose {
- Builder::from_env(Env::default().default_filter_or("gcli=debug"))
- } else {
- match &self.command {
- Command::Claim(_)
- | Command::Create(_)
- | Command::Reply(_)
- | Command::Send(_)
- | Command::Upload(_)
- | Command::Transfer(_) => {
- let mut builder = Builder::from_env(Env::default().default_filter_or("info"));
- builder
- .format_target(false)
- .format_module_path(false)
- .format_timestamp(None)
- .filter_level(LevelFilter::Info);
-
- builder
- }
- _ => Builder::from_default_env(),
- }
- };
+#[async_trait::async_trait]
+impl App for Opt {
+ fn timeout(&self) -> u64 {
+ self.timeout
+ }
- builder.try_init()?;
- Ok(())
+ fn verbose(&self) -> u16 {
+ self.verbose
}
- /// run program
- pub async fn run() -> Result<()> {
- let opt = Opt::parse();
+ fn endpoint(&self) -> Option {
+ self.endpoint.clone()
+ }
- opt.setup_logs()?;
- opt.exec().await?;
- Ok(())
+ fn passwd(&self) -> Option {
+ self.passwd.clone()
}
- /// Create api client from endpoint
- async fn api(&self) -> Result {
- Api::new(self.endpoint.as_deref()).await.map_err(Into::into)
+ async fn exec(&self) -> anyhow::Result<()> {
+ self.command.exec(self).await
}
+}
- /// Execute command sync
+impl Opt {
+ /// Run command sync.
pub fn exec_sync(&self) -> color_eyre::Result<()> {
let rt = tokio::runtime::Runtime::new().unwrap();
-
- rt.block_on(self.exec()).map_err(Into::into)
- }
-
- /// Execute command.
- pub async fn exec(&self) -> Result<()> {
- match &self.command {
- Command::Key(key) => key.exec(self.passwd.as_deref())?,
- Command::Login(login) => login.exec()?,
- Command::New(new) => new.exec().await?,
- Command::Program(program) => program.exec(self.api().await?).await?,
- Command::Update(update) => update.exec().await?,
- sub => {
- let api = self.api().await?;
- let pair = if let Ok(s) = keystore::cache(self.passwd.as_deref()) {
- s
- } else {
- keystore::keyring(self.passwd.as_deref())?
- };
- let signer = (api, pair).into();
-
- match sub {
- Command::Claim(claim) => claim.exec(signer).await?,
- Command::Create(create) => create.exec(signer).await?,
- Command::Info(info) => info.exec(signer).await?,
- Command::Send(send) => send.exec(signer).await?,
- Command::Upload(upload) => upload.exec(signer).await?,
- Command::Transfer(transfer) => transfer.exec(signer).await?,
- Command::Reply(reply) => reply.exec(signer).await?,
- _ => unreachable!("Already matched"),
- }
- }
- }
-
- Ok(())
+ rt.block_on(self.run()).map_err(Into::into)
}
}
diff --git a/gcli/src/cmd/program.rs b/gcli/src/cmd/program.rs
index 80db325eb2b..e6c7bdaba78 100644
--- a/gcli/src/cmd/program.rs
+++ b/gcli/src/cmd/program.rs
@@ -17,13 +17,13 @@
// along with this program. If not, see .
//! Command `program`.
-use crate::{meta::Meta, result::Result};
+use crate::{meta::Meta, result::Result, App};
use clap::Parser;
use gsdk::{ext::sp_core::H256, Api};
use std::{fs, path::PathBuf};
/// Read program state, etc.
-#[derive(Clone, Debug, Parser)]
+#[derive(Debug, Parser)]
pub enum Program {
/// Display metadata of the program.
///
@@ -61,7 +61,7 @@ pub enum Program {
impl Program {
/// Run command program.
- pub async fn exec(&self, api: Api) -> Result<()> {
+ pub async fn exec(&self, app: &impl App) -> Result<()> {
match self {
Program::State {
pid,
@@ -70,6 +70,7 @@ impl Program {
args,
at,
} => {
+ let api = app.signer().await?.api().clone();
if let (Some(wasm), Some(method)) = (wasm, method) {
// read state from wasm.
Self::wasm_state(api, *pid, wasm.to_vec(), method, args.clone(), *at).await?;
diff --git a/gcli/src/cmd/upload.rs b/gcli/src/cmd/upload.rs
index 34635284f92..e55389efb23 100644
--- a/gcli/src/cmd/upload.rs
+++ b/gcli/src/cmd/upload.rs
@@ -18,6 +18,7 @@
//! command `upload_program`
use crate::{result::Result, utils::Hex};
+use anyhow::anyhow;
use clap::Parser;
use gsdk::signer::Signer;
use std::{fs, path::PathBuf};
@@ -47,7 +48,8 @@ pub struct Upload {
impl Upload {
/// Exec command submit
pub async fn exec(&self, signer: Signer) -> Result<()> {
- let code = fs::read(&self.code)?;
+ let code =
+ fs::read(&self.code).map_err(|e| anyhow!("program {:?} not found, {e}", &self.code))?;
if self.code_only {
signer.calls.upload_code(code).await?;
return Ok(());
diff --git a/gcli/src/lib.rs b/gcli/src/lib.rs
index 569987e692c..364fe1e00a8 100644
--- a/gcli/src/lib.rs
+++ b/gcli/src/lib.rs
@@ -122,6 +122,7 @@
//!
//! GPL v3.0
+mod app;
pub mod cmd;
pub mod keystore;
pub mod meta;
@@ -129,5 +130,10 @@ pub mod result;
pub mod template;
pub mod utils;
+pub use self::{app::App, cmd::Command};
+pub use async_trait::async_trait;
+pub use clap::Parser;
+pub use gsdk::signer::Signer;
+
/// SS58 prefix for vara network.
pub const VARA_SS58_PREFIX: u8 = 137;
diff --git a/gcli/tests/cmd/claim.rs b/gcli/tests/cmd/claim.rs
index 7d9e57b924d..de82638fc2c 100644
--- a/gcli/tests/cmd/claim.rs
+++ b/gcli/tests/cmd/claim.rs
@@ -18,7 +18,7 @@
//! Integration tests for command `send`
-use crate::common::{self, logs, traits::NodeExec, Args, Result, ALICE_SS58_ADDRESS as ADDRESS};
+use crate::common::{self, logs, node::NodeExec, Args, Result, ALICE_SS58_ADDRESS as ADDRESS};
use gsdk::Api;
const REWARD_PER_BLOCK: u128 = 75_000; // 3_000 gas * 25 value per gas
@@ -68,9 +68,7 @@ async fn test_command_claim_works() -> Result<()> {
let burned_after = signer.api().get_balance(&signer.address()).await? - initial_stash;
let after = signer.api().get_balance(ADDRESS).await?;
- assert_eq!(initial_balance - before - burned_before, REWARD_PER_BLOCK,);
-
+ assert_eq!(initial_balance - before - burned_before, REWARD_PER_BLOCK);
assert_eq!(initial_balance - burned_after, after);
-
Ok(())
}
diff --git a/gcli/tests/cmd/info.rs b/gcli/tests/cmd/info.rs
index 90360519a59..83b08aa8996 100644
--- a/gcli/tests/cmd/info.rs
+++ b/gcli/tests/cmd/info.rs
@@ -19,7 +19,7 @@
//! Integration tests for command `deploy`
use crate::common::{
self, logs,
- traits::{Convert, NodeExec},
+ node::{Convert, NodeExec},
Args, Result,
};
diff --git a/gcli/tests/cmd/key.rs b/gcli/tests/cmd/key.rs
index afad9b0e186..977d1a19a3a 100644
--- a/gcli/tests/cmd/key.rs
+++ b/gcli/tests/cmd/key.rs
@@ -17,7 +17,7 @@
// along with this program. If not, see .
//! Integration tests for command `key`
-use crate::common::{self, traits::Convert, Result};
+use crate::common::{self, node::Convert, Result};
const SIGNATURE_PATT: &str = "Signature:";
const SEED_PATT: &str = "Seed:";
@@ -27,6 +27,10 @@ const PUBLIC_PATT: &str = "Public key:";
fn parse_from<'s>(log: &'s str, patt: &'s str) -> &'s str {
let arr = log.split(patt).collect::>();
+ if arr.len() != 2 {
+ panic!("Failed to parse {patt}, log: {log}");
+ }
+
arr[1].split_whitespace().collect::>()[0]
}
diff --git a/gcli/tests/cmd/program.rs b/gcli/tests/cmd/program.rs
index d1e1594afa1..12c32f83ca7 100644
--- a/gcli/tests/cmd/program.rs
+++ b/gcli/tests/cmd/program.rs
@@ -19,7 +19,7 @@
//! Integration tests for command `program`
use crate::common::{
self, env, logs,
- traits::{Convert, NodeExec},
+ node::{Convert, NodeExec},
Args, Result,
};
use demo_new_meta::{MessageInitIn, Wallet};
@@ -100,9 +100,8 @@ fn test_command_program_metadata_works() -> Result<()> {
let node = common::dev()?;
let meta = env::wasm_bin("demo_new_meta.meta.txt");
let args = Args::new("program").action("meta").meta(meta);
- let result = node.run(args)?;
+ let stdout = node.stdout(args)?;
- let stdout = result.stdout.convert();
assert_eq!(
stdout.trim(),
DEMO_NEW_META_METADATA.trim(),
@@ -121,9 +120,7 @@ fn test_command_program_metadata_derive_works() -> Result<()> {
.flag("--derive")
.derive("Person");
- let result = node.run(args)?;
- let stdout = result.stdout.convert();
-
+ let stdout = node.stdout(args)?;
let expected = "Person { surname: String, name: String }";
assert_eq!(
stdout.trim(),
@@ -158,9 +155,8 @@ fn test_command_program_metawasm_works() -> Result<()> {
let node = common::dev()?;
let meta = env::wasm_bin("demo_meta_state_v1.meta.wasm");
let args = Args::new("program").action("meta").meta(meta);
- let result = node.run(args)?;
+ let stdout = node.stdout(args)?;
- let stdout = result.stdout.convert();
assert_eq!(
stdout.trim(),
META_WASM_V1_OUTPUT.trim(),
@@ -179,8 +175,7 @@ fn test_command_program_metawasm_derive_works() -> Result<()> {
.flag("--derive")
.derive("Person");
- let result = node.run(args)?;
- let stdout = result.stdout.convert();
+ let stdout = node.stdout(args)?;
let expected = "Person { surname: String, name: String }";
assert_eq!(
diff --git a/gcli/tests/cmd/send.rs b/gcli/tests/cmd/send.rs
index 83d8fb2020c..f7e3ac2c2da 100644
--- a/gcli/tests/cmd/send.rs
+++ b/gcli/tests/cmd/send.rs
@@ -17,7 +17,7 @@
// along with this program. If not, see .
//! Integration tests for command `send`
-use crate::common::{self, traits::NodeExec, Args, Result};
+use crate::common::{self, node::NodeExec, Args, Result};
use gsdk::Api;
use scale_info::scale::Encode;
diff --git a/gcli/tests/cmd/upload.rs b/gcli/tests/cmd/upload.rs
index 3a28c7d20b8..39aa6cdc6ea 100644
--- a/gcli/tests/cmd/upload.rs
+++ b/gcli/tests/cmd/upload.rs
@@ -19,7 +19,7 @@
//! Integration tests for command `upload`
use crate::common::{
self, env, logs,
- traits::{Convert, NodeExec},
+ node::{Convert, NodeExec},
Args, Result,
};
use gear_core::ids::CodeId;
@@ -32,9 +32,10 @@ async fn test_command_upload_works() {
node.wait_for_log_record(logs::gear_node::IMPORTING_BLOCKS)
.expect("node timeout");
- let signer = Api::new(Some(&node.ws()))
+ let ws = node.ws();
+ let signer = Api::new(Some(&ws))
.await
- .expect("build api failed")
+ .unwrap_or_else(|_| panic!("failed to connect to {ws}"))
.signer("//Alice", None)
.expect("get signer failed");
@@ -53,8 +54,8 @@ async fn test_command_upload_works() {
.stderr
.convert()
.contains(logs::gear_program::EX_UPLOAD_PROGRAM),
- "code should be uploaded, but got: {:?}",
- output.stderr
+ "code should be uploaded, but got: {}",
+ output.stderr.convert(),
);
assert!(
signer.api().code_storage(code_id).await.is_ok(),
diff --git a/gcli/tests/common/mod.rs b/gcli/tests/common/mod.rs
index 78494bb3d1b..aa245fe6bbd 100644
--- a/gcli/tests/common/mod.rs
+++ b/gcli/tests/common/mod.rs
@@ -19,14 +19,16 @@
//! Common utils for integration tests
pub use self::{
args::Args,
+ node::{Convert, NodeExec},
result::{Error, Result},
- traits::{Convert, NodeExec},
};
+use anyhow::anyhow;
use gear_core::ids::{CodeId, ProgramId};
use gsdk::{
ext::{sp_core::crypto::Ss58Codec, sp_runtime::AccountId32},
testing::Node,
};
+pub use scale_info::scale::Encode;
use std::{
iter::IntoIterator,
process::{Command, Output},
@@ -35,17 +37,10 @@ use std::{
mod args;
pub mod env;
pub mod logs;
+pub mod node;
mod result;
-pub mod traits;
-mod prelude {
- pub use scale_info::scale::Encode;
-
- pub const ALICE_SS58_ADDRESS: &str = "kGkLEU3e3XXkJp2WK4eNpVmSab5xUNL9QtmLPh8QfCL2EgotW";
-}
-
-#[cfg(not(feature = "vara-testing"))]
-pub use prelude::*;
+pub const ALICE_SS58_ADDRESS: &str = "kGkLEU3e3XXkJp2WK4eNpVmSab5xUNL9QtmLPh8QfCL2EgotW";
impl NodeExec for Node {
fn ws(&self) -> String {
@@ -73,8 +68,9 @@ pub fn gcli(args: impl IntoIterator- ) -> Result