Skip to content

Commit

Permalink
feat(contract-e2e): auto-source substrate-contracts-node with e2e tes…
Browse files Browse the repository at this point in the history
…ts (#254)

* feat(contracts-e2e): auto-source contracts-node binary if not present

* chore(contract-e2e): deprecate --features e2e-tests

* fix: remove unnecessary async function

* refactor(contracts-node): reduce duplicated code with binary sourcing (#255)

* refactor(up-contract): use Binary struct from parachains -> up -> sourcing to auto-launch contracts-node

* refactor(contracts_node): reduce duplicated code -- checkpoint, not working

* refactor(contracts_node): use Binary and Source structs for substrate-contracts-node

* chore: small changes

* chore: small changes

* chore: add e2e help, prevent cmd output, and cleanup

* refactor(contracts_node): introduce helper to download contracts node if it does not exist

* refactor(sourcing): move sourcing functionality to `pop-common` (#258)

* refactor(sourcing): move sourcing to pop-common

* refactor(sourcing-tests): move sourcing tests to pop-common

* refactor(sourcing): better imports

* refactor(sourcing): clean sourcing module with better component categorization. Other cleanups

* fix: failing tests

* fix: needed to download contracts_node for test

* fix: add contracts feature, more tests need s c n downloaded

* fix: manually create temp dir

* fix: append pop_tmp folder in tests

* refactor: e2e test improvements (#263)

* fix: use async sleep in async context

* refactor: remove unnecessary module

* refactor: standardise sourcing ux

* fix: temp dir issue

* chore: component locations, minor improvements

---------

Co-authored-by: Frank Bell <[email protected]>
  • Loading branch information
peterwht and evilrobot-01 authored Jul 25, 2024
1 parent a2b88f8 commit 5d2a940
Show file tree
Hide file tree
Showing 28 changed files with 921 additions and 766 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/pop-cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Command {
},
#[cfg(feature = "contract")]
Self::Test(args) => match args.command {
test::Command::Contract(cmd) => match cmd.execute() {
test::Command::Contract(cmd) => match cmd.execute().await {
Ok(feature) => Ok(json!(feature)),
Err(e) => Err(e),
},
Expand Down
37 changes: 33 additions & 4 deletions crates/pop-cli/src/commands/test/contract.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
// SPDX-License-Identifier: GPL-3.0

use crate::style::style;
use crate::{common::contracts::check_contracts_node_and_prompt, style::style};
use clap::Args;
use cliclack::{clear_screen, intro, outro};
use cliclack::{clear_screen, intro, log::warning, outro};
use pop_contracts::{test_e2e_smart_contract, test_smart_contract};
use std::path::PathBuf;
#[cfg(not(test))]
use {std::time::Duration, tokio::time::sleep};

#[derive(Args)]
pub(crate) struct TestContractCommand {
#[arg(short = 'p', long, help = "Path for the contract project [default: current directory]")]
path: Option<PathBuf>,
#[arg(short = 'f', long = "features", help = "Features for the contract project", value_parser=["e2e-tests"])]
/// [DEPRECATED] Run e2e tests
#[arg(short = 'f', long = "features", value_parser=["e2e-tests"])]
features: Option<String>,
/// Run end-to-end tests
#[arg(short = 'e', long = "e2e")]
e2e: bool,
#[arg(
short = 'n',
long = "node",
Expand All @@ -22,13 +28,36 @@ pub(crate) struct TestContractCommand {

impl TestContractCommand {
/// Executes the command.
pub(crate) fn execute(self) -> anyhow::Result<&'static str> {
pub(crate) async fn execute(mut self) -> anyhow::Result<&'static str> {
clear_screen()?;

let mut show_deprecated = false;
if self.features.is_some() && self.features.clone().unwrap().contains("e2e-tests") {
show_deprecated = true;
self.e2e = true;
#[cfg(not(test))]
sleep(Duration::from_secs(3)).await;
}

if self.e2e {
intro(format!(
"{}: Starting end-to-end tests",
style(" Pop CLI ").black().on_magenta()
))?;

if show_deprecated {
warning("NOTE: --features e2e-tests is deprecated. Use --e2e instead.")?;
}

let maybe_node_path = check_contracts_node_and_prompt().await?;
if let Some(node_path) = maybe_node_path {
if node_path != PathBuf::new() {
self.node = Some(node_path);
}
} else {
warning("🚫 substrate-contracts-node is necessary to run e2e tests. Will try to run tests anyway...")?;
}

test_e2e_smart_contract(self.path.as_deref(), self.node.as_deref())?;
outro("End-to-end testing complete")?;
Ok("e2e")
Expand Down
23 changes: 21 additions & 2 deletions crates/pop-cli/src/commands/up/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::{
cli::{traits::Cli as _, Cli},
common::contracts::check_contracts_node_and_prompt,
style::style,
};
use clap::Args;
Expand Down Expand Up @@ -124,10 +125,28 @@ impl UpContractCommand {
// Update url to that of the launched node
self.url = Url::parse(DEFAULT_URL).expect("default url is valid");

let log = NamedTempFile::new()?;

// default to standalone binary, if it exists.
let mut binary_path = PathBuf::from("substrate-contracts-node");

// uses the cache location
let maybe_node_path = check_contracts_node_and_prompt().await?;
if let Some(node_path) = maybe_node_path {
if node_path != PathBuf::new() {
binary_path = node_path;
}
} else {
Cli.outro_cancel(
"🚫 You need to specify an accessible endpoint to deploy the contract.",
)?;
return Ok(());
}

let spinner = spinner();
spinner.start("Starting local node...");
let log = NamedTempFile::new()?;
let process = run_contracts_node(crate::cache()?, Some(log.as_file())).await?;

let process = run_contracts_node(binary_path, Some(log.as_file())).await?;
let bar = Style::new().magenta().dim().apply_to(Emoji("│", "|"));
spinner.stop(format!(
"Local node started successfully:{}",
Expand Down
3 changes: 2 additions & 1 deletion crates/pop-cli/src/commands/up/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use cliclack::{
};
use console::{Emoji, Style, Term};
use duct::cmd;
use pop_parachains::{Error, IndexSet, NetworkNode, Status, Zombienet};
use pop_common::Status;
use pop_parachains::{Error, IndexSet, NetworkNode, Zombienet};
use std::{path::PathBuf, time::Duration};
use tokio::time::sleep;

Expand Down
41 changes: 41 additions & 0 deletions crates/pop-cli/src/common/contracts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use cliclack::{confirm, log::warning, spinner};
use pop_contracts::{does_contracts_node_exist, download_contracts_node};
use std::path::PathBuf;

/// Helper function to check if the contracts node binary exists, and if not download it.
/// returns:
/// - Some("") if the standalone binary exists
/// - Some(binary_cache_location) if the binary exists in pop's cache
/// - None if the binary does not exist
pub async fn check_contracts_node_and_prompt() -> anyhow::Result<Option<PathBuf>> {
let mut node_path = None;

// if the contracts node binary does not exist, prompt the user to download it
let maybe_contract_node_path = does_contracts_node_exist(crate::cache()?);
if maybe_contract_node_path == None {
warning("⚠️ The substrate-contracts-node binary is not found.")?;
if confirm("📦 Would you like to source it automatically now?")
.initial_value(true)
.interact()?
{
let spinner = spinner();
spinner.start("📦 Sourcing substrate-contracts-node...");

let cache_path = crate::cache()?;
let binary = download_contracts_node(cache_path.clone()).await?;

spinner.stop(format!(
"✅ substrate-contracts-node successfully sourced. Cached at: {}",
binary.path().to_str().unwrap()
));
node_path = Some(binary.path());
}
} else {
if let Some(contract_node_path) = maybe_contract_node_path {
// If the node_path is not empty (cached binary). Otherwise, the standalone binary will be used by cargo-contract.
node_path = Some(contract_node_path.0);
}
}

Ok(node_path)
}
2 changes: 2 additions & 0 deletions crates/pop-cli/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(feature = "contract")]
pub mod contracts;
1 change: 1 addition & 0 deletions crates/pop-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use {
mod cli;
#[cfg(any(feature = "parachain", feature = "contract"))]
mod commands;
mod common;
mod style;

#[tokio::main]
Expand Down
11 changes: 6 additions & 5 deletions crates/pop-cli/tests/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use anyhow::Result;
use assert_cmd::Command;
use pop_common::templates::Template;
use pop_contracts::{
dry_run_gas_estimate_instantiate, instantiate_smart_contract, run_contracts_node,
set_up_deployment, Contract, UpOpts,
download_contracts_node, dry_run_gas_estimate_instantiate, instantiate_smart_contract,
run_contracts_node, set_up_deployment, Contract, UpOpts,
};
use std::{path::Path, process::Command as Cmd};
use std::{env::temp_dir, path::Path, process::Command as Cmd};
use strum::VariantArray;
use url::Url;

Expand Down Expand Up @@ -43,8 +43,9 @@ async fn contract_lifecycle() -> Result<()> {
assert!(temp_dir.join("test_contract/target/ink/test_contract.json").exists());

// Run the contracts node
let cache = temp_dir.join("cache");
let process = run_contracts_node(cache, None).await?;
let node_path = download_contracts_node(temp_dir.to_path_buf().clone()).await?;
let process = run_contracts_node(node_path.path(), None).await?;

// Only upload the contract
// pop up contract --upload-only
Command::cargo_bin("pop")
Expand Down
5 changes: 5 additions & 0 deletions crates/pop-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
cargo_toml.workspace = true
duct.workspace = true
flate2.workspace = true
git2.workspace = true
git2_credentials.workspace = true
regex.workspace = true
reqwest.workspace = true
serde_json.workspace = true
serde.workspace = true
strum.workspace = true
tar.workspace = true
tempfile.workspace = true
thiserror.workspace = true
tokio.workspace = true
url.workspace = true

[dev-dependencies]
mockito.workspace = true
strum_macros.workspace = true
tempfile.workspace = true
10 changes: 9 additions & 1 deletion crates/pop-common/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-3.0

use crate::templates;
use crate::{sourcing, templates};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("Anyhow error: {0}")]
AnyhowError(#[from] anyhow::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("a git error occurred: {0}")]
Expand All @@ -17,6 +19,12 @@ pub enum Error {
ManifestError(#[from] cargo_toml::Error),
#[error("ParseError error: {0}")]
ParseError(#[from] url::ParseError),
#[error("SourceError error: {0}")]
SourceError(#[from] sourcing::Error),
#[error("TemplateError error: {0}")]
TemplateError(#[from] templates::Error),
#[error("Unsupported command: {0}")]
UnsupportedCommand(String),
#[error("Unsupported platform: {arch} {os}")]
UnsupportedPlatform { arch: &'static str, os: &'static str },
}
Loading

0 comments on commit 5d2a940

Please sign in to comment.