Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: set CONTRACTS_NODE_PATH env variable for e2e tests #209

Merged
merged 13 commits into from
Jun 27, 2024
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,22 +225,21 @@ pop call contract -p ./my_contract --contract $INSTANTIATED_CONTRACT_ADDRESS --m
For end-to-end testing you will need to have a Substrate node with `pallet contracts`.
You do not need to run it in the background since the node is started for each test independently.
To install the latest version:

```
cargo install contracts-node --git https://github.com/paritytech/substrate-contracts-node.git
```

If you want to run any other node with `pallet-contracts` you need to change `CONTRACTS_NODE` environment variable:
Run e2e testing on the Smart Contract:

```sh
export CONTRACTS_NODE="YOUR_CONTRACTS_NODE_PATH"
# Run e2e tests for an existing smart contract
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
pop test contract -p ./my_contract --features e2e-tests
```

Run e2e testing on the Smart Contract:

If you want to run a different node with `pallet-contracts` you need to change `CONTRACTS_NODE` environment variable using the `--node` flag:
```sh
# Run e2e tests for an existing smart contract
pop test contract -p ./my_contract --features e2e-tests
pop test contract -p ./my_contract --features e2e-tests --node YOUR_CONTRACTS_NODE_PATH
```

### Pallets
Expand Down
15 changes: 9 additions & 6 deletions crates/pop-cli/src/commands/test/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,31 @@ use std::path::PathBuf;
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")]
#[arg(short = 'f', long = "features", help = "Features for the contract project", value_parser=["e2e-tests"])]
features: Option<String>,
#[arg(
short = 'n',
long = "node",
help = "Path to the contracts node to run e2e tests [default: none]"
)]
node: Option<PathBuf>,
}

impl TestContractCommand {
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
/// Executes the command.
pub(crate) fn execute(self) -> anyhow::Result<&'static str> {
clear_screen()?;

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

test_e2e_smart_contract(&self.path)?;
test_e2e_smart_contract(self.path.as_deref(), self.node.as_deref())?;
outro("End-to-end testing complete")?;
Ok("e2e")
} else {
intro(format!("{}: Starting unit tests", style(" Pop CLI ").black().on_magenta()))?;

test_smart_contract(&self.path)?;
test_smart_contract(self.path.as_deref())?;
outro("Unit testing complete")?;
Ok("unit")
}
Expand Down
3 changes: 2 additions & 1 deletion crates/pop-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ Test an existing Smart Contract:
use pop_contracts::{test_e2e_smart_contract, test_smart_contract};

let contract_path = ...;
let contracts_node_path = ...;

//unit testing
test_smart_contract(&contract_path)?;
//e2e testing
test_e2e_smart_contract(&contract_path)?;
test_e2e_smart_contract(&contract_path, contracts_node_path)?;
```

Deploy and instantiate an existing Smart Contract:
Expand Down
77 changes: 56 additions & 21 deletions crates/pop-contracts/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,95 @@

use crate::errors::Error;
use duct::cmd;
use std::path::PathBuf;
use std::{env, path::Path};

/// Run unit tests of a smart contract.
///
/// # Arguments
///
/// * `path` - location of the smart contract.
pub fn test_smart_contract(path: &Option<PathBuf>) -> Result<(), Error> {
pub fn test_smart_contract(path: Option<&Path>) -> Result<(), Error> {
// Execute `cargo test` command in the specified directory.
cmd("cargo", vec!["test"])
.dir(path.clone().unwrap_or_else(|| PathBuf::from("./")))
.dir(path.unwrap_or_else(|| Path::new("./")))
.run()
.map_err(|e| Error::TestCommand(format!("Cargo test command failed: {}", e)))?;
Ok(())
}

/// Run the e2e tests of a smart contract.
/// Run e2e tests of a smart contract.
///
/// # Arguments
///
/// * `path` - location of the smart contract.
AlexD10S marked this conversation as resolved.
Show resolved Hide resolved
pub fn test_e2e_smart_contract(path: &Option<PathBuf>) -> Result<(), Error> {
/// * `node` - location of the contracts node binary.
pub fn test_e2e_smart_contract(path: Option<&Path>, node: Option<&Path>) -> Result<(), Error> {
// Set the environment variable `CONTRACTS_NODE` to the path of the contracts node.
if let Some(node) = node {
env::set_var("CONTRACTS_NODE", node);
}
// Execute `cargo test --features=e2e-tests` command in the specified directory.
cmd("cargo", vec!["test", "--features=e2e-tests"])
.dir(path.clone().unwrap_or_else(|| PathBuf::from("./")))
.dir(path.unwrap_or_else(|| Path::new("./")))
.run()
.map_err(|e| Error::TestCommand(format!("Cargo test command failed: {}", e)))?;
Ok(())
}

#[cfg(feature = "unit_contract")]
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile;

fn setup_test_environment() -> Result<tempfile::TempDir, Error> {
#[test]
fn test_smart_contract_works() -> Result<(), Error> {
let temp_dir = tempfile::tempdir()?;
let temp_contract_dir = temp_dir.path().join("test_contract");
fs::create_dir(&temp_contract_dir)?;
crate::create_smart_contract(
"test_contract",
temp_contract_dir.as_path(),
&crate::Template::Standard,
)?;
Ok(temp_dir)
cmd("cargo", ["new", "test_contract", "--bin"]).dir(temp_dir.path()).run()?;
// Run unit tests for the smart contract in the temporary contract directory.
test_smart_contract(Some(&temp_dir.path().join("test_contract")))?;
Ok(())
}

#[test]
fn test_contract_test() -> Result<(), Error> {
let temp_contract_dir = setup_test_environment()?;
// Run unit tests for the smart contract in the temporary contract directory.
test_smart_contract(&Some(temp_contract_dir.path().join("test_contract")))?;
fn test_smart_contract_wrong_folder() -> Result<(), Error> {
let temp_dir = tempfile::tempdir()?;
assert!(matches!(
test_smart_contract(Some(&temp_dir.path().join(""))),
Err(Error::TestCommand(..))
));
Ok(())
}

#[test]
fn test_e2e_smart_contract_set_env_variable() -> Result<(), Error> {
let temp_dir = tempfile::tempdir()?;
cmd("cargo", ["new", "test_contract", "--bin"]).dir(temp_dir.path()).run()?;
// Ignore 2e2 testing in this scenario, will fail. Only test if the environment variable CONTRACTS_NODE is set.
let err = test_e2e_smart_contract(Some(&temp_dir.path().join("test_contract")), None);
assert!(err.is_err());
// The environment variable `CONTRACTS_NODE` should not be set.
assert!(env::var("CONTRACTS_NODE").is_err());
let err = test_e2e_smart_contract(
Some(&temp_dir.path().join("test_contract")),
Some(&Path::new("/path/to/contracts-node")),
);
assert!(err.is_err());
// The environment variable `CONTRACTS_NODE` should has been set.
assert_eq!(
env::var("CONTRACTS_NODE").unwrap(),
Path::new("/path/to/contracts-node").display().to_string()
);
Ok(())
}

#[test]
fn test_e2e_smart_contract_fails_no_e2e_tests() -> Result<(), Error> {
let temp_dir = tempfile::tempdir()?;
cmd("cargo", ["new", "test_contract", "--bin"]).dir(temp_dir.path()).run()?;
assert!(matches!(
test_e2e_smart_contract(Some(&temp_dir.path().join("test_contract")), None),
Err(Error::TestCommand(..))
));
Ok(())
}
}
Loading