diff --git a/end2end-tests/README.md b/end2end-tests/README.md index 8dfd383e446e87d2a69752a2eaa1adcde508d91c..c15fc019df0c420eb01b9306143f20885a764947 100644 --- a/end2end-tests/README.md +++ b/end2end-tests/README.md @@ -1,4 +1,4 @@ -# Duniter-v2s integration tests +# Duniter-v2s end2end tests ## cucumber functionnal tests @@ -47,37 +47,9 @@ Feature: My awesome feature Then We should observe that ``` -### Test users - -6 test users are provided: - -- alice -- bob -- charlie -- dave -- eve -- ferdie - -### genesis state - -Each scenario bootstraps its own blockchain with its own genesis state. - -By default, all scenarios use the same configuration for the genesis, which is located in the file -`/cucumber-genesis/default.json`. +### Steps -You can define a custom genesis state for each scenario with the tag `@genesis.confName`. - -The genesis configuration must then be defined in a json file located at -`/cucumber-genesis/confName.json`. - -You can also define a custom genesis at the feature level, all the scenarios of this feature will -then inherit the genesis configuration. - -### Currency amounts - -Amounts must be expressed as an integer of `ÄžD` or `UD`, decimal numbers are not supported. -If you need more precision, you can express amounts in cents of ÄžD (write `cÄžD`), or in thousandths -of UD (write `mUD`). +Each scenario is a list of steps. In our context (blockchain), only the `When` and `Then` steps make sense, `Given` being the genesis. #### When @@ -95,23 +67,54 @@ List of possible actions: Example: `alice should have 10 ÄžD` -### Universal dividend creation - -#### Then - - Check the current UD amount Usage: `Current UD amount should be {amount}.{cents} ÄžD` Example: `Current UD amount should be 10.00 ÄžD` - - Check the monetary mass Usage: `Monetary mass should be {amount}.{cents} ÄžD` Example: `Monetary mass should be 30.00 ÄžD` +### Test users + +6 test users are provided: + +- alice +- bob +- charlie +- dave +- eve +- ferdie + +### Currency amounts + +Amounts must be expressed as an integer of `ÄžD` or `UD`, decimal numbers are not supported. +If you need more precision, you can express amounts in cents of ÄžD (write `cÄžD`), or in thousandths +of UD (write `mUD`). + +### genesis state + +Each scenario bootstraps its own blockchain with its own genesis state. + +By default, all scenarios use the same configuration for the genesis, which is located in the file +`/cucumber-genesis/default.json`. + +You can define a custom genesis state for each scenario with the tag `@genesis.confName`. + +The genesis configuration must then be defined in a json file located at +`/cucumber-genesis/confName.json`. + +You can also define a custom genesis at the feature level, all the scenarios of this feature will +then inherit the genesis configuration. + +### ignoreErrors + +For some scenarios, you may need to perform an action (When) that fails voluntarily, in this case you must add the tag @ignoreErrors to your scenario, otherwise it will be considered as failed + ### Run cucumber functional tests To run the cucumber tests, you will need to have the rust toolchain installed locally. diff --git a/end2end-tests/cucumber-features/account_creation.feature b/end2end-tests/cucumber-features/account_creation.feature index bc2fc4e787279c44e18f7d66a299f9ca2c139a74..e3df0d149011a0c8575eb49cc0f6c25284a0d9a6 100644 --- a/end2end-tests/cucumber-features/account_creation.feature +++ b/end2end-tests/cucumber-features/account_creation.feature @@ -26,6 +26,7 @@ Feature: Balance transfer """ Then eve should have 2 ÄžD + @ignoreErrors Scenario: Create a new account without any founds Then eve should have 0 ÄžD When eve send 0 ÄžD to alice diff --git a/end2end-tests/cucumber-features/transfer_all.feature b/end2end-tests/cucumber-features/transfer_all.feature index b111bb51822d628e4cefed0b71b199e33bf7a5bc..23b516ee4733765aa80ef9932fbda193168e4728 100644 --- a/end2end-tests/cucumber-features/transfer_all.feature +++ b/end2end-tests/cucumber-features/transfer_all.feature @@ -1,11 +1,14 @@ @genesis.default Feature: Balance transfer all - Scenario: If alice sends all her ÄžDs to Dave, Dave will get 8 ÄžD - When alice sends all her ÄžDs to dave + Scenario: If bob sends all his ÄžDs to Dave + When bob sends all her ÄžDs to dave """ - Alice is a smith member, as such she is not allowed to empty her account completely, - if she tries to do so, the existence deposit (2 ÄžD) must remain. + Bob is a member, as such he is not allowed to empty his account completely, + if he tries to do so, the existence deposit (2 ÄžD) must remain. """ - Then alice should have 2 ÄžD - Then dave should have 8 ÄžD + Then bob should have 2 ÄžD + """ + 10 ÄžD (initial Bob balance) - 2 ÄžD (Existential deposit) - 0.02 ÄžD (transaction fees) + """ + Then dave should have 798 cÄžD diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs index ffe54030a61315e4f916611d0b95c6003594f379..a039bc2b73908db6ac9665f1069a96d7b1ba4a05 100644 --- a/end2end-tests/tests/cucumber_tests.rs +++ b/end2end-tests/tests/cucumber_tests.rs @@ -29,33 +29,44 @@ use std::sync::{ }; #[derive(WorldInit)] -pub struct DuniterWorld(Option<DuniterWorldInner>); +pub struct DuniterWorld { + ignore_errors: bool, + inner: Option<DuniterWorldInner>, +} impl DuniterWorld { + // Write methods async fn init(&mut self, maybe_genesis_conf_file: Option<PathBuf>) { - if let Some(ref mut inner) = self.0 { + if let Some(ref mut inner) = self.inner { + inner.kill(); + } + self.inner = Some(DuniterWorldInner::new(maybe_genesis_conf_file).await); + } + fn kill(&mut self) { + if let Some(ref mut inner) = self.inner { inner.kill(); } - self.0 = Some(DuniterWorldInner::new(maybe_genesis_conf_file).await); } + fn set_ignore_errors(&mut self, ignore_errors: bool) { + self.ignore_errors = ignore_errors; + } + // Read methods fn api(&self) -> &Api { - if let Some(ref inner) = self.0 { + if let Some(ref inner) = self.inner { &inner.api } else { panic!("uninit") } } fn client(&self) -> &Client { - if let Some(ref inner) = self.0 { + if let Some(ref inner) = self.inner { &inner.client } else { panic!("uninit") } } - fn kill(&mut self) { - if let Some(ref mut inner) = self.0 { - inner.kill(); - } + fn ignore_errors(&self) -> bool { + self.ignore_errors } } @@ -71,7 +82,10 @@ impl World for DuniterWorld { type Error = Infallible; async fn new() -> std::result::Result<Self, Infallible> { - Ok(DuniterWorld(None)) + Ok(Self { + ignore_errors: false, + inner: None, + }) } } @@ -148,10 +162,16 @@ async fn transfer( let to = AccountKeyring::from_str(&to).expect("unknown to"); let (amount, is_ud) = parse_amount(amount, &unit); - if is_ud { + let res = if is_ud { common::balances::transfer_ud(world.api(), world.client(), from, amount, to).await } else { common::balances::transfer(world.api(), world.client(), from, amount, to).await + }; + + if world.ignore_errors() { + Ok(()) + } else { + res } } @@ -164,13 +184,18 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu common::balances::transfer_all(world.api(), world.client(), from, to).await } -#[then(regex = r"([a-zA-Z]+) should have (\d+) ÄžD")] -async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Result<()> { +#[then(regex = r"([a-zA-Z]+) should have (\d+) (ÄžD|cÄžD)")] +async fn should_have( + world: &mut DuniterWorld, + who: String, + amount: u64, + unit: String, +) -> Result<()> { // Parse inputs let who = AccountKeyring::from_str(&who) .expect("unknown to") .to_account_id(); - let amount = amount * 100; + let (amount, _is_ud) = parse_amount(amount, &unit); let who_account = world.api().storage().system().account(who, None).await?; assert_eq!(who_account.data.free, amount); @@ -250,6 +275,7 @@ async fn main() { "{}.json", genesis_conf_name(&feature.tags, &scenario.tags) )); + world.set_ignore_errors(ignore_errors(&scenario.tags)); Box::pin(world.init(Some(genesis_conf_file_path))) }) .after(move |_feature, _rule, _scenario, maybe_world| { @@ -280,3 +306,12 @@ fn genesis_conf_name(feature_tags: &[String], scenario_tags: &[String]) -> Strin } "default".to_owned() } + +fn ignore_errors(scenario_tags: &[String]) -> bool { + for tag in scenario_tags { + if tag == "ignoreErrors" { + return true; + } + } + false +} diff --git a/runtime/common/src/fees.rs b/runtime/common/src/fees.rs index aec304d1d4f756e8dee94fc2f0c8e06e086f1990..cac38836fec495c2bbe3ad7cf6a39825806fe5e0 100644 --- a/runtime/common/src/fees.rs +++ b/runtime/common/src/fees.rs @@ -17,7 +17,7 @@ pub use frame_support::weights::{ Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; -use sp_arithmetic::traits::{BaseArithmetic, Unsigned, Zero}; +use sp_arithmetic::traits::{BaseArithmetic, One, Unsigned}; pub struct WeightToFeeImpl<T>(sp_std::marker::PhantomData<T>); @@ -35,8 +35,8 @@ where degree: 1, }) } - // Force disable fees + // Force constant fees fn calc(_weight: &Weight) -> Self::Balance { - Zero::zero() + One::one() } }