From 33630d5257a3c4a391703a3f505c837228e95615 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo.trentesaux@lilo.org>
Date: Wed, 15 Jun 2022 23:00:56 +0200
Subject: [PATCH] tests(cucumber): add certification (!56)

* tests(cucumber): improve regex

* tests(cucumber): add certification
---
 Cargo.lock                                    |  1 +
 end2end-tests/Cargo.toml                      |  1 +
 .../cucumber-features/certification.feature   | 12 ++++
 end2end-tests/cucumber-genesis/wot.json       | 66 +++++++++++++++++++
 end2end-tests/tests/common/cert.rs            | 43 ++++++++++++
 end2end-tests/tests/common/mod.rs             |  2 +
 end2end-tests/tests/cucumber_tests.rs         | 61 ++++++++++++++++-
 7 files changed, 185 insertions(+), 1 deletion(-)
 create mode 100644 end2end-tests/cucumber-features/certification.feature
 create mode 100644 end2end-tests/cucumber-genesis/wot.json
 create mode 100644 end2end-tests/tests/common/cert.rs

diff --git a/Cargo.lock b/Cargo.lock
index f7fbb6a6a..3908137f8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1517,6 +1517,7 @@ dependencies = [
 name = "duniter-end2end-tests"
 version = "3.0.0"
 dependencies = [
+ "anyhow",
  "async-trait",
  "clap",
  "ctrlc",
diff --git a/end2end-tests/Cargo.toml b/end2end-tests/Cargo.toml
index 8d7b445ee..09aae7a49 100644
--- a/end2end-tests/Cargo.toml
+++ b/end2end-tests/Cargo.toml
@@ -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"
diff --git a/end2end-tests/cucumber-features/certification.feature b/end2end-tests/cucumber-features/certification.feature
new file mode 100644
index 000000000..4f749bece
--- /dev/null
+++ b/end2end-tests/cucumber-features/certification.feature
@@ -0,0 +1,12 @@
+@genesis.wot
+
+Feature: Certification
+
+    Scenario: Dave certifies Alice
+        When dave certifies alice
+        Then alice should be certified by dave
+
+    @ignoreErrors
+    Scenario: Dave certifies Alice (but dave is not certified by charlie, this test should fail)
+        When dave certifies alice
+        Then dave should be certified by charlie
\ No newline at end of file
diff --git a/end2end-tests/cucumber-genesis/wot.json b/end2end-tests/cucumber-genesis/wot.json
new file mode 100644
index 000000000..988e6857f
--- /dev/null
+++ b/end2end-tests/cucumber-genesis/wot.json
@@ -0,0 +1,66 @@
+{
+  "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"
+}
diff --git a/end2end-tests/tests/common/cert.rs b/end2end-tests/tests/common/cert.rs
new file mode 100644
index 000000000..021dfa2ea
--- /dev/null
+++ b/end2end-tests/tests/common/cert.rs
@@ -0,0 +1,43 @@
+// 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(())
+}
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index 078abd747..c3d53e9d6 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -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::*;
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index 09913a11e..13d88217d 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -28,6 +28,8 @@ use std::sync::{
     Arc,
 };
 
+// ===== world =====
+
 #[derive(WorldInit)]
 pub struct DuniterWorld {
     ignore_errors: bool,
@@ -119,6 +121,8 @@ fn parse_amount(amount: u64, unit: &str) -> (u64, bool) {
     }
 }
 
+// ===== 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
@@ -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 {
@@ -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?|UDs?) 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,48 @@ 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_else(|| anyhow::anyhow!("no certification found from {} to {}", issuer, receiver))?;
+
+    Ok(())
+}
+
+// ============================================================
+
 #[derive(clap::Args)]
 struct CustomOpts {
     /// Keep running
-- 
GitLab