From dab1c74b2a8f70a8ff7362e4de3369a9ca7b827d Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 10 Oct 2022 03:27:13 +0200 Subject: [PATCH] Remove Rust integration testing behaviour from `forc test` in anticipation of unit testing support (#2264) This PR is a WIP that aims to address the first 3 steps in #1833. This is in anticipation of using `forc test` to support unit testing (rather than Rust integration testing) #1832. The `forc test` command remains for now, but outputs a message explaining that the command is now reserved for unit testing and links to the issues above. ## TODO - [x] Create a new `sway-test-rs` repo or similar that can be used as a `cargo generate` template. - [x] Update Rust integration testing docs in the Sway book to describe how to use the `cargo generate` command to easily add Sway integration testing to an existing Rust project. ## Follow-up - Create a `forc-test-rs` crate that re-exports and extends `fuels` with useful `forc` functionality for integration testing (e.g. re-exporting `forc_pkg::build` to ensure sway code is built and available under `out/` at the start of testing). --- .github/workflows/ci.yml | 20 +- docs/src/introduction/forc_project.md | 19 +- docs/src/testing/index.md | 11 +- docs/src/testing/testing-with-rust.md | 181 ++++++++++++++++-- forc-util/src/lib.rs | 4 - forc/src/cli/commands/clean.rs | 4 +- forc/src/cli/commands/test.rs | 125 ++++-------- forc/src/ops/forc_clean.rs | 14 +- forc/src/ops/forc_init.rs | 18 -- forc/src/ops/forc_template.rs | 16 +- forc/src/utils/defaults.rs | 89 --------- .../examples/forc_init.md | 14 +- .../examples/forc_new.md | 11 +- .../examples/forc_test.md | 7 - templates/README.md | 4 + templates/sway-test-rs/README.md | 11 ++ templates/sway-test-rs/template/Cargo.toml | 16 ++ .../sway-test-rs/template/cargo-generate.toml | 3 + .../sway-test-rs/template/tests/harness.rs | 40 ++++ 19 files changed, 312 insertions(+), 295 deletions(-) delete mode 100644 scripts/mdbook-forc-documenter/examples/forc_test.md create mode 100644 templates/README.md create mode 100644 templates/sway-test-rs/README.md create mode 100644 templates/sway-test-rs/template/Cargo.toml create mode 100644 templates/sway-test-rs/template/cargo-generate.toml create mode 100644 templates/sway-test-rs/template/tests/harness.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a655c6974d4..5443389d2fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,15 +158,25 @@ jobs: run: forc new test-proj - name: Update project forc manifest to use local sway-lib-std run: echo "std = { path = \"../sway-lib-std/\" }" >> test-proj/Forc.toml + - name: Build test project + run: forc build --path test-proj + # TODO: Re-add this upon landing unit test support: #1832 + # - name: Run test project's test suite + # run: (cd test-proj && forc test) + - name: Install cargo-generate + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-generate + - name: Generate Rust Integration Tests + run: (cd test-proj && cargo generate --init --path ../templates/sway-test-rs --name test-proj) - name: Update project cargo manifest with workspace run: | echo " [workspace]" >> test-proj/Cargo.toml - - name: Build test project - run: forc build --path test-proj - - name: Run test project's test suite - run: cd test-proj && forc test + - name: Build and Run Default Integration Test + run: (cd test-proj && cargo test) cargo-build-workspace: runs-on: ubuntu-latest @@ -212,7 +222,7 @@ jobs: crate: cargo-toml-lint version: "0.1" - name: Run Cargo.toml linter - run: git ls-files | grep Cargo.toml$ | xargs --verbose -n 1 cargo-toml-lint + run: git ls-files | grep Cargo.toml$ | grep -v 'templates/sway-test-rs' | xargs --verbose -n 1 cargo-toml-lint cargo-fmt-check: runs-on: ubuntu-latest diff --git a/docs/src/introduction/forc_project.md b/docs/src/introduction/forc_project.md index 6d3dbcbf220..351dd6b9c40 100644 --- a/docs/src/introduction/forc_project.md +++ b/docs/src/introduction/forc_project.md @@ -11,12 +11,9 @@ Here is the project that Forc has initialized: ```console $ cd my-fuel-project $ tree . -├── Cargo.toml ├── Forc.toml -├── src -│   └── main.sw -└── tests - └── harness.rs +└── src +    └── main.sw ``` `Forc.toml` is the _manifest file_ (similar to `Cargo.toml` for Cargo or `package.json` for Node), and defines project metadata such as the project name and dependencies. @@ -76,15 +73,3 @@ data_0 .word 559005003 Compiled contract "my-fuel-project". Bytecode size is 60 bytes. ``` - -To test this contract, use `forc test`: - -```console -$ forc test -running 1 test -test can_get_contract_id ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s -``` - -The `forc test` command tests the contract using the Rust SDK test harness that lives under `tests/`. The default test harness `harness.rs` contains boilerplate code to get you started but doesn't actually call any contract methods. For additional information on testing contracts using the Rust SDK, refer to the [Testing with Rust](../testing/testing-with-rust.md) section. diff --git a/docs/src/testing/index.md b/docs/src/testing/index.md index fb2ccd24bb7..555704f4f5b 100644 --- a/docs/src/testing/index.md +++ b/docs/src/testing/index.md @@ -1,5 +1,14 @@ # Testing -Testing your Sway contracts can be done with the Rust SDK. +Sway aims to provide facilities for both unit testing and integration testing. + +**Unit testing** refers to "in-language" testing which can be triggered via the +`forc test` command. Sway unit testing is currently a high-priority +work-in-progress, you can follow along at [this +issue](https://github.com/FuelLabs/sway/issues/1832). + +**Integration testing** refers to the testing of your Sway project's integration +within some wider application. You can add integration testing to your Sway+Rust +projects today using the cargo generate template and Rust SDK. - [Testing with Rust](./testing-with-rust.md) diff --git a/docs/src/testing/testing-with-rust.md b/docs/src/testing/testing-with-rust.md index 7a19051fc46..28227ff1e7d 100644 --- a/docs/src/testing/testing-with-rust.md +++ b/docs/src/testing/testing-with-rust.md @@ -1,30 +1,169 @@ # Testing with Rust -If you look again at the project structure when you create a [new Forc project](../introduction/forc_project.md) with `forc new`, you can see a directory called `tests/`: +A common use of Sway is for writing contracts or scripts that exist as part of a +wider Rust application. In order to test the interaction between our Sway code +and our Rust code we can add integration testing. -```plaintext -$ forc new my-fuel-project +## Adding Rust Integration Testing + +To add Rust integration testing to a Forc project we can use [the `sway-test-rs` +cargo generate +template](https://github.com/FuelLabs/sway/tree/master/templates/sway-test-rs). +This template makes it easy for Sway devs to add the boilerplate required when +setting up their Rust integration testing. + +Let's add a Rust integration test to [the fresh project we created in the +introduction](../introduction/forc_project.md). + +### 1. Enter the project + +To recap, here's what our empty project looks like: + +```console $ cd my-fuel-project $ tree . +├── Forc.toml +└── src +    └── main.sw +``` + +### 2. Install `cargo generate` + +We're going to add a Rust integration test harness using a cargo generate +template. Let's make sure we have the `cargo generate` command installed! + +```console +cargo install cargo-generate +``` + +> _**Note**: You can learn more about cargo generate by visiting [its +> repository](https://github.com/cargo-generate/cargo-generate)._ + +### 3. Generate the test harness + +Let's generate the default test harness with the following: + +```console +cargo generate --init fuellabs/sway templates/sway-test-rs --name my-fuel-project +``` + +If all goes well, the output should look as follows: + +```console +⚠️ Favorite `fuellabs/sway` not found in config, using it as a git repository: https://github.com/fuellabs/sway +🤷 Project Name : my-fuel-project +🔧 Destination: /home/user/path/to/my-fuel-project ... +🔧 Generating template ... +[1/3] Done: Cargo.toml +[2/3] Done: tests/harness.rs +[3/3] Done: tests +🔧 Moving generated files into: `/home/user/path/to/my-fuel-project`... +✨ Done! New project created /home/user/path/to/my-fuel-project +``` + +Let's have a look at the result: + +```console +$ tree . +├── Cargo.toml +├── Forc.toml +├── src +│   └── main.sw +└── tests + └── harness.rs +``` + +We have two new files! + +- The `Cargo.toml` is the manifest for our new test harness and specifies the + required dependencies including `fuels` the Fuel Rust SDK. +- The `tests/harness.rs` contains some boilerplate test code to get us started, + though doesn't call any contract methods just yet. + +### 4. Build the forc project + +Before running the tests, we need to build our contract so that the necessary +ABI, storage and bytecode artifacts are available. We can do so with `forc +build`: + +```console +$ forc build + Creating a new `Forc.lock` file. (Cause: lock file did not exist) + Adding core + Adding std git+https://github.com/fuellabs/sway?tag=v0.24.5#e695606d8884a18664f6231681333a784e623bc9 + Created new lock file at /home/user/path/to/my-fuel-project/Forc.lock + Compiled library "core". + Compiled library "std". + Compiled contract "my-fuel-project". + Bytecode size is 60 bytes. +``` + +At this point, our project should look like the following: + +```console +$ tree ├── Cargo.toml +├── Forc.lock ├── Forc.toml +├── out +│   └── debug +│   ├── my-fuel-project-abi.json +│   ├── my-fuel-project.bin +│   └── my-fuel-project-storage_slots.json ├── src │   └── main.sw └── tests └── harness.rs ``` -Note that this is also a Rust package, hence the existence of a `Cargo.toml` (Rust manifest file) in the project root directory. The `Cargo.toml` in the root directory contains necessary Rust dependencies to enable you to write Rust-based tests using our [Rust SDK](https://fuellabs.github.io/fuels-rs/latest), (`fuels-rs`). +We now have an `out` directory with our required JSON files! + +> _**Note**: This step may no longer be required in the future as we plan to +> enable the integration testing to automatically build the artifacts as +> necessary so that files like the ABI JSON are always up to date._ + +### 5. Build and run the tests + +Now we're ready to build and run the default integration test. + +```console +$ cargo test + Updating crates.io index + Compiling version_check v0.9.4 + Compiling proc-macro2 v1.0.46 + Compiling quote v1.0.21 + ... + Compiling fuels v0.24.0 + Compiling my-fuel-project v0.1.0 (/home/user/path/to/my-fuel-project) + Finished test [unoptimized + debuginfo] target(s) in 1m 03s + Running tests/harness.rs (target/debug/deps/integration_tests-373971ac377845f7) + +running 1 test +test can_get_contract_id ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.36s +``` + +> _**Note**: The first time we run `cargo test`, cargo will spend some time +> fetching and building the dependencies for Fuel's Rust SDK. This might take a +> while, but only the first time!_ + +If all went well, we should see some output that looks like the above! -These tests can be run using `forc test` which will look for Rust tests under the `tests/` directory (created automatically with `forc new` and prepopulated with boilerplate code). +## Writing Tests -For example, let's write tests against the following contract, written in Sway. This can be done in the pregenerated `src/main.sw` or in a new file in `src`. In the case of the latter, update the `entry` field in `Forc.toml` to point at the new contract. +Now that we've learned how to setup Rust integration testing in our project, +let's try to write some of our own tests! + +First, let's update our contract code with a simple counter example: ```sway {{#include ../../../examples/counter/src/main.sw}} ``` -Our `tests/harness.rs` file could look like: +To test our `initialize_counter` and `increment_counter` contract methods from +the Rust test harness, we could update our `tests/harness.rs` file with the +following: @@ -58,13 +197,13 @@ async fn get_contract_instance() -> (TestContract, ContractId) { .await .unwrap(); - let instance = TestContractBuilder::new(id.to_string(), wallet).build(); + let instance = TestContract::new(id.to_string(), wallet); (instance, id.into()) } #[tokio::test] -async fn can_get_contract_id() { +async fn initialize_and_increment() { let (contract_instance, _id) = get_contract_instance().await; // Now you have an instance of your contract you can use to test each function @@ -87,13 +226,27 @@ async fn can_get_contract_id() { } ``` -Then, in the root of our project, running `forc test` will run the test above, compiling and deploying the contract to a local Fuel network, and calling the ABI methods against the contract deployed in there: +Let's build our project once more and run the test: + +```console +forc build +``` ```console -$ forc test -... +$ cargo test + Compiling my-fuel-project v0.1.0 (/home/mindtree/programming/sway/my-fuel-project) + Finished test [unoptimized + debuginfo] target(s) in 11.61s + Running tests/harness.rs (target/debug/deps/integration_tests-373971ac377845f7) + running 1 test -test can_get_contract_id ... ok +test initialize_and_increment ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.25s ``` + +When cargo runs our test, our test uses the SDK to spin up a local in-memory +Fuel network, deploy our contract to it, and call the contract methods via the +ABI. + +You can add as many functions decorated with `#[tokio::test]` as you like, and +`cargo test` will automatically test each of them! diff --git a/forc-util/src/lib.rs b/forc-util/src/lib.rs index 30f7f91639f..0b40e8c98bd 100644 --- a/forc-util/src/lib.rs +++ b/forc-util/src/lib.rs @@ -44,10 +44,6 @@ pub fn find_parent_dir_with_file(starter_path: &Path, file_name: &str) -> Option pub fn find_manifest_dir(starter_path: &Path) -> Option { find_parent_dir_with_file(starter_path, constants::MANIFEST_FILE_NAME) } -/// Continually go up in the file tree until a Cargo manifest file is found. -pub fn find_cargo_manifest_dir(starter_path: &Path) -> Option { - find_parent_dir_with_file(starter_path, constants::TEST_MANIFEST_FILE_NAME) -} pub fn is_sway_file(file: &Path) -> bool { let res = file.extension(); diff --git a/forc/src/cli/commands/clean.rs b/forc/src/cli/commands/clean.rs index cfd5909298f..5490eeee4e3 100644 --- a/forc/src/cli/commands/clean.rs +++ b/forc/src/cli/commands/clean.rs @@ -2,9 +2,7 @@ use crate::ops::forc_clean; use anyhow::Result; use clap::Parser; -/// Removes the default forc compiler output artifact directory, i.e. `/out`. Also -/// calls `cargo clean` which removes the `target` directory generated by `cargo` when running -/// tests. +/// Removes the default forc compiler output artifact directory, i.e. `/out`. #[derive(Debug, Parser)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. diff --git a/forc/src/cli/commands/test.rs b/forc/src/cli/commands/test.rs index abc3bcfe0c8..e9d99b0fe90 100644 --- a/forc/src/cli/commands/test.rs +++ b/forc/src/cli/commands/test.rs @@ -1,104 +1,43 @@ -use crate::ops::forc_build; use anyhow::{bail, Result}; use clap::Parser; -use std::io::{BufRead, BufReader}; -use std::process; -use std::thread; -use tracing::{error, info}; -/// Run Rust-based tests on current project. -/// As of now, `forc test` is a simple wrapper on -/// `cargo test`; `forc new` also creates a rust -/// package under your project, named `tests`. -/// You can opt to either run these Rust tests by -/// using `forc test` or going inside the package -/// and using `cargo test`. +/// Run the Sway unit tests for the current project. +/// +/// NOTE: This feature is not yet implemented. Track progress at the following link: +/// https://github.com/FuelLabs/sway/issues/1832 +/// +/// NOTE: Previously this command was used to support Rust integration testing, however the +/// provided behaviour served no benefit over running `cargo test` directly. The proposal to change +/// the behaviour to support unit testing can be found at the following link: +/// https://github.com/FuelLabs/sway/issues/1833 +/// +/// Sway unit tests are functions decorated with the `#[test_script]` attribute. Each test is +/// compiled as an independent `script` program and has access to the namespace of the module in +/// which it is declared. Unit tests declared within `contract` projects may also call directly +/// into their associated contract's ABI. +/// +/// Upon successful compilation, test scripts are executed to their completion. A test is +/// considered a failure in the case that a revert (`rvrt`) instruction is encountered during +/// execution. Otherwise, it is considered a success. #[derive(Debug, Parser)] pub(crate) struct Command { - /// If specified, only run tests containing this string in their names - pub test_name: Option, - /// Options passed through to the `cargo test` invocation. - /// - /// E.g. Given the following: - /// - /// `forc test --cargo-test-opts="--color always"` - /// - /// The `--color always` option is forwarded to `cargo test` like so: - /// - /// `cargo test --color always` - #[clap(long)] - pub cargo_test_opts: Option, - /// All trailing arguments following `--` are collected within this argument. - /// - /// E.g. Given the following: - /// - /// `forc test -- foo bar baz` - /// - /// The arguments `foo`, `bar` and `baz` are forwarded on to `cargo test` like so: - /// - /// `cargo test -- foo bar baz` - #[clap(raw = true)] - pub cargo_test_args: Vec, + /// When specified, only tests containing the given string will be executed. + pub filter: Option, } -pub(crate) fn exec(command: Command) -> Result<()> { - // Ensure the project builds before running tests. - forc_build::build(Default::default())?; +pub(crate) fn exec(_command: Command) -> Result<()> { + bail!( + r#" +Sway unit testing is not yet implemented. Track progress at the following link: - let mut cmd = process::Command::new("cargo"); - cmd.arg("test"); +https://github.com/FuelLabs/sway/issues/1832 - // Pass through cargo test options. - let mut user_specified_color_opt = false; - if let Some(opts) = command.cargo_test_opts { - user_specified_color_opt = opts.contains("--color"); - for opt in opts.split_whitespace() { - cmd.arg(&opt); - } - } +NOTE: Previously this command was used to support Rust integration testing, +however the provided behaviour served no benefit over running `cargo test` +directly. The proposal to change the behaviour to support unit testing can be +found at the following link: - // If the coloring option wasn't specified by the user, enable it ourselves. This is useful as - // `cargo test`'s coloring is disabled by default when run as a child process. - if !user_specified_color_opt { - cmd.args(&["--color", "always"]); - } - - // Pass through test name. - if let Some(ref name) = command.test_name { - cmd.arg(name); - } - - // Pass through cargo test args. - if !command.cargo_test_args.is_empty() { - cmd.arg("--"); - cmd.args(&command.cargo_test_args); - } - - let mut child = cmd - .stdout(process::Stdio::piped()) - .stderr(process::Stdio::piped()) - .spawn() - .unwrap(); - - let out = BufReader::new(child.stdout.take().unwrap()); - let err = BufReader::new(child.stderr.take().unwrap()); - - // Reading stderr on a separate thread so we keep things non-blocking - let thread = thread::spawn(move || { - err.lines().for_each(|line| error!("{}", line.unwrap())); - }); - - out.lines().for_each(|line| info!("{}", line.unwrap())); - thread.join().unwrap(); - - let child_success = match child.try_wait() { - Ok(Some(returned_status)) => returned_status.success(), - Ok(None) => child.wait().unwrap().success(), - Err(_) => false, - }; - - match child_success { - true => Ok(()), - false => bail!("child test process failed"), - } +https://github.com/FuelLabs/sway/issues/1833 + "# + ); } diff --git a/forc/src/ops/forc_clean.rs b/forc/src/ops/forc_clean.rs index 01b4e20d733..fabeef1c6fa 100644 --- a/forc/src/ops/forc_clean.rs +++ b/forc/src/ops/forc_clean.rs @@ -1,7 +1,7 @@ use crate::cli::CleanCommand; use anyhow::{anyhow, bail, Result}; -use forc_util::{default_output_directory, find_cargo_manifest_dir, find_manifest_dir}; -use std::{path::PathBuf, process}; +use forc_util::{default_output_directory, find_manifest_dir}; +use std::path::PathBuf; use sway_utils::MANIFEST_FILE_NAME; pub fn clean(command: CleanCommand) -> Result<()> { @@ -29,15 +29,5 @@ pub fn clean(command: CleanCommand) -> Result<()> { let out_dir = default_output_directory(&manifest_dir); let _ = std::fs::remove_dir_all(out_dir); - // Run `cargo clean`, forwarding stdout and stderr (`cargo clean` doesn't appear to output - // anything as of writing this). - if find_cargo_manifest_dir(&this_dir).is_some() { - process::Command::new("cargo") - .arg("clean") - .stderr(process::Stdio::inherit()) - .stdout(process::Stdio::inherit()) - .output()?; - } - Ok(()) } diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index 037c40492c3..79311ddf427 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -89,9 +89,6 @@ pub fn init(command: InitCommand) -> Result<()> { // Make a new directory for the project fs::create_dir_all(Path::new(&project_dir).join("src"))?; - // Make directory for tests - fs::create_dir_all(Path::new(&project_dir).join("tests"))?; - // Insert default manifest file match program_type { Library => fs::write( @@ -104,12 +101,6 @@ pub fn init(command: InitCommand) -> Result<()> { )?, } - // Insert default test manifest file - fs::write( - Path::new(&project_dir).join(constants::TEST_MANIFEST_FILE_NAME), - defaults::default_tests_manifest(&project_name), - )?; - match program_type { Contract => fs::write( Path::new(&project_dir) @@ -137,15 +128,6 @@ pub fn init(command: InitCommand) -> Result<()> { )?, } - // Insert default test function - let harness_path = Path::new(&project_dir).join("tests").join("harness.rs"); - fs::write(&harness_path, defaults::default_test_program(&project_name))?; - - debug!( - "\nCreated test harness at {}", - harness_path.canonicalize()?.display() - ); - // Ignore default `out` and `target` directories created by forc and cargo. let gitignore_path = Path::new(&project_dir).join(".gitignore"); // Append to existing gitignore if it exists otherwise create a new one. diff --git a/forc/src/ops/forc_template.rs b/forc/src/ops/forc_template.rs index ddf3e12fe53..0101b662622 100644 --- a/forc/src/ops/forc_template.rs +++ b/forc/src/ops/forc_template.rs @@ -1,15 +1,14 @@ use crate::cli::TemplateCommand; -use crate::utils::defaults; use anyhow::{anyhow, Context, Result}; use forc_pkg::{ fetch_git, fetch_id, find_dir_within, git_commit_path, pin_git, PackageManifest, SourceGit, }; use forc_util::validate_name; use fs_extra::dir::{copy, CopyOptions}; +use std::env; use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::{env, fs}; use sway_utils::constants; use tracing::info; use url::Url; @@ -73,19 +72,6 @@ pub fn init(command: TemplateCommand) -> Result<()> { edit_forc_toml(&target_dir, &command.project_name, &whoami::realname())?; if target_dir.join("test").exists() { edit_cargo_toml(&target_dir, &command.project_name, &whoami::realname())?; - } else { - // Create the tests directory, harness.rs and Cargo.toml file - fs::create_dir_all(target_dir.join("tests"))?; - - fs::write( - target_dir.join("tests").join("harness.rs"), - defaults::default_test_program(&command.project_name), - )?; - - fs::write( - target_dir.join("Cargo.toml"), - defaults::default_tests_manifest(&command.project_name), - )?; } Ok(()) } diff --git a/forc/src/utils/defaults.rs b/forc/src/utils/defaults.rs index 24ef6807f29..223531864e1 100644 --- a/forc/src/utils/defaults.rs +++ b/forc/src/utils/defaults.rs @@ -15,32 +15,6 @@ name = "{project_name}" ) } -/// Creates a default Cargo manifest for the Rust-based tests. -/// It includes necessary packages to make the Rust-based -/// tests work. -pub(crate) fn default_tests_manifest(project_name: &str) -> String { - let author = get_author(); - - format!( - r#"[project] -name = "{project_name}" -version = "0.1.0" -authors = ["{author}"] -edition = "2021" -license = "Apache-2.0" - -[dependencies] -fuels = {{ version = "0.25", features = ["fuel-core-lib"] }} -tokio = {{ version = "1.12", features = ["rt", "macros"] }} - -[[test]] -harness = true -name = "integration_tests" -path = "tests/harness.rs" -"# - ) -} - pub(crate) fn default_contract() -> String { r#"contract; @@ -86,61 +60,6 @@ fn main() -> bool { .into() } -// TODO Ideally after (instance, id) it should link to the The Fuels-rs Book -// to provide further information for writing tests/working with sway -pub(crate) fn default_test_program(project_name: &str) -> String { - format!( - "{}{}{}{}{}{}{}", - r#"use fuels::{prelude::*, tx::ContractId}; - -// Load abi from json -abigen!(MyContract, "out/debug/"#, - project_name, - r#"-abi.json"); - -async fn get_contract_instance() -> (MyContract, ContractId) { - // Launch a local network and deploy the contract - let mut wallets = launch_custom_provider_and_get_wallets( - WalletsConfig::new( - Some(1), /* Single wallet */ - Some(1), /* Single coin (UTXO) */ - Some(1_000_000_000), /* Amount per coin */ - ), - None, - ) - .await; - let wallet = wallets.pop().unwrap(); - - let id = Contract::deploy( - "./out/debug/"#, - project_name, - r#".bin", - &wallet, - TxParameters::default(), - StorageConfiguration::with_storage_path(Some( - "./out/debug/"#, - project_name, - r#"-storage_slots.json".to_string(), - )), - ) - .await - .unwrap(); - - let instance = MyContract::new(id.to_string(), wallet); - - (instance, id.into()) -} - -#[tokio::test] -async fn can_get_contract_id() { - let (_instance, _id) = get_contract_instance().await; - - // Now you have an instance of your contract you can use to test each function -} -"# - ) -} - pub(crate) fn default_gitignore() -> String { r#"out target @@ -161,11 +80,3 @@ fn parse_default_manifest() { .unwrap() ) } - -#[test] -fn parse_default_tests_manifest() { - tracing::info!( - "{:#?}", - toml::from_str::(&default_tests_manifest("test_proj")).unwrap() - ) -} diff --git a/scripts/mdbook-forc-documenter/examples/forc_init.md b/scripts/mdbook-forc-documenter/examples/forc_init.md index be328dbdd10..fbcd4626e37 100644 --- a/scripts/mdbook-forc-documenter/examples/forc_init.md +++ b/scripts/mdbook-forc-documenter/examples/forc_init.md @@ -3,20 +3,16 @@ ## EXAMPLE ```console -$ forc init my-fuel-project +$ mkdir my-fuel-project $ cd my-fuel-project +$ forc init $ tree . -├── Cargo.toml ├── Forc.toml -├── src -│ └── main.sw -└── tests - └── harness.rs +└── src + └── main.sw ``` -`Forc.toml` is the Forc manifest file, containing information about the project and dependencies. `Cargo.toml` is the Rust project manifest file, used by the Rust-based tests package. +`Forc.toml` is the Forc manifest file, containing information about the project and dependencies. A `src/` directory is created, with a single `main.sw` Sway file in it. - -A `tests/` directory is also created. The `Cargo.toml` in the root directory contains necessary Rust dependencies to enable you to write Rust-based tests using our Rust SDK (`fuels-rs`). More on this in the `Test` section down below. diff --git a/scripts/mdbook-forc-documenter/examples/forc_new.md b/scripts/mdbook-forc-documenter/examples/forc_new.md index af44cc43794..286cd5b9122 100644 --- a/scripts/mdbook-forc-documenter/examples/forc_new.md +++ b/scripts/mdbook-forc-documenter/examples/forc_new.md @@ -7,16 +7,11 @@ $ forc new my-fuel-project $ cd my-fuel-project $ tree . -├── Cargo.toml ├── Forc.toml -├── src -│ └── main.sw -└── tests - └── harness.rs +└── src + └── main.sw ``` -`Forc.toml` is the Forc manifest file, containing information about the project and dependencies. `Cargo.toml` is the Rust project manifest file, used by the Rust-based tests package. +`Forc.toml` is the Forc manifest file, containing information about the project and dependencies. A `src/` directory is created, with a single `main.sw` Sway file in it. - -A `tests/` directory is also created. The `Cargo.toml` in the root directory contains necessary Rust dependencies to enable you to write Rust-based tests using our Rust SDK (`fuels-rs`). More on this in the `Test` section down below. diff --git a/scripts/mdbook-forc-documenter/examples/forc_test.md b/scripts/mdbook-forc-documenter/examples/forc_test.md deleted file mode 100644 index f8304657c83..00000000000 --- a/scripts/mdbook-forc-documenter/examples/forc_test.md +++ /dev/null @@ -1,7 +0,0 @@ - - -## EXAMPLE - -You can write tests in Rust using our [Rust SDK](https://github.com/FuelLabs/fuels-rs). These tests can be run using `forc test`, which will look for Rust tests under the `tests/` directory (which is created automatically with `forc new`). - -You can find an example under the [Testing with Rust](../../testing/testing-with-rust.md) section. diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 00000000000..e69c6053bf7 --- /dev/null +++ b/templates/README.md @@ -0,0 +1,4 @@ +# templates + +A directory of useful templates for Sway developers, currently only home to the +`sway-test-rs` `cargo generate` template for Sway + Rust integration testing. diff --git a/templates/sway-test-rs/README.md b/templates/sway-test-rs/README.md new file mode 100644 index 00000000000..16ed9e4793e --- /dev/null +++ b/templates/sway-test-rs/README.md @@ -0,0 +1,11 @@ +# Rust + Sway integration testing + +A cargo-generate template that makes it easy to initialise Rust integration +testing within an existing Sway project. + +This template is designed for Rust developers who wish to test integration of +their Rust application and Sway code. + +See the "Testing with Rust" chapter of [the Sway +Book](https://fuellabs.github.io/sway/v0.25.0/) for a thorough guide on how to +use this template. diff --git a/templates/sway-test-rs/template/Cargo.toml b/templates/sway-test-rs/template/Cargo.toml new file mode 100644 index 00000000000..262b3f5a0de --- /dev/null +++ b/templates/sway-test-rs/template/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "{{project-name}}" +description = "A cargo-generate template for Rust + Sway integration testing." +version = "0.1.0" +edition = "2021" +authors = ["{{authors}}"] +license = "Apache-2.0" + +[dev-dependencies] +fuels = { version = "0.25", features = ["fuel-core-lib"] } +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/templates/sway-test-rs/template/cargo-generate.toml b/templates/sway-test-rs/template/cargo-generate.toml new file mode 100644 index 00000000000..7fb1ecbc761 --- /dev/null +++ b/templates/sway-test-rs/template/cargo-generate.toml @@ -0,0 +1,3 @@ +# Even though we don't need any custom configuration, the `cargo-generate.toml` +# file is added to indicate the root of the default template within the +# repository. diff --git a/templates/sway-test-rs/template/tests/harness.rs b/templates/sway-test-rs/template/tests/harness.rs new file mode 100644 index 00000000000..528964406c6 --- /dev/null +++ b/templates/sway-test-rs/template/tests/harness.rs @@ -0,0 +1,40 @@ +use fuels::{prelude::*, tx::ContractId}; + +// Load abi from json +abigen!(MyContract, "out/debug/{{project-name}}-abi.json"); + +async fn get_contract_instance() -> (MyContract, ContractId) { + // Launch a local network and deploy the contract + let mut wallets = launch_custom_provider_and_get_wallets( + WalletsConfig::new( + Some(1), /* Single wallet */ + Some(1), /* Single coin (UTXO) */ + Some(1_000_000_000), /* Amount per coin */ + ), + None, + ) + .await; + let wallet = wallets.pop().unwrap(); + + let id = Contract::deploy( + "./out/debug/{{project-name}}.bin", + &wallet, + TxParameters::default(), + StorageConfiguration::with_storage_path(Some( + "./out/debug/{{project-name}}-storage_slots.json".to_string(), + )), + ) + .await + .unwrap(); + + let instance = MyContract::new(id.to_string(), wallet); + + (instance, id.into()) +} + +#[tokio::test] +async fn can_get_contract_id() { + let (_instance, _id) = get_contract_instance().await; + + // Now you have an instance of your contract you can use to test each function +}