Skip to content
Snippets Groups Projects
Commit da4b25bf authored by Hugo Trentesaux's avatar Hugo Trentesaux
Browse files

feat(cucumber) add certification

parent db18f612
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !56. Comments created here will be created in the context of that merge request.
......@@ -1517,6 +1517,7 @@ dependencies = [
name = "duniter-end2end-tests"
version = "3.0.0"
dependencies = [
"anyhow",
"async-trait",
"clap",
"ctrlc",
......
......@@ -9,6 +9,7 @@ repository = 'https://git.duniter.org/nodes/rust/duniter-v2s'
version = '3.0.0'
[dev-dependencies]
anyhow = "1.0"
async-trait = "0.1"
clap = { version = "3.0", features = ["derive"] }
ctrlc = "3.2.2"
......
# Duniter-v2s end2end tests
## cucumber functionnal tests
## Cucumber functionnal tests
We use [cucumber] to be able to describe test scenarios in human language.
......@@ -20,7 +20,7 @@ Feature: Balance transfer
Then dave should have 5 ĞD
```
### create a new functional test
### Create a new functional test
To create a new test scenario, simply create a new file with a name of your choice in the
`/cucumber-features` folder and give it the extension `.feature`.
......@@ -55,8 +55,8 @@ Each scenario is a list of steps. In our context (blockchain), only the `When` a
List of possible actions:
- transfer: `alice send 5 ĞD to bob`
- transfer_ud: `alice send 3 UD to bob`
- transfer: `alice sends 5 ĞD to bob`
- transfer_ud: `alice sends 3 UD to bob`
- transfer_all: `alice sends all her ĞDs to bob`
#### Then
......@@ -96,7 +96,7 @@ Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are no
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
### Genesis state
Each scenario bootstraps its own blockchain with its own genesis state.
......@@ -117,17 +117,22 @@ For some scenarios, you may need to perform an action (When) that fails voluntar
### Run cucumber functional tests
The cucumber tests use the last debug binary in your `target` folder. Make sure this binary corresponds to the executable you want to test by running `cargo build` before.
To run the cucumber tests, you will need to have the rust toolchain installed locally.
To run all the scenarios (there are many) use the command: `cargo cucumber`
You can filter the `.feature` files to run with the option `i`, for instante:
You can filter the `.feature` files to run with the option `i`, for instance:
```
cargo cucumber -i monetary*
```
Will only run `.feature` files that start with `"monetary"`.
will only run `.feature` files that start with `"monetary"`.
The features will be tested in parallel and logs files will be written in the `end2end-tests` folder.
If you get an `Error: Timeout`, look at the logs to understand why Duniter did not launch successfully. You can also set the environment variable `DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT` to increase the timeout for node spawn.
### Contribute to the code that runs the tests
......
@genesis.wot
Feature: Certification
Scenario: Dave certifies Alice
When dave certifies alice
Then alice should be certified by dave
\ No newline at end of file
{
"first_ud": 1000,
"first_ud_reeval": 100,
"identities": {
"Alice": {
"balance": 1000,
"certs": ["Bob", "Charlie", "Dave"],
"pubkey": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
},
"Bob": {
"balance": 1000,
"certs": ["Alice", "Charlie", "Dave"],
"pubkey": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
},
"Charlie": {
"balance": 1000,
"certs": ["Alice", "Bob"],
"pubkey": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
},
"Dave": {
"balance": 1000,
"certs": [],
"pubkey": "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy"
}
},
"parameters": {
"babe_epoch_duration": 30,
"cert_period": 15,
"cert_max_by_issuer": 10,
"cert_min_received_cert_to_issue_cert": 2,
"cert_renewable_period": 50,
"cert_validity_period": 1000,
"idty_confirm_period": 40,
"idty_creation_period": 50,
"membership_period": 1000,
"membership_renewable_period": 50,
"pending_membership_period": 500,
"ud_creation_period": 10,
"ud_reeval_period": 100,
"smith_cert_period": 15,
"smith_cert_max_by_issuer": 8,
"smith_cert_min_received_cert_to_issue_cert": 2,
"smith_cert_renewable_period": 50,
"smith_cert_validity_period": 1000,
"smith_membership_period": 1000,
"smith_membership_renewable_period": 20,
"smith_pending_membership_period": 500,
"smiths_wot_first_cert_issuable_on": 20,
"smiths_wot_min_cert_for_membership": 2,
"wot_first_cert_issuable_on": 20,
"wot_min_cert_for_create_idty_right": 2,
"wot_min_cert_for_membership": 2
},
"smiths": {
"Alice": {
"certs": ["Bob", "Charlie"]
},
"Bob": {
"certs": ["Alice", "Charlie"]
},
"Charlie": {
"certs": ["Alice", "Bob"]
}
},
"sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
}
// Copyright 2021 Axiom-Team
//
// This file is part of Substrate-Libre-Currency.
//
// Substrate-Libre-Currency 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, version 3 of the License.
//
// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
use super::node_runtime::runtime_types::gdev_runtime;
use super::node_runtime::runtime_types::pallet_certification;
use super::*;
use sp_keyring::AccountKeyring;
use subxt::{sp_runtime::MultiAddress, PairSigner};
pub async fn certify(
api: &Api,
client: &Client,
from: AccountKeyring,
to: AccountKeyring,
) -> Result<()> {
let from = PairSigner::new(from.pair());
let to = to.to_account_id();
let _events = create_block_with_extrinsic(
client,
api.tx()
.cert()
.add_cert(to)
.create_signed(&from, ())
.await?,
)
.await?;
Ok(())
}
......@@ -17,10 +17,12 @@
#![allow(clippy::enum_variant_names, dead_code, unused_imports)]
pub mod balances;
pub mod cert;
#[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
pub mod node_runtime {}
use anyhow::anyhow;
use serde_json::Value;
use sp_keyring::AccountKeyring;
use std::io::prelude::*;
......@@ -55,7 +57,6 @@ struct FullNode {
}
pub async fn spawn_node(maybe_genesis_conf_file: Option<PathBuf>) -> (Api, Client, Process) {
println!("maybe_genesis_conf_file={:?}", maybe_genesis_conf_file);
let duniter_binary_path = std::env::var("DUNITER_BINARY_PATH").unwrap_or_else(|_| {
if std::path::Path::new(DUNITER_DOCKER_PATH).exists() {
DUNITER_DOCKER_PATH.to_owned()
......
......@@ -28,6 +28,8 @@ use std::sync::{
Arc,
};
// ===== world =====
#[derive(WorldInit)]
pub struct DuniterWorld {
ignore_errors: bool,
......@@ -119,7 +121,9 @@ fn parse_amount(amount: u64, unit: &str) -> (u64, bool) {
}
}
#[given(regex = r"([a-zA-Z]+) have (\d+) (ĞD|cĞD|UD|mUD)")]
// ===== given =====
#[given(regex = r"([a-zA-Z]+) ha(?:ve|s) (\d+) (ĞD|cĞD|UD|mUD)")]
async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: String) -> Result<()> {
// Parse inputs
let who = AccountKeyring::from_str(&who).expect("unknown to");
......@@ -141,6 +145,8 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
Ok(())
}
// ===== when =====
#[when(regex = r"(\d+) blocks? later")]
async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> {
for _ in 0..n {
......@@ -149,7 +155,7 @@ async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> {
Ok(())
}
#[when(regex = r"([a-zA-Z]+) send (\d+) (ĞD|cĞD|UD|mUD) to ([a-zA-Z]+)")]
#[when(regex = r"([a-zA-Z]+) sends? (\d+) (ĞD|cĞD|UD|mUD) to ([a-zA-Z]+)")]
async fn transfer(
world: &mut DuniterWorld,
from: String,
......@@ -175,7 +181,7 @@ async fn transfer(
}
}
#[when(regex = r"([a-zA-Z]+) sends all (?:his|her) (?:ĞDs?|DUs?) to ([a-zA-Z]+)")]
#[when(regex = r"([a-zA-Z]+) sends? all (?:his|her) (?:ĞDs?|DUs?) to ([a-zA-Z]+)")]
async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
// Parse inputs
let from = AccountKeyring::from_str(&from).expect("unknown from");
......@@ -184,6 +190,17 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu
common::balances::transfer_all(world.api(), world.client(), from, to).await
}
#[when(regex = r"([a-zA-Z]+) certifies ([a-zA-Z]+)")]
async fn certifies(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
// Parse inputs
let from = AccountKeyring::from_str(&from).expect("unknown from");
let to = AccountKeyring::from_str(&to).expect("unknown to");
common::cert::certify(world.api(), world.client(), from, to).await
}
// ===== then ====
#[then(regex = r"([a-zA-Z]+) should have (\d+) (ĞD|cĞD)")]
async fn should_have(
world: &mut DuniterWorld,
......@@ -232,6 +249,52 @@ async fn monetary_mass_should_be(world: &mut DuniterWorld, amount: u64, cents: u
Ok(())
}
#[then(regex = r"([a-zA-Z]+) should be certified by ([a-zA-Z]+)")]
async fn should_be_certified_by(
world: &mut DuniterWorld,
receiver: String,
issuer: String,
) -> Result<()> {
// Parse inputs
let receiver_account = AccountKeyring::from_str(&receiver)
.expect("unknown to")
.to_account_id();
let issuer_account = AccountKeyring::from_str(&issuer)
.expect("unknown to")
.to_account_id();
let issuer_index = world
.api()
.storage()
.identity()
.identity_index_of(issuer_account, None)
.await?
.unwrap();
let receiver_index = world
.api()
.storage()
.identity()
.identity_index_of(receiver_account, None)
.await?
.unwrap();
let _certification = world
.api()
.storage()
.cert()
.storage_certs_by_issuer(issuer_index, receiver_index, None)
.await?
.ok_or(anyhow::anyhow!(
"no certification found from {} to {}",
issuer,
receiver
));
Ok(())
}
// ============================================================
#[derive(clap::Args)]
struct CustomOpts {
/// Keep running
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment