Skip to content
Snippets Groups Projects
Commit 238413c2 authored by Pascal Engélibert's avatar Pascal Engélibert :bicyclist:
Browse files

test(distance): WiP end2end test

parent f4bed521
Branches
No related tags found
No related merge requests found
Pipeline #32311 failed
Feature: Distance
Scenario: Alice certifies Eve
When alice sends 6 ĞD to dave
When 15 blocks later
When alice creates identity for dave
Then dave identity should be created
Then dave should be certified by alice
When dave confirms his identity with pseudo "dave"
Then dave identity should be confirmed
When bob certifies dave
Then dave should be certified by bob
When alice requests distance evaluation for dave
Then dave should have distance result in 2 sessions
When 30 blocks later
Then dave should have distance result in 1 session
When distance oracle runs
Then dave should have distance result in 1 session
When 30 blocks later
Then dave should have distance result in 0 session
Then dave should have distance ok
When alice validates dave identity
When 3 blocks later
Then dave identity should be validated
// Copyright 2023 Axiom-Team
//
// This file is part of Duniter-v2S.
//
// Duniter-v2S 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.
//
// Duniter-v2S 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 Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
use super::gdev;
use super::gdev::runtime_types::pallet_identity;
use super::*;
use crate::DuniterWorld;
use sp_keyring::AccountKeyring;
use subxt::tx::PairSigner;
pub async fn request_evaluation(client: &Client, from: AccountKeyring, to: u32) -> Result<()> {
let from = PairSigner::new(from.pair());
let _events = create_block_with_extrinsic(
client,
client
.tx()
.create_signed(
&gdev::tx().distance().evaluate_distance(to),
&from,
BaseExtrinsicParamsBuilder::new(),
)
.await?,
)
.await?;
Ok(())
}
......@@ -18,6 +18,7 @@
pub mod balances;
pub mod cert;
pub mod distance;
pub mod identity;
pub mod oneshot;
......@@ -85,6 +86,7 @@ impl Process {
}
}
pub const DISTANCE_ORACLE_LOCAL_PATH: &str = "../target/debug/distance-oracle";
const DUNITER_DOCKER_PATH: &str = "/usr/local/bin/duniter";
const DUNITER_LOCAL_PATH: &str = "../target/debug/duniter";
......@@ -94,7 +96,7 @@ struct FullNode {
ws_port: u16,
}
pub async fn spawn_node(maybe_genesis_conf_file: Option<PathBuf>) -> (Client, Process) {
pub async fn spawn_node(maybe_genesis_conf_file: Option<PathBuf>) -> (Client, Process, u16) {
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() {
......@@ -117,7 +119,7 @@ pub async fn spawn_node(maybe_genesis_conf_file: Option<PathBuf>) -> (Client, Pr
.await
.expect("fail to connect to node");
(client, process)
(client, process, ws_port)
}
pub async fn create_empty_block(client: &Client) -> Result<()> {
......@@ -173,6 +175,9 @@ fn spawn_full_node(
let log_file_path = format!("duniter-v2s-{}.log", ws_port);
let log_file = std::fs::File::create(&log_file_path).expect("fail to create log file");
// Clean previous data
std::fs::remove_dir_all("/tmp/duniter-cucumber").ok();
// Command
let process = Process(
Command::new(duniter_binary_path)
......@@ -180,13 +185,14 @@ fn spawn_full_node(
[
"--no-telemetry",
"--no-prometheus",
"--tmp",
"--port",
&p2p_port.to_string(),
"--rpc-port",
&rpc_port.to_string(),
"--ws-port",
&ws_port.to_string(),
"--base-path",
"/tmp/duniter-cucumber",
]
.iter()
.chain(args),
......@@ -249,3 +255,22 @@ fn wait_until_log_line(expected_log_line: &str, log_file_path: &str, timeout: st
}
}
}
pub fn spawn_distance_oracle(distance_oracle_binary_path: &str, duniter_rpc_port: u16) {
Command::new(distance_oracle_binary_path)
.args(
[
"-u",
&format!("ws://127.0.0.1:{duniter_rpc_port}"),
"-d",
"/tmp/duniter-cucumber/chains/gdev/distance",
"-c",
"2",
]
.iter(),
)
.spawn()
.expect("failed to spawn distance oracle")
.wait()
.unwrap();
}
......@@ -120,12 +120,17 @@ impl World for DuniterWorld {
struct DuniterWorldInner {
client: Client,
process: Process,
ws_port: u16,
}
impl DuniterWorldInner {
async fn new(maybe_genesis_conf_file: Option<PathBuf>) -> Self {
let (client, process) = spawn_node(maybe_genesis_conf_file).await;
DuniterWorldInner { client, process }
let (client, process, ws_port) = spawn_node(maybe_genesis_conf_file).await;
DuniterWorldInner {
client,
process,
ws_port,
}
}
fn kill(&mut self) {
self.process.kill();
......@@ -322,6 +327,28 @@ async fn validate_identity(world: &mut DuniterWorld, from: String, to: String) -
common::identity::validate_identity(world.client(), from, to).await
}
#[when(regex = r#"([a-zA-Z]+) requests distance evaluation for ([a-zA-Z]+)"#)]
async fn request_distance_evaluation(
world: &mut DuniterWorld,
from: String,
to: String,
) -> Result<()> {
// input names to keyrings
let from = AccountKeyring::from_str(&from).expect("unknown from");
let to: u32 = common::identity::get_identity_index(world, to).await?;
common::distance::request_evaluation(world.client(), from, to).await
}
#[when(regex = r#"distance oracle runs"#)]
async fn run_distance_oracle(world: &mut DuniterWorld) -> Result<()> {
common::spawn_distance_oracle(
common::DISTANCE_ORACLE_LOCAL_PATH,
world.inner.as_ref().unwrap().ws_port,
);
Ok(())
}
// ===== then ====
#[then(regex = r"([a-zA-Z]+) should have (\d+) (ĞD|cĞD)")]
......@@ -437,6 +464,66 @@ async fn should_be_certified_by(
}
}
#[then(regex = r"([a-zA-Z]+) should have distance result in (\d+) sessions?")]
async fn should_have_distance_result_in_sessions(
world: &mut DuniterWorld,
who: String,
sessions: u32,
) -> Result<()> {
assert!(sessions < 3, "Session number must be < 3");
let who = AccountKeyring::from_str(&who).unwrap().to_account_id();
let idty_id = world
.read(&gdev::storage().identity().identity_index_of(&who))
.await?
.unwrap();
let current_session = world
.read(&gdev::storage().session().current_index())
.await?
.unwrap_or_default();
let pool = world
.read(&match (current_session + sessions) % 3 {
0 => gdev::storage().distance().evaluation_pool0(),
1 => gdev::storage().distance().evaluation_pool1(),
2 => gdev::storage().distance().evaluation_pool2(),
_ => unreachable!("n%3<3"),
})
.await
.unwrap()
.ok_or_else(|| anyhow::anyhow!("given pool is empty"))?;
for (sample_idty, _) in pool.0 .0 {
if sample_idty == idty_id {
return Ok(());
}
}
Err(anyhow::anyhow!("no evaluation in given pool").into())
}
#[then(regex = r"([a-zA-Z]+) should have distance ok")]
async fn should_have_distance_ok(world: &mut DuniterWorld, who: String) -> Result<()> {
let who = AccountKeyring::from_str(&who).unwrap().to_account_id();
let idty_id = world
.read(&gdev::storage().identity().identity_index_of(&who))
.await?
.unwrap();
if world
.read(&gdev::storage().distance().distance_ok_identities(&idty_id))
.await?
.unwrap()
{
Ok(())
} else {
Err(anyhow::anyhow!("no evaluation in given pool").into())
}
}
use gdev::runtime_types::pallet_identity::types::IdtyStatus;
// status from string
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment