Skip to content
Snippets Groups Projects
Commit a0c494c7 authored by Éloïs's avatar Éloïs
Browse files

ref+tests: make sub-commands testable and test it

parent 3c09b927
No related branches found
No related tags found
No related merge requests found
...@@ -15,6 +15,15 @@ version = "0.2.3" ...@@ -15,6 +15,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.11.0" version = "0.11.0"
...@@ -169,12 +178,24 @@ version = "0.8.2" ...@@ -169,12 +178,24 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]] [[package]]
name = "doc-comment" name = "doc-comment"
version = "0.3.3" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "downcast"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
...@@ -212,6 +233,15 @@ dependencies = [ ...@@ -212,6 +233,15 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
...@@ -243,6 +273,12 @@ dependencies = [ ...@@ -243,6 +273,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fragile"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]] [[package]]
name = "fuchsia-zircon" name = "fuchsia-zircon"
version = "0.3.3" version = "0.3.3"
...@@ -394,6 +430,7 @@ version = "0.1.0" ...@@ -394,6 +430,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"graphql_client", "graphql_client",
"mockall",
"reqwest", "reqwest",
"serde", "serde",
"structopt", "structopt",
...@@ -664,6 +701,33 @@ dependencies = [ ...@@ -664,6 +701,33 @@ dependencies = [
"ws2_32-sys", "ws2_32-sys",
] ]
[[package]]
name = "mockall"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cabea45a7fc0e37093f4f30a5e2b62602253f91791c057d5f0470c63260c3d"
dependencies = [
"cfg-if 0.1.10",
"downcast",
"fragile",
"lazy_static",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c461918bf7f59eefb1459252756bf2351a995d6bd510d0b2061bd86bcdabfa6"
dependencies = [
"cfg-if 0.1.10",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.6" version = "0.2.6"
...@@ -693,6 +757,21 @@ dependencies = [ ...@@ -693,6 +757,21 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.0"
...@@ -824,6 +903,35 @@ version = "0.2.10" ...@@ -824,6 +903,35 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a"
dependencies = [
"difference",
"float-cmp",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
[[package]]
name = "predicates-tree"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"
dependencies = [
"predicates-core",
"treeline",
]
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
...@@ -913,6 +1021,24 @@ version = "0.1.57" ...@@ -913,6 +1021,24 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "regex"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.3" version = "0.5.3"
...@@ -1147,6 +1273,15 @@ dependencies = [ ...@@ -1147,6 +1273,15 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.1.0" version = "1.1.0"
...@@ -1241,6 +1376,12 @@ dependencies = [ ...@@ -1241,6 +1376,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.3" version = "0.2.3"
......
...@@ -13,3 +13,6 @@ graphql_client = "0.9.0" ...@@ -13,3 +13,6 @@ graphql_client = "0.9.0"
reqwest = { version = "0.10.9", features = ["blocking", "json"] } reqwest = { version = "0.10.9", features = ["blocking", "json"] }
serde = { version = "1.0.105", features = ["derive"] } serde = { version = "1.0.105", features = ["derive"] }
structopt = "0.3.18" structopt = "0.3.18"
[dev-dependencies]
mockall = "0.8.0"
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*; use crate::*;
pub(crate) struct Client(reqwest::blocking::Client); #[cfg_attr(test, allow(dead_code))]
pub(crate) struct Client {
inner: reqwest::blocking::Client,
server_url: String,
}
#[cfg_attr(test, mockall::automock, allow(dead_code))]
impl Client { impl Client {
pub(crate) fn new() -> Self { pub(crate) fn new(server_url: String) -> Self {
Client(reqwest::blocking::Client::new()) Client {
inner: reqwest::blocking::Client::new(),
server_url,
}
} }
pub(crate) fn send_gql_query<Req: serde::Serialize, ResData: serde::de::DeserializeOwned>( pub(crate) fn send_gql_query<
Req: 'static + serde::Serialize,
ResData: 'static + serde::de::DeserializeOwned,
>(
&self, &self,
request_body: &Req, request_body: &Req,
server_url: &str,
) -> anyhow::Result<ResData> { ) -> anyhow::Result<ResData> {
let request = self.0.post(server_url).json(request_body); let request = self.inner.post(&self.server_url).json(request_body);
let start_time = Instant::now(); let start_time = Instant::now();
let response = request.send()?; let response = request.send()?;
......
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::*;
#[derive(StructOpt)]
pub(crate) enum Command {
/// Get account balance
Balance {
pubkey_or_script: String,
#[structopt(short, long)]
ud_unit: bool,
},
/// Get current UD value
CurrentUd,
}
pub(crate) fn balance<W: Write>(
client: &Client,
out: &mut W,
pubkey_or_script: &str,
ud_unit: bool,
) -> anyhow::Result<()> {
let request_body = BalanceQuery::build_query(balance_query::Variables {
script: pubkey_or_script.to_owned(),
with_ud: ud_unit,
});
let balance_query::ResponseData {
balance: balance_query::BalanceQueryBalance { amount },
current_ud: current_ud_opt,
} = client.send_gql_query(&request_body)?;
if let Some(balance_query::BalanceQueryCurrentUd { amount: ud_amount }) = current_ud_opt {
writeln!(
out,
"The balance of account '{}' is {:.2} UDĞ1 !",
pubkey_or_script,
amount as f64 / ud_amount as f64,
)?;
} else {
writeln!(
out,
"The balance of account '{}' is {}.{:02} Ğ1 !",
pubkey_or_script,
amount / 100,
amount % 100
)?;
}
Ok(())
}
pub(crate) fn current_ud<W: Write>(client: &Client, out: &mut W) -> anyhow::Result<()> {
let request_body = CurrentUdQuery::build_query(current_ud_query::Variables);
if let current_ud_query::ResponseData {
current_ud: Some(current_ud_query::CurrentUdQueryCurrentUd { amount }),
} = client.send_gql_query(&request_body)?
{
let int_part = amount / 100;
let dec_part = amount % 100;
writeln!(
out,
"The current UD value is {}.{:02} Ğ1 !",
int_part, dec_part
)?;
} else {
writeln!(out, "server with empty blockchain")?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_balance() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<balance_query::Variables>, _>()
.returning(|_| {
Ok(balance_query::ResponseData {
balance: balance_query::BalanceQueryBalance { amount: 2_046 },
current_ud: None,
})
});
let mut out = Vec::new();
balance(&client, &mut out, "toto", false)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The balance of account 'toto' is 20.46 Ğ1 !\n");
Ok(())
}
#[test]
fn test_balance_with_ud_unit() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<balance_query::Variables>, _>()
.returning(|_| {
Ok(balance_query::ResponseData {
balance: balance_query::BalanceQueryBalance { amount: 2_046 },
current_ud: Some(balance_query::BalanceQueryCurrentUd { amount: 1_023 }),
})
});
let mut out = Vec::new();
balance(&client, &mut out, "toto", true)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The balance of account 'toto' is 2.00 UDĞ1 !\n");
Ok(())
}
#[test]
fn test_current_ud() -> anyhow::Result<()> {
let mut client = Client::default();
client
.expect_send_gql_query::<graphql_client::QueryBody<current_ud_query::Variables>, _>()
.returning(|_| {
Ok(current_ud_query::ResponseData {
current_ud: Some(current_ud_query::CurrentUdQueryCurrentUd { amount: 1_023 }),
})
});
let mut out = Vec::new();
current_ud(&client, &mut out)?;
let output = std::str::from_utf8(&out)?;
assert_eq!(output, "The current UD value is 10.23 Ğ1 !\n");
Ok(())
}
}
...@@ -25,10 +25,17 @@ ...@@ -25,10 +25,17 @@
)] )]
mod client; mod client;
mod commands;
#[cfg(not(test))]
use crate::client::Client; use crate::client::Client;
#[cfg(test)]
use crate::client::MockClient as Client;
use crate::commands::Command;
use commands::{balance, current_ud};
use graphql_client::GraphQLQuery; use graphql_client::GraphQLQuery;
use graphql_client::Response; use graphql_client::Response;
use std::io::Write;
use std::time::Instant; use std::time::Instant;
use structopt::StructOpt; use structopt::StructOpt;
...@@ -49,71 +56,21 @@ struct CliArgs { ...@@ -49,71 +56,21 @@ struct CliArgs {
#[structopt(short, long, default_value = DEFAULT_GVA_SERVER)] #[structopt(short, long, default_value = DEFAULT_GVA_SERVER)]
server: String, server: String,
#[structopt(subcommand)] #[structopt(subcommand)]
command: SubCommand, command: Command,
}
#[derive(StructOpt)]
enum SubCommand {
/// Get account balance
Balance {
pubkey_or_script: String,
#[structopt(short, long)]
ud_unit: bool,
},
/// Get current UD value
CurrentUd,
} }
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let cli_args = CliArgs::from_args(); let cli_args = CliArgs::from_args();
let client = Client::new(); let client = Client::new(cli_args.server);
let mut out = std::io::stdout();
match cli_args.command { match cli_args.command {
SubCommand::Balance { Command::Balance {
pubkey_or_script, pubkey_or_script,
ud_unit, ud_unit,
} => { } => balance(&client, &mut out, &pubkey_or_script, ud_unit)?,
let request_body = BalanceQuery::build_query(balance_query::Variables { Command::CurrentUd => current_ud(&client, &mut out)?,
script: pubkey_or_script.clone(),
with_ud: ud_unit,
});
let balance_query::ResponseData {
balance: balance_query::BalanceQueryBalance { amount },
current_ud: current_ud_opt,
} = client.send_gql_query(&request_body, &cli_args.server)?;
if let Some(balance_query::BalanceQueryCurrentUd { amount: ud_amount }) = current_ud_opt
{
println!(
"The balance of account '{}' is {:.2} UDĞ1 !",
pubkey_or_script,
amount as f64 / ud_amount as f64,
);
} else {
println!(
"The balance of account '{}' is {}.{} Ğ1 !",
pubkey_or_script,
amount / 100,
amount % 100
);
}
}
SubCommand::CurrentUd => {
let request_body = CurrentUdQuery::build_query(current_ud_query::Variables);
if let current_ud_query::ResponseData {
current_ud: Some(current_ud_query::CurrentUdQueryCurrentUd { amount }),
} = client.send_gql_query(&request_body, &cli_args.server)?
{
let int_part = amount / 100;
let dec_part = amount % 100;
println!("The current UD value is {}.{} Ğ1 !", int_part, dec_part);
} else {
println!("server with empty blockchain");
}
}
} }
Ok(()) Ok(())
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment