From fa0f537cbb4f7ba51a82708007484456227e3761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Wed, 30 Oct 2024 20:44:08 +0200 Subject: [PATCH 01/18] test: adder example --- .github/workflows/test_examples.yml | 2 +- examples/adder/.cargo/config.toml | 3 -- examples/adder/Cargo.toml | 2 +- examples/adder/src/lib.rs | 48 ++++++++++++++--------------- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index df30b006a..9cbafd9d3 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -16,7 +16,7 @@ jobs: toolchain: [1.81] # toolchain: [stable] example: [ - adder, + # adder, 1.82 callback-results, cross-contract-calls, factory-contract, diff --git a/examples/adder/.cargo/config.toml b/examples/adder/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/adder/.cargo/config.toml +++ b/examples/adder/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/adder/Cargo.toml b/examples/adder/Cargo.toml index 54624f33c..cb1d5b0c4 100644 --- a/examples/adder/Cargo.toml +++ b/examples/adder/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] near-sdk = { path = "../../near-sdk" } [dev-dependencies] -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"] } tokio = { version = "1.14", features = ["full"] } anyhow = "1.0" near-abi = "0.4.0" diff --git a/examples/adder/src/lib.rs b/examples/adder/src/lib.rs index 079bcca8c..c8536a627 100644 --- a/examples/adder/src/lib.rs +++ b/examples/adder/src/lib.rs @@ -46,12 +46,10 @@ fn sum_pair(a: &Pair, b: &Pair) -> Pair { mod tests { use near_abi::*; use near_sdk::serde_json; - use tokio::fs; - #[ignore] #[tokio::test] async fn embedded_abi_test() -> anyhow::Result<()> { - let wasm = fs::read("res/adder.wasm").await?; + let wasm = near_workspaces::compile_project("./").await?; let worker = near_workspaces::sandbox().await?; let contract = worker.dev_deploy(&wasm).await?; @@ -60,31 +58,31 @@ mod tests { let abi_root = serde_json::from_slice::(&zstd::decode_all(&res.result[..])?)?; - assert_eq!(abi_root.schema_version, "0.3.0"); + assert_eq!(abi_root.schema_version, "0.4.0"); assert_eq!(abi_root.metadata.name, Some("adder".to_string())); - assert_eq!(abi_root.metadata.version, Some("0.1.0".to_string())); - assert_eq!( - &abi_root.metadata.authors[..], - &["Near Inc "] - ); - assert_eq!(abi_root.body.functions.len(), 3); + // assert_eq!(abi_root.metadata.version, Some("0.1.0".to_string())); + // assert_eq!( + // &abi_root.metadata.authors[..], + // &["Near Inc "] + // ); + // assert_eq!(abi_root.body.functions.len(), 3); - let add_function = &abi_root.body.functions[0]; + // let add_function = &abi_root.body.functions[0]; - assert_eq!(add_function.name, "add"); - assert_eq!(add_function.doc, Some(" Adds two pairs point-wise.".to_string())); - assert_eq!(add_function.kind, AbiFunctionKind::View); - assert_eq!(add_function.modifiers, &[]); - match &add_function.params { - AbiParameters::Json { args } => { - assert_eq!(args.len(), 2); - assert_eq!(args[0].name, "a"); - assert_eq!(args[1].name, "b"); - } - AbiParameters::Borsh { .. } => { - assert!(false); - } - } + // assert_eq!(add_function.name, "add"); + // assert_eq!(add_function.doc, Some(" Adds two pairs point-wise.".to_string())); + // assert_eq!(add_function.kind, AbiFunctionKind::View); + // assert_eq!(add_function.modifiers, &[]); + // match &add_function.params { + // AbiParameters::Json { args } => { + // assert_eq!(args.len(), 2); + // assert_eq!(args[0].name, "a"); + // assert_eq!(args[1].name, "b"); + // } + // AbiParameters::Borsh { .. } => { + // assert!(false); + // } + // } Ok(()) } From cf87432dd770bb97e3fa46e3e6976b395be7da63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 31 Oct 2024 20:53:50 +0200 Subject: [PATCH 02/18] test: callback-results example --- .github/workflows/test_examples.yml | 8 ++++---- examples/callback-results/.cargo/config.toml | 3 --- examples/callback-results/Cargo.toml | 2 +- examples/callback-results/src/lib.rs | 4 +--- examples/cross-contract-calls/.cargo/config.toml | 3 --- examples/fungible-token/tests/workspaces.rs | 7 +++++++ 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index 9cbafd9d3..aefc805f2 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -17,12 +17,12 @@ jobs: # toolchain: [stable] example: [ # adder, 1.82 - callback-results, + # callback-results, 1.82 cross-contract-calls, - factory-contract, fungible-token, non-fungible-token, - versioned + versioned, + factory-contract ] steps: - uses: actions/checkout@v3 @@ -49,4 +49,4 @@ jobs: cargo +${{ matrix.toolchain }} build --manifest-path="./examples/${{matrix.example}}/Cargo.toml" --target wasm32-unknown-unknown --release --all && cp ./examples/${{matrix.example}}/target/wasm32-unknown-unknown/release/*.wasm ./examples/${{matrix.example}}/res/ - name: Test - run: cargo +${{ matrix.toolchain }} test --manifest-path="./examples/${{ matrix.example }}/Cargo.toml" --all + run: cargo +${{ matrix.toolchain }} test --manifest-path="./examples/${{ matrix.example }}/Cargo.toml" --workspace diff --git a/examples/callback-results/.cargo/config.toml b/examples/callback-results/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/callback-results/.cargo/config.toml +++ b/examples/callback-results/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/callback-results/Cargo.toml b/examples/callback-results/Cargo.toml index 748c97ebd..ecb8e3978 100644 --- a/examples/callback-results/Cargo.toml +++ b/examples/callback-results/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] near-sdk = { path = "../../near-sdk" } [dev-dependencies] -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"]} tokio = { version = "1.14", features = ["full"] } anyhow = "1.0" near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } diff --git a/examples/callback-results/src/lib.rs b/examples/callback-results/src/lib.rs index 1faa13e46..2b0841abb 100644 --- a/examples/callback-results/src/lib.rs +++ b/examples/callback-results/src/lib.rs @@ -63,11 +63,9 @@ impl Callback { #[cfg(all(test, not(target_arch = "wasm32")))] mod tests { - use tokio::fs; - #[tokio::test] async fn workspaces_test() -> anyhow::Result<()> { - let wasm = fs::read("res/callback_results.wasm").await?; + let wasm = near_workspaces::compile_project("./").await?; let worker = near_workspaces::sandbox().await?; let contract = worker.dev_deploy(&wasm).await?; diff --git a/examples/cross-contract-calls/.cargo/config.toml b/examples/cross-contract-calls/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/cross-contract-calls/.cargo/config.toml +++ b/examples/cross-contract-calls/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index 2652a3cf2..802f84a90 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -5,6 +5,13 @@ use near_workspaces::{types::NearToken, Account, AccountId, Contract, DevNetwork const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); +// TODO: PROMINENT COMMENT +// in order to be able to use a once fixture from rstest +// https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture +// for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) +// `tokio::fs::read` has to be replaced with `std::fs::read` +// and the function made NON-async + async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::Result<()> { let res = contract .call("storage_deposit") From 1d306dadfc253ccd9255674ac6163b5ad0020579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 31 Oct 2024 23:12:22 +0200 Subject: [PATCH 03/18] test: cross-contract-calls example --- .github/workflows/test_examples.yml | 2 +- examples/cross-contract-calls/Cargo.toml | 2 +- examples/cross-contract-calls/tests/workspaces.rs | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index aefc805f2..b35077987 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -18,7 +18,7 @@ jobs: example: [ # adder, 1.82 # callback-results, 1.82 - cross-contract-calls, + # cross-contract-calls, 1.82 fungible-token, non-fungible-token, versioned, diff --git a/examples/cross-contract-calls/Cargo.toml b/examples/cross-contract-calls/Cargo.toml index f32c5f152..d1209d6a7 100644 --- a/examples/cross-contract-calls/Cargo.toml +++ b/examples/cross-contract-calls/Cargo.toml @@ -9,7 +9,7 @@ anyhow = "1.0" near-sdk = { path = "../../near-sdk", features = ["default", "unit-testing"] } test-case = "2.0" tokio = { version = "1.14", features = ["full"] } -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"] } cross-contract-high-level = { path = "./high-level" } cross-contract-low-level = { path = "./low-level" } diff --git a/examples/cross-contract-calls/tests/workspaces.rs b/examples/cross-contract-calls/tests/workspaces.rs index b0b9e1963..68e041b91 100644 --- a/examples/cross-contract-calls/tests/workspaces.rs +++ b/examples/cross-contract-calls/tests/workspaces.rs @@ -1,12 +1,13 @@ use test_case::test_case; -#[test_case("cross_contract_high_level")] -#[test_case("cross_contract_low_level")] +#[test_case("./high-level")] +#[test_case("./low-level")] #[tokio::test] -async fn test_factorial(contract_name: &str) -> anyhow::Result<()> { +async fn test_factorial(contract_path: &str) -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project(contract_path).await?; let worker = near_workspaces::sandbox().await?; let contract = - worker.dev_deploy(&std::fs::read(format!("res/{}.wasm", contract_name))?).await?; + worker.dev_deploy(&wasm).await?; let res = contract.call("factorial").args_json((1,)).max_gas().transact().await?; assert!(res.is_success()); From b60f83c1608ce0fef1b927625141398f69e2c417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Fri, 1 Nov 2024 00:08:50 +0200 Subject: [PATCH 04/18] test: fungible-token example --- .github/workflows/test_examples.yml | 2 +- examples/fungible-token/.cargo/config.toml | 2 - examples/fungible-token/Cargo.toml | 4 +- examples/fungible-token/tests/workspaces.rs | 332 ++++++++++++++------ 4 files changed, 244 insertions(+), 96 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index b35077987..c6c31a71e 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -19,7 +19,7 @@ jobs: # adder, 1.82 # callback-results, 1.82 # cross-contract-calls, 1.82 - fungible-token, + # fungible-token, 1.82 non-fungible-token, versioned, factory-contract diff --git a/examples/fungible-token/.cargo/config.toml b/examples/fungible-token/.cargo/config.toml index 4a9f7c79c..ce0730dd4 100644 --- a/examples/fungible-token/.cargo/config.toml +++ b/examples/fungible-token/.cargo/config.toml @@ -1,5 +1,3 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] -[build] -target-dir = "../../target" diff --git a/examples/fungible-token/Cargo.toml b/examples/fungible-token/Cargo.toml index 611bc14a6..2d250aa1e 100644 --- a/examples/fungible-token/Cargo.toml +++ b/examples/fungible-token/Cargo.toml @@ -8,7 +8,9 @@ edition = "2021" anyhow = "1.0" near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } tokio = { version = "1.14", features = ["full"] } -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"] } +cargo-near-build = "0.2.0" +rstest = "0.23.0" [profile.release] codegen-units = 1 diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index 802f84a90..8ea697b09 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -1,16 +1,13 @@ +use std::str::FromStr; + use near_sdk::json_types::U128; use near_workspaces::operations::Function; use near_workspaces::result::ValueOrReceiptId; -use near_workspaces::{types::NearToken, Account, AccountId, Contract, DevNetwork, Worker}; +use near_workspaces::{types::NearToken, Account, AccountId, Contract}; +use rstest::{fixture, rstest}; const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); -// TODO: PROMINENT COMMENT -// in order to be able to use a once fixture from rstest -// https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture -// for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) -// `tokio::fs::read` has to be replaced with `std::fs::read` -// and the function made NON-async async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::Result<()> { let res = contract @@ -25,11 +22,62 @@ async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::R Ok(()) } -async fn init( - worker: &Worker, +// TODO: in order to be able to use a once fixture from rstest (wich uses std::sync::OnceLock) +// https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture +// or std::sync::LazyLock as in neardevhub-contract +// for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) +// `tokio::fs::read` has to be replaced with `std::fs::read` +// and the function made NON-async +#[fixture] +#[once] +fn fungible_contract_wasm() -> Vec { + let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { + manifest_path: Some( + cargo_near_build::camino::Utf8PathBuf::from_str("./ft/Cargo.toml") + .expect("camino PathBuf from str"), + ), + ..Default::default() + }) + .expect("building `fungible-token` contract for tests"); + + let contract_wasm = std::fs::read(&artifact.path) + .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) + .expect("std::fs::read"); + contract_wasm +} + +#[fixture] +#[once] +fn defi_contract_wasm() -> Vec { + let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { + manifest_path: Some( + cargo_near_build::camino::Utf8PathBuf::from_str("./test-contract-defi/Cargo.toml") + .expect("camino PathBuf from str"), + ), + ..Default::default() + }) + .expect("building `defi` contract for tests"); + + let contract_wasm = std::fs::read(&artifact.path) + .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) + .expect("std::fs::read"); + contract_wasm +} + +#[fixture] +fn initial_balance() -> U128 { + U128::from(NearToken::from_near(10000).as_yoctonear()) +} + +#[fixture] +async fn initialized_contracts( initial_balance: U128, + fungible_contract_wasm: &Vec, + defi_contract_wasm: &Vec, ) -> anyhow::Result<(Contract, Account, Contract)> { - let ft_contract = worker.dev_deploy(include_bytes!("../res/fungible_token.wasm")).await?; + let worker = near_workspaces::sandbox().await?; + + let ft_contract = worker.dev_deploy(fungible_contract_wasm).await?; let res = ft_contract .call("new_default_meta") @@ -39,9 +87,14 @@ async fn init( .await?; assert!(res.is_success()); - let defi_contract = worker.dev_deploy(include_bytes!("../res/defi.wasm")).await?; + let defi_contract = worker.dev_deploy(defi_contract_wasm).await?; - let res = defi_contract.call("new").args_json((ft_contract.id(),)).max_gas().transact().await?; + let res = defi_contract + .call("new") + .args_json((ft_contract.id(),)) + .max_gas() + .transact() + .await?; assert!(res.is_success()); let alice = ft_contract @@ -65,11 +118,14 @@ async fn init( return Ok((ft_contract, alice, defi_contract)); } +#[rstest] #[tokio::test] -async fn test_total_supply() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn test_total_supply( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let res = contract.call("ft_total_supply").view().await?; assert_eq!(res.json::()?, initial_balance); @@ -77,11 +133,13 @@ async fn test_total_supply() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_storage_deposit_not_enough_deposit() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn test_storage_deposit_not_enough_deposit( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let new_account = contract .as_account() @@ -110,8 +168,11 @@ async fn test_storage_deposit_not_enough_deposit() -> anyhow::Result<()> { assert!(new_account_balance_diff > NearToken::from_near(0)); assert!(new_account_balance_diff < NearToken::from_millinear(1)); - let contract_balance_diff = - contract.view_account().await?.balance.saturating_sub(contract_balance_before_deposit); + let contract_balance_diff = contract + .view_account() + .await? + .balance + .saturating_sub(contract_balance_before_deposit); // contract receives a gas rewards for the function call, so it should gain some NEAR assert!(contract_balance_diff > NearToken::from_near(0)); assert!(contract_balance_diff < NearToken::from_yoctonear(30_000_000_000_000_000_000)); @@ -119,11 +180,13 @@ async fn test_storage_deposit_not_enough_deposit() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_storage_deposit_minimal_deposit() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn test_storage_deposit_minimal_deposit( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let new_account = contract .as_account() @@ -154,8 +217,11 @@ async fn test_storage_deposit_minimal_deposit() -> anyhow::Result<()> { new_account_balance_diff < minimal_deposit.saturating_add(NearToken::from_millinear(1)) ); - let contract_balance_diff = - contract.view_account().await?.balance.saturating_sub(contract_balance_before_deposit); + let contract_balance_diff = contract + .view_account() + .await? + .balance + .saturating_sub(contract_balance_before_deposit); // contract receives a gas rewards for the function call, so the difference should be slightly more than minimal_deposit assert!(contract_balance_diff > minimal_deposit); // adjust the upper limit of the assertion to be more flexible for small variations in the gas reward received @@ -167,11 +233,13 @@ async fn test_storage_deposit_minimal_deposit() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_storage_deposit_refunds_excessive_deposit() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn test_storage_deposit_refunds_excessive_deposit( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let minimal_deposit = near_sdk::env::storage_byte_cost().saturating_mul(125); @@ -183,10 +251,19 @@ async fn test_storage_deposit_refunds_excessive_deposit() -> anyhow::Result<()> min: U128, max: U128, } - let storage_balance_bounds: StorageBalanceBounds = - contract.call("storage_balance_bounds").view().await?.json()?; - assert_eq!(storage_balance_bounds.min, minimal_deposit.as_yoctonear().into()); - assert_eq!(storage_balance_bounds.max, minimal_deposit.as_yoctonear().into()); + let storage_balance_bounds: StorageBalanceBounds = contract + .call("storage_balance_bounds") + .view() + .await? + .json()?; + assert_eq!( + storage_balance_bounds.min, + minimal_deposit.as_yoctonear().into() + ); + assert_eq!( + storage_balance_bounds.max, + minimal_deposit.as_yoctonear().into() + ); // Check that a non-registered account does not have storage balance // @@ -236,7 +313,10 @@ async fn test_storage_deposit_refunds_excessive_deposit() -> anyhow::Result<()> .view() .await? .json()?; - assert_eq!(storage_balance_bounds.total, minimal_deposit.as_yoctonear().into()); + assert_eq!( + storage_balance_bounds.total, + minimal_deposit.as_yoctonear().into() + ); assert_eq!(storage_balance_bounds.available, 0.into()); let new_account_balance_diff = new_account_balance_before_deposit @@ -247,8 +327,11 @@ async fn test_storage_deposit_refunds_excessive_deposit() -> anyhow::Result<()> new_account_balance_diff < minimal_deposit.saturating_add(NearToken::from_millinear(1)) ); - let contract_balance_diff = - contract.view_account().await?.balance.saturating_sub(contract_balance_before_deposit); + let contract_balance_diff = contract + .view_account() + .await? + .balance + .saturating_sub(contract_balance_before_deposit); // contract receives a gas rewards for the function call, so the difference should be slightly more than minimal_deposit assert!(contract_balance_diff > minimal_deposit); assert!( @@ -259,12 +342,15 @@ async fn test_storage_deposit_refunds_excessive_deposit() -> anyhow::Result<()> Ok(()) } +#[rstest] #[tokio::test] -async fn test_simple_transfer() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_simple_transfer( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice, _) = init(&worker, initial_balance).await?; + let (contract, alice, _) = + initialized_contracts.await?; let res = contract .call("ft_transfer") @@ -275,21 +361,31 @@ async fn test_simple_transfer() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let root_balance = - contract.call("ft_balance_of").args_json((contract.id(),)).view().await?.json::()?; - let alice_balance = - contract.call("ft_balance_of").args_json((alice.id(),)).view().await?.json::()?; + let root_balance = contract + .call("ft_balance_of") + .args_json((contract.id(),)) + .view() + .await? + .json::()?; + let alice_balance = contract + .call("ft_balance_of") + .args_json((alice.id(),)) + .view() + .await? + .json::()?; assert_eq!(initial_balance.0 - transfer_amount.0, root_balance.0); assert_eq!(transfer_amount.0, alice_balance.0); Ok(()) } +#[rstest] #[tokio::test] -async fn test_close_account_empty_balance() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice, _) = init(&worker, initial_balance).await?; +async fn test_close_account_empty_balance( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, alice, _) = + initialized_contracts.await?; let res = alice .call(contract.id(), "storage_unregister") @@ -303,11 +399,13 @@ async fn test_close_account_empty_balance() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_close_account_non_empty_balance() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn test_close_account_non_empty_balance( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let res = contract .call("storage_unregister") @@ -332,11 +430,13 @@ async fn test_close_account_non_empty_balance() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_close_account_force_non_empty_balance() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, _) = init(&worker, initial_balance).await?; +async fn simulate_close_account_force_non_empty_balance( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { + let (contract, _, _) = + initialized_contracts.await?; let res = contract .call("storage_unregister") @@ -353,12 +453,14 @@ async fn simulate_close_account_force_non_empty_balance() -> anyhow::Result<()> Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_with_burned_amount() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn simulate_transfer_call_with_burned_amount( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, defi_contract) = init(&worker, initial_balance).await?; + let (contract, _, defi_contract) = + initialized_contracts.await?; // defi contract must be registered as a FT account register_user(&contract, defi_contract.id()).await?; @@ -368,7 +470,12 @@ async fn simulate_transfer_call_with_burned_amount() -> anyhow::Result<()> { .batch() .call( Function::new("ft_transfer_call") - .args_json((defi_contract.id(), transfer_amount, Option::::None, "10")) + .args_json(( + defi_contract.id(), + transfer_amount, + Option::::None, + "10", + )) .deposit(ONE_YOCTO) .gas(near_sdk::Gas::from_tgas(150)), ) @@ -410,12 +517,15 @@ async fn simulate_transfer_call_with_burned_amount() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_with_immediate_return_and_no_refund() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn simulate_transfer_call_with_immediate_return_and_no_refund( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, defi_contract) = init(&worker, initial_balance).await?; + let (contract, _, defi_contract) = + initialized_contracts.await?; // defi contract must be registered as a FT account register_user(&contract, defi_contract.id()).await?; @@ -423,15 +533,24 @@ async fn simulate_transfer_call_with_immediate_return_and_no_refund() -> anyhow: // root invests in defi by calling `ft_transfer_call` let res = contract .call("ft_transfer_call") - .args_json((defi_contract.id(), transfer_amount, Option::::None, "take-my-money")) + .args_json(( + defi_contract.id(), + transfer_amount, + Option::::None, + "take-my-money", + )) .max_gas() .deposit(ONE_YOCTO) .transact() .await?; assert!(res.is_success()); - let root_balance = - contract.call("ft_balance_of").args_json((contract.id(),)).view().await?.json::()?; + let root_balance = contract + .call("ft_balance_of") + .args_json((contract.id(),)) + .view() + .await? + .json::()?; let defi_balance = contract .call("ft_balance_of") .args_json((defi_contract.id(),)) @@ -444,18 +563,25 @@ async fn simulate_transfer_call_with_immediate_return_and_no_refund() -> anyhow: Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_when_called_contract_not_registered_with_ft() -> anyhow::Result<()> -{ - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn simulate_transfer_call_when_called_contract_not_registered_with_ft( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, defi_contract) = init(&worker, initial_balance).await?; + let (contract, _, defi_contract) = + initialized_contracts.await?; // call fails because DEFI contract is not registered as FT user let res = contract .call("ft_transfer_call") - .args_json((defi_contract.id(), transfer_amount, Option::::None, "take-my-money")) + .args_json(( + defi_contract.id(), + transfer_amount, + Option::::None, + "take-my-money", + )) .max_gas() .deposit(ONE_YOCTO) .transact() @@ -463,8 +589,12 @@ async fn simulate_transfer_call_when_called_contract_not_registered_with_ft() -> assert!(res.is_failure()); // balances remain unchanged - let root_balance = - contract.call("ft_balance_of").args_json((contract.id(),)).view().await?.json::()?; + let root_balance = contract + .call("ft_balance_of") + .args_json((contract.id(),)) + .view() + .await? + .json::()?; let defi_balance = contract .call("ft_balance_of") .args_json((defi_contract.id(),)) @@ -477,13 +607,16 @@ async fn simulate_transfer_call_when_called_contract_not_registered_with_ft() -> Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_with_promise_and_refund() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn simulate_transfer_call_with_promise_and_refund( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let refund_amount = U128::from(NearToken::from_near(50).as_yoctonear()); let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, defi_contract) = init(&worker, initial_balance).await?; + let (contract, _, defi_contract) = + initialized_contracts.await?; // defi contract must be registered as a FT account register_user(&contract, defi_contract.id()).await?; @@ -502,26 +635,37 @@ async fn simulate_transfer_call_with_promise_and_refund() -> anyhow::Result<()> .await?; assert!(res.is_success()); - let root_balance = - contract.call("ft_balance_of").args_json((contract.id(),)).view().await?.json::()?; + let root_balance = contract + .call("ft_balance_of") + .args_json((contract.id(),)) + .view() + .await? + .json::()?; let defi_balance = contract .call("ft_balance_of") .args_json((defi_contract.id(),)) .view() .await? .json::()?; - assert_eq!(initial_balance.0 - transfer_amount.0 + refund_amount.0, root_balance.0); + assert_eq!( + initial_balance.0 - transfer_amount.0 + refund_amount.0, + root_balance.0 + ); assert_eq!(transfer_amount.0 - refund_amount.0, defi_balance.0); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_promise_panics_for_a_full_refund() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn simulate_transfer_call_promise_panics_for_a_full_refund( + initial_balance: U128, + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _, defi_contract) = init(&worker, initial_balance).await?; + + let (contract, _, defi_contract) = + initialized_contracts.await?; // defi contract must be registered as a FT account register_user(&contract, defi_contract.id()).await?; @@ -551,8 +695,12 @@ async fn simulate_transfer_call_promise_panics_for_a_full_refund() -> anyhow::Re } // balances remain unchanged - let root_balance = - contract.call("ft_balance_of").args_json((contract.id(),)).view().await?.json::()?; + let root_balance = contract + .call("ft_balance_of") + .args_json((contract.id(),)) + .view() + .await? + .json::()?; let defi_balance = contract .call("ft_balance_of") .args_json((defi_contract.id(),)) From 74440fbde08edfcaa27ae94aa328ae308e1285c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Mon, 4 Nov 2024 23:55:01 +0200 Subject: [PATCH 05/18] test: non-fungible-token example --- .github/workflows/test_examples.yml | 2 +- examples/non-fungible-token/Cargo.toml | 4 +- .../tests/workspaces/test_approval.rs | 91 +++++++--- .../tests/workspaces/test_core.rs | 163 +++++++++++++----- .../tests/workspaces/test_enumeration.rs | 37 ++-- .../tests/workspaces/utils.rs | 62 +++++-- 6 files changed, 261 insertions(+), 98 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index c6c31a71e..840a5e64b 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -20,7 +20,7 @@ jobs: # callback-results, 1.82 # cross-contract-calls, 1.82 # fungible-token, 1.82 - non-fungible-token, + # non-fungible-token, 1.82 versioned, factory-contract ] diff --git a/examples/non-fungible-token/Cargo.toml b/examples/non-fungible-token/Cargo.toml index 7f9a7e530..c5de2db10 100644 --- a/examples/non-fungible-token/Cargo.toml +++ b/examples/non-fungible-token/Cargo.toml @@ -9,7 +9,9 @@ anyhow = "1.0" near-contract-standards = { path = "../../near-contract-standards" } near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } tokio = { version = "1.14", features = ["full"] } -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"]} +rstest = "0.23.0" +cargo-near-build = "0.2.0" [profile.release] codegen-units = 1 diff --git a/examples/non-fungible-token/tests/workspaces/test_approval.rs b/examples/non-fungible-token/tests/workspaces/test_approval.rs index d3f9d6b91..d87d855b4 100644 --- a/examples/non-fungible-token/tests/workspaces/test_approval.rs +++ b/examples/non-fungible-token/tests/workspaces/test_approval.rs @@ -1,8 +1,10 @@ -use crate::utils::init; +use crate::utils::initialized_contracts; use near_contract_standards::non_fungible_token::Token; use near_sdk::AccountId; use near_workspaces::types::NearToken; +use near_workspaces::{Account, Contract}; +use rstest::rstest; use std::collections::HashMap; use std::convert::TryFrom; @@ -11,10 +13,12 @@ pub const TOKEN_ID: &str = "0"; const ONE_NEAR: NearToken = NearToken::from_near(1); const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); +#[rstest] #[tokio::test] -async fn simulate_simple_approve() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_simple_approve( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, token_receiver_contract, _) = initialized_contracts.await?; // root approves alice let res = nft_contract @@ -54,8 +58,12 @@ async fn simulate_simple_approve() -> anyhow::Result<()> { assert!(!alice_approval_id_is_2); // alternatively, one could check the data returned by nft_token - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; let mut expected_approvals: HashMap = HashMap::new(); expected_approvals.insert(AccountId::try_from(alice.id().to_string())?, 1); assert_eq!(token.approved_account_ids.unwrap(), expected_approvals); @@ -81,7 +89,11 @@ async fn simulate_simple_approve() -> anyhow::Result<()> { // approving another account gives different approval_id let res = nft_contract .call("nft_approve") - .args_json((TOKEN_ID, token_receiver_contract.id(), Option::::None)) + .args_json(( + TOKEN_ID, + token_receiver_contract.id(), + Option::::None, + )) .max_gas() // note that token_receiver's account name is shorter, and so takes less bytes to store and // therefore requires a smaller deposit! @@ -101,14 +113,20 @@ async fn simulate_simple_approve() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_approval_with_call() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, approval_receiver_contract) = init(&worker).await?; +async fn simulate_approval_with_call( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, _, approval_receiver_contract) = initialized_contracts.await?; let res = nft_contract .call("nft_approve") - .args_json((TOKEN_ID, approval_receiver_contract.id(), Some("return-now".to_string()))) + .args_json(( + TOKEN_ID, + approval_receiver_contract.id(), + Some("return-now".to_string()), + )) .max_gas() .deposit(NearToken::from_yoctonear(450000000000000000000)) .transact() @@ -131,10 +149,12 @@ async fn simulate_approval_with_call() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_approved_account_transfers_token() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, _, _) = init(&worker).await?; +async fn simulate_approved_account_transfers_token( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, _, _) = initialized_contracts.await?; // root approves alice let res = nft_contract @@ -149,7 +169,12 @@ async fn simulate_approved_account_transfers_token() -> anyhow::Result<()> { // alice sends to self let res = alice .call(nft_contract.id(), "nft_transfer") - .args_json((alice.id(), TOKEN_ID, Some(1u64), Some("gotcha! bahahaha".to_string()))) + .args_json(( + alice.id(), + TOKEN_ID, + Some(1u64), + Some("gotcha! bahahaha".to_string()), + )) .max_gas() .deposit(ONE_YOCTO) .transact() @@ -157,17 +182,23 @@ async fn simulate_approved_account_transfers_token() -> anyhow::Result<()> { assert!(res.is_success()); // token now owned by alice - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), alice.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_revoke() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_revoke( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, token_receiver_contract, _) = initialized_contracts.await?; // root approves alice let res = nft_contract @@ -182,7 +213,11 @@ async fn simulate_revoke() -> anyhow::Result<()> { // root approves token_receiver let res = nft_contract .call("nft_approve") - .args_json((TOKEN_ID, token_receiver_contract.id(), Option::::None)) + .args_json(( + TOKEN_ID, + token_receiver_contract.id(), + Option::::None, + )) .max_gas() .deposit(NearToken::from_yoctonear(450000000000000000000)) .transact() @@ -248,10 +283,12 @@ async fn simulate_revoke() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_revoke_all() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_revoke_all( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, token_receiver_contract, _) = initialized_contracts.await?; // root approves alice let res = nft_contract @@ -266,7 +303,11 @@ async fn simulate_revoke_all() -> anyhow::Result<()> { // root approves token_receiver let res = nft_contract .call("nft_approve") - .args_json((TOKEN_ID, token_receiver_contract.id(), Option::::None)) + .args_json(( + TOKEN_ID, + token_receiver_contract.id(), + Option::::None, + )) .max_gas() .deposit(NearToken::from_yoctonear(450000000000000000000)) .transact() diff --git a/examples/non-fungible-token/tests/workspaces/test_core.rs b/examples/non-fungible-token/tests/workspaces/test_core.rs index 067fdc2f6..44485c1ff 100644 --- a/examples/non-fungible-token/tests/workspaces/test_core.rs +++ b/examples/non-fungible-token/tests/workspaces/test_core.rs @@ -1,22 +1,35 @@ -use crate::utils::{init, TOKEN_ID}; +use crate::utils::{initialized_contracts, TOKEN_ID}; use near_contract_standards::non_fungible_token::Token; use near_workspaces::types::NearToken; +use near_workspaces::{Account, Contract}; +use rstest::rstest; const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); +#[rstest] #[tokio::test] -async fn simulate_simple_transfer() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, _, _) = init(&worker).await?; - - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; +async fn simulate_simple_transfer( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, _, _) = initialized_contracts.await?; + + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); let res = nft_contract .call("nft_transfer") - .args_json((alice.id(), TOKEN_ID, Option::::None, Some("simple transfer".to_string()))) + .args_json(( + alice.id(), + TOKEN_ID, + Option::::None, + Some("simple transfer".to_string()), + )) .max_gas() .deposit(ONE_YOCTO) .transact() @@ -26,17 +39,23 @@ async fn simulate_simple_transfer() -> anyhow::Result<()> { // A single NFT transfer event should have been logged: assert_eq!(res.logs().len(), 1); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), alice.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_fast_return_to_sender() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_transfer_call_fast_return_to_sender( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -53,17 +72,23 @@ async fn simulate_transfer_call_fast_return_to_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_slow_return_to_sender() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_transfer_call_slow_return_to_sender( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -80,17 +105,23 @@ async fn simulate_transfer_call_slow_return_to_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_fast_keep_with_sender() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_transfer_call_fast_keep_with_sender( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -108,17 +139,26 @@ async fn simulate_transfer_call_fast_keep_with_sender() -> anyhow::Result<()> { assert!(res.is_success()); assert_eq!(res.logs().len(), 2); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; - assert_eq!(token.owner_id.to_string(), token_receiver_contract.id().to_string()); + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; + assert_eq!( + token.owner_id.to_string(), + token_receiver_contract.id().to_string() + ); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_slow_keep_with_sender() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_transfer_call_slow_keep_with_sender( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -135,17 +175,26 @@ async fn simulate_transfer_call_slow_keep_with_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; - assert_eq!(token.owner_id.to_string(), token_receiver_contract.id().to_string()); + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; + assert_eq!( + token.owner_id.to_string(), + token_receiver_contract.id().to_string() + ); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_transfer_call_receiver_panics() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn simulate_transfer_call_receiver_panics( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -165,18 +214,23 @@ async fn simulate_transfer_call_receiver_panics() -> anyhow::Result<()> { // Prints final logs assert_eq!(res.logs().len(), 3); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] async fn simulate_transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no_log_if_not_enough_gas( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, ) -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; + let (nft_contract, _, token_receiver_contract, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer_call") @@ -196,22 +250,33 @@ async fn simulate_transfer_call_receiver_panics_and_nft_resolve_transfer_produce // Prints no logs assert_eq!(res.logs().len(), 0); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_simple_transfer_no_logs_on_failure() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; +async fn simulate_simple_transfer_no_logs_on_failure( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, _, _) = initialized_contracts.await?; let res = nft_contract .call("nft_transfer") // transfer to the current owner should fail and not print log - .args_json((nft_contract.id(), TOKEN_ID, Option::::None, Some("simple transfer"))) + .args_json(( + nft_contract.id(), + TOKEN_ID, + Option::::None, + Some("simple transfer"), + )) .gas(near_sdk::Gas::from_tgas(200)) .deposit(ONE_YOCTO) .transact() @@ -221,8 +286,12 @@ async fn simulate_simple_transfer_no_logs_on_failure() -> anyhow::Result<()> { // Prints no logs assert_eq!(res.logs().len(), 0); - let token = - nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + let token = nft_contract + .call("nft_token") + .args_json((TOKEN_ID,)) + .view() + .await? + .json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) diff --git a/examples/non-fungible-token/tests/workspaces/test_enumeration.rs b/examples/non-fungible-token/tests/workspaces/test_enumeration.rs index 651ec4cfd..025b6673a 100644 --- a/examples/non-fungible-token/tests/workspaces/test_enumeration.rs +++ b/examples/non-fungible-token/tests/workspaces/test_enumeration.rs @@ -1,7 +1,8 @@ -use crate::utils::{helper_mint, init}; +use crate::utils::{helper_mint, initialized_contracts}; use near_contract_standards::non_fungible_token::Token; use near_sdk::json_types::U128; -use near_workspaces::Contract; +use near_workspaces::{Account, Contract}; +use rstest::rstest; async fn mint_more(nft_contract: &Contract) -> anyhow::Result<()> { helper_mint( @@ -29,10 +30,12 @@ async fn mint_more(nft_contract: &Contract) -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_enum_total_supply() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; +async fn simulate_enum_total_supply( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, _, _) = initialized_contracts.await?; mint_more(&nft_contract).await?; let total_supply: U128 = nft_contract.call("nft_total_supply").view().await?.json()?; @@ -41,10 +44,12 @@ async fn simulate_enum_total_supply() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_enum_nft_tokens() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; +async fn simulate_enum_nft_tokens( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, _, _, _) = initialized_contracts.await?; mint_more(&nft_contract).await?; // No optional args should return all @@ -91,10 +96,12 @@ async fn simulate_enum_nft_tokens() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_enum_nft_supply_for_owner() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, _, _) = init(&worker).await?; +async fn simulate_enum_nft_supply_for_owner( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, _, _) = initialized_contracts.await?; // Get number from account with no NFTs let owner_num_tokens: U128 = nft_contract @@ -126,10 +133,12 @@ async fn simulate_enum_nft_supply_for_owner() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn simulate_enum_nft_tokens_for_owner() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, alice, _, _) = init(&worker).await?; +async fn simulate_enum_nft_tokens_for_owner( + #[future] initialized_contracts: anyhow::Result<(Contract, Account, Contract, Contract)>, +) -> anyhow::Result<()> { + let (nft_contract, alice, _, _) = initialized_contracts.await?; mint_more(&nft_contract).await?; // Get tokens from account with no NFTs diff --git a/examples/non-fungible-token/tests/workspaces/utils.rs b/examples/non-fungible-token/tests/workspaces/utils.rs index 5f816983b..accb4861e 100644 --- a/examples/non-fungible-token/tests/workspaces/utils.rs +++ b/examples/non-fungible-token/tests/workspaces/utils.rs @@ -1,8 +1,11 @@ +use std::str::FromStr; + use near_contract_standards::non_fungible_token::metadata::TokenMetadata; use near_contract_standards::non_fungible_token::TokenId; use near_workspaces::types::NearToken; -use near_workspaces::{Account, Contract, DevNetwork, Worker}; +use near_workspaces::{Account, Contract}; +use rstest::fixture; pub const TOKEN_ID: &str = "0"; @@ -38,16 +41,52 @@ pub async fn helper_mint( Ok(()) } +fn build_contract(path: &str, contract_name: &str) -> Vec { + let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { + manifest_path: Some( + cargo_near_build::camino::Utf8PathBuf::from_str(path).expect("camino PathBuf from str"), + ), + ..Default::default() + }) + .expect(&format!("building `{}` contract for tests", contract_name)); + + let contract_wasm = std::fs::read(&artifact.path) + .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) + .expect("std::fs::read"); + contract_wasm +} + +#[fixture] +#[once] +fn non_fungible_contract_wasm() -> Vec { + build_contract("./nft/Cargo.toml", "non-fungible-token") +} + +#[fixture] +#[once] +fn token_receiver_contract_wasm() -> Vec { + build_contract("./test-token-receiver/Cargo.toml", "token-receiver") +} + +#[fixture] +#[once] +fn approval_receiver_contract_wasm() -> Vec { + build_contract("./test-approval-receiver/Cargo.toml", "approval-receiver") +} + /// Deploy and initialize contracts and return: /// * nft_contract: the NFT contract, callable with `call!` and `view!` /// * alice: a user account, does not yet own any tokens /// * token_receiver_contract: a contract implementing `nft_on_transfer` for use with `transfer_and_call` /// * approval_receiver_contract: a contract implementing `nft_on_approve` for use with `nft_approve` -pub async fn init( - worker: &Worker, +#[fixture] +pub async fn initialized_contracts( + non_fungible_contract_wasm: &Vec, + token_receiver_contract_wasm: &Vec, + approval_receiver_contract_wasm: &Vec, ) -> anyhow::Result<(Contract, Account, Contract, Contract)> { - let nft_contract = - worker.dev_deploy(include_bytes!("../../res/non_fungible_token.wasm")).await?; + let worker = near_workspaces::sandbox().await?; + let nft_contract = worker.dev_deploy(non_fungible_contract_wasm).await?; let res = nft_contract .call("new_default_meta") @@ -89,8 +128,7 @@ pub async fn init( assert!(res.is_success()); let alice = res.result; - let token_receiver_contract = - worker.dev_deploy(include_bytes!("../../res/token_receiver.wasm")).await?; + let token_receiver_contract = worker.dev_deploy(token_receiver_contract_wasm).await?; let res = token_receiver_contract .call("new") .args_json((nft_contract.id(),)) @@ -99,8 +137,7 @@ pub async fn init( .await?; assert!(res.is_success()); - let approval_receiver_contract = - worker.dev_deploy(include_bytes!("../../res/approval_receiver.wasm")).await?; + let approval_receiver_contract = worker.dev_deploy(approval_receiver_contract_wasm).await?; let res = approval_receiver_contract .call("new") .args_json((nft_contract.id(),)) @@ -109,5 +146,10 @@ pub async fn init( .await?; assert!(res.is_success()); - return Ok((nft_contract, alice, token_receiver_contract, approval_receiver_contract)); + return Ok(( + nft_contract, + alice, + token_receiver_contract, + approval_receiver_contract, + )); } From 31b65a4ae82949ae1c588f0c91af92168e345efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Tue, 5 Nov 2024 20:22:15 +0200 Subject: [PATCH 06/18] test: versioned example --- .github/workflows/test_examples.yml | 2 +- examples/factory-contract/.cargo/config.toml | 3 --- examples/non-fungible-token/.cargo/config.toml | 3 --- examples/status-message/.cargo/config.toml | 3 --- examples/versioned/.cargo/config.toml | 3 --- 5 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index 840a5e64b..5d92daa77 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -21,7 +21,7 @@ jobs: # cross-contract-calls, 1.82 # fungible-token, 1.82 # non-fungible-token, 1.82 - versioned, + # versioned, 1.82 factory-contract ] steps: diff --git a/examples/factory-contract/.cargo/config.toml b/examples/factory-contract/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/factory-contract/.cargo/config.toml +++ b/examples/factory-contract/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/non-fungible-token/.cargo/config.toml b/examples/non-fungible-token/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/non-fungible-token/.cargo/config.toml +++ b/examples/non-fungible-token/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/status-message/.cargo/config.toml b/examples/status-message/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/status-message/.cargo/config.toml +++ b/examples/status-message/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" diff --git a/examples/versioned/.cargo/config.toml b/examples/versioned/.cargo/config.toml index 4a9f7c79c..dadf1d355 100644 --- a/examples/versioned/.cargo/config.toml +++ b/examples/versioned/.cargo/config.toml @@ -1,5 +1,2 @@ [target.wasm32-unknown-unknown] rustflags = ["-C", "link-arg=-s"] - -[build] -target-dir = "../../target" From 91c2e8855be4e0a1ca9588ada2e16b69bf3d9533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Tue, 5 Nov 2024 22:51:35 +0200 Subject: [PATCH 07/18] test: factory-contract example --- .github/workflows/test_examples.yml | 28 ++++--------- examples/factory-contract/Cargo.toml | 2 +- .../factory-contract/high-level/Cargo.toml | 2 + examples/factory-contract/high-level/build.rs | 40 +++++++++++++++++++ .../factory-contract/high-level/src/lib.rs | 2 +- .../factory-contract/low-level/Cargo.toml | 2 + examples/factory-contract/low-level/build.rs | 39 ++++++++++++++++++ .../factory-contract/low-level/src/lib.rs | 2 +- examples/factory-contract/tests/workspaces.rs | 11 ++--- 9 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 examples/factory-contract/high-level/build.rs create mode 100644 examples/factory-contract/low-level/build.rs diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index 5d92daa77..fb2363624 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -13,15 +13,14 @@ jobs: strategy: matrix: platform: [ubuntu-latest, macos-latest] - toolchain: [1.81] - # toolchain: [stable] + toolchain: [stable] example: [ - # adder, 1.82 - # callback-results, 1.82 - # cross-contract-calls, 1.82 - # fungible-token, 1.82 - # non-fungible-token, 1.82 - # versioned, 1.82 + adder, + callback-results, + cross-contract-calls, + fungible-token, + non-fungible-token, + versioned, factory-contract ] steps: @@ -35,18 +34,5 @@ jobs: - uses: Swatinem/rust-cache@v1 with: working-directory: ./examples/${{ matrix.example }} - - name: Build `status-message`, that `factory-contract` depends on - if: matrix.example == 'factory-contract' - env: - RUSTFLAGS: '-C link-arg=-s' - run: | - cargo +${{ matrix.toolchain }} build --manifest-path="./examples/status-message/Cargo.toml" --target wasm32-unknown-unknown --release --all && - cp ./examples/status-message/target/wasm32-unknown-unknown/release/*.wasm ./examples/status-message/res/ - - name: Build - env: - RUSTFLAGS: '-C link-arg=-s' - run: | - cargo +${{ matrix.toolchain }} build --manifest-path="./examples/${{matrix.example}}/Cargo.toml" --target wasm32-unknown-unknown --release --all && - cp ./examples/${{matrix.example}}/target/wasm32-unknown-unknown/release/*.wasm ./examples/${{matrix.example}}/res/ - name: Test run: cargo +${{ matrix.toolchain }} test --manifest-path="./examples/${{ matrix.example }}/Cargo.toml" --workspace diff --git a/examples/factory-contract/Cargo.toml b/examples/factory-contract/Cargo.toml index d94193794..623c430b4 100644 --- a/examples/factory-contract/Cargo.toml +++ b/examples/factory-contract/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" anyhow = "1.0" test-case = "2.0" tokio = { version = "1.14", features = ["full"] } -near-workspaces = "0.14" near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } +near-workspaces = { version = "0.14.1", features = ["unstable"] } [profile.release] codegen-units = 1 diff --git a/examples/factory-contract/high-level/Cargo.toml b/examples/factory-contract/high-level/Cargo.toml index 3225e6ce5..1b56f3e09 100644 --- a/examples/factory-contract/high-level/Cargo.toml +++ b/examples/factory-contract/high-level/Cargo.toml @@ -9,6 +9,8 @@ crate-type = ["cdylib"] [dependencies] near-sdk = { path = "../../../near-sdk" } +[build-dependencies] +cargo-near-build = { version = "0.3.0", features = ["build_script"] } [dev-dependencies] near-sdk = { path = "../../../near-sdk", features = ["unit-testing"] } diff --git a/examples/factory-contract/high-level/build.rs b/examples/factory-contract/high-level/build.rs new file mode 100644 index 000000000..840874aa4 --- /dev/null +++ b/examples/factory-contract/high-level/build.rs @@ -0,0 +1,40 @@ +use std::str::FromStr; + +use cargo_near_build::BuildOpts; +use cargo_near_build::{bon, camino, extended}; + +fn main() -> Result<(), Box> { + // directory of target `status-message` sub-contract's crate + let workdir = "../../status-message"; + // unix path to target `status-message` sub-contract's crate from root of the repo + let nep330_contract_path = "examples/status-message"; + + let manifest = camino::Utf8PathBuf::from_str(workdir) + .expect("pathbuf from str") + .join("Cargo.toml"); + + let build_opts = BuildOpts::builder() + .manifest_path(manifest) + .override_nep330_contract_path(nep330_contract_path) + // a distinct target is needed to avoid deadlock during build + .override_cargo_target_dir("../target/build-rs-status-message-for-high-level-factory") + .build(); + + let build_script_opts = extended::BuildScriptOpts::builder() + .rerun_if_changed_list(bon::vec![workdir, "Cargo.toml", "../Cargo.lock"]) + .build_skipped_when_env_is(vec![ + // shorter build for `cargo check` + ("PROFILE", "debug"), + (cargo_near_build::env_keys::BUILD_RS_ABI_STEP_HINT, "true"), + ]) + .stub_path("../target/status-message-high-level-stub.bin") + .result_env_key("BUILD_RS_SUB_BUILD_STATUS-MESSAGE") + .build(); + + let extended_opts = extended::BuildOptsExtended::builder() + .build_opts(build_opts) + .build_script_opts(build_script_opts) + .build(); + cargo_near_build::extended::build(extended_opts)?; + Ok(()) +} diff --git a/examples/factory-contract/high-level/src/lib.rs b/examples/factory-contract/high-level/src/lib.rs index 7b1167ff6..02d33bc0d 100644 --- a/examples/factory-contract/high-level/src/lib.rs +++ b/examples/factory-contract/high-level/src/lib.rs @@ -21,7 +21,7 @@ impl FactoryContract { .transfer(amount) .add_full_access_key(env::signer_account_pk()) .deploy_contract( - include_bytes!("../../../status-message/res/status_message.wasm").to_vec(), + include_bytes!(env!("BUILD_RS_SUB_BUILD_STATUS-MESSAGE")).to_vec(), ); } diff --git a/examples/factory-contract/low-level/Cargo.toml b/examples/factory-contract/low-level/Cargo.toml index 2807fb8c2..ba2a1e683 100644 --- a/examples/factory-contract/low-level/Cargo.toml +++ b/examples/factory-contract/low-level/Cargo.toml @@ -9,6 +9,8 @@ crate-type = ["cdylib"] [dependencies] near-sdk = { path = "../../../near-sdk" } +[build-dependencies] +cargo-near-build = { version = "0.3.0", features = ["build_script"] } [dev-dependencies] near-sdk = { path = "../../../near-sdk", features = ["unit-testing"] } diff --git a/examples/factory-contract/low-level/build.rs b/examples/factory-contract/low-level/build.rs new file mode 100644 index 000000000..86b2d5fae --- /dev/null +++ b/examples/factory-contract/low-level/build.rs @@ -0,0 +1,39 @@ +use std::str::FromStr; + +use cargo_near_build::BuildOpts; +use cargo_near_build::{bon, camino, extended}; + +fn main() -> Result<(), Box> { + // directory of target `status-message` sub-contract's crate + let workdir = "../../status-message"; + // unix path to target `status-message` sub-contract's crate from root of the repo + let nep330_contract_path = "examples/status-message"; + + let manifest = + camino::Utf8PathBuf::from_str(workdir).expect("pathbuf from str").join("Cargo.toml"); + + let build_opts = BuildOpts::builder() + .manifest_path(manifest) + .override_nep330_contract_path(nep330_contract_path) + // a distinct target is needed to avoid deadlock during build + .override_cargo_target_dir("../target/build-rs-status-message-for-low-level-factory") + .build(); + + let build_script_opts = extended::BuildScriptOpts::builder() + .rerun_if_changed_list(bon::vec![workdir, "Cargo.toml", "../Cargo.lock"]) + .build_skipped_when_env_is(vec![ + // shorter build for `cargo check` + ("PROFILE", "debug"), + (cargo_near_build::env_keys::BUILD_RS_ABI_STEP_HINT, "true"), + ]) + .stub_path("../target/status-message-low-level-stub.bin") + .result_env_key("BUILD_RS_SUB_BUILD_STATUS-MESSAGE") + .build(); + + let extended_opts = extended::BuildOptsExtended::builder() + .build_opts(build_opts) + .build_script_opts(build_script_opts) + .build(); + cargo_near_build::extended::build(extended_opts)?; + Ok(()) +} diff --git a/examples/factory-contract/low-level/src/lib.rs b/examples/factory-contract/low-level/src/lib.rs index d2bda474e..a2cf0c245 100644 --- a/examples/factory-contract/low-level/src/lib.rs +++ b/examples/factory-contract/low-level/src/lib.rs @@ -20,7 +20,7 @@ impl FactoryContract { &env::signer_account_pk(), 0, ); - let code: &[u8] = include_bytes!("../../../status-message/res/status_message.wasm"); + let code: &[u8] = include_bytes!(env!("BUILD_RS_SUB_BUILD_STATUS-MESSAGE")); env::promise_batch_action_deploy_contract(promise_idx, code); } diff --git a/examples/factory-contract/tests/workspaces.rs b/examples/factory-contract/tests/workspaces.rs index d7d589176..1d4b2e9b2 100644 --- a/examples/factory-contract/tests/workspaces.rs +++ b/examples/factory-contract/tests/workspaces.rs @@ -1,13 +1,14 @@ use near_workspaces::types::{AccountId, NearToken}; use test_case::test_case; -#[test_case("factory_contract_high_level")] -#[test_case("factory_contract_low_level")] +#[test_case("./high-level")] +#[test_case("./low-level")] #[tokio::test] -async fn test_deploy_status_message(contract_name: &str) -> anyhow::Result<()> { +async fn test_deploy_status_message(contract_path: &str) -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project(contract_path).await?; let worker = near_workspaces::sandbox().await?; let contract = - worker.dev_deploy(&std::fs::read(format!("res/{}.wasm", contract_name))?).await?; + worker.dev_deploy(&wasm).await?; let status_id: AccountId = format!("status.{}", contract.id()).parse()?; let status_amt = NearToken::from_near(20); @@ -20,7 +21,7 @@ async fn test_deploy_status_message(contract_name: &str) -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let message = "hello world"; + let message = "hello world from factory"; let res = contract.call("complex_call").args_json((status_id, message)).max_gas().transact().await?; assert!(res.is_success()); From f24a608382ba2ba44e64afff92aa18894ac1ea7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Tue, 5 Nov 2024 22:56:36 +0200 Subject: [PATCH 08/18] ci: typo --- examples/fungible-token/tests/workspaces.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index 8ea697b09..1e4487d1b 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -22,7 +22,7 @@ async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::R Ok(()) } -// TODO: in order to be able to use a once fixture from rstest (wich uses std::sync::OnceLock) +// TODO: in order to be able to use a once fixture from rstest (which uses std::sync::OnceLock) // https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture // or std::sync::LazyLock as in neardevhub-contract // for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) From 411fd206d844736214809ed84aa79e4eaeb6c9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Tue, 5 Nov 2024 23:40:04 +0200 Subject: [PATCH 09/18] fix: add .no_locked(true) to build scripts config (status-message dep) --- examples/factory-contract/high-level/build.rs | 1 + examples/factory-contract/low-level/build.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/factory-contract/high-level/build.rs b/examples/factory-contract/high-level/build.rs index 840874aa4..ed30ff09d 100644 --- a/examples/factory-contract/high-level/build.rs +++ b/examples/factory-contract/high-level/build.rs @@ -15,6 +15,7 @@ fn main() -> Result<(), Box> { let build_opts = BuildOpts::builder() .manifest_path(manifest) + .no_locked(true) .override_nep330_contract_path(nep330_contract_path) // a distinct target is needed to avoid deadlock during build .override_cargo_target_dir("../target/build-rs-status-message-for-high-level-factory") diff --git a/examples/factory-contract/low-level/build.rs b/examples/factory-contract/low-level/build.rs index 86b2d5fae..22f491719 100644 --- a/examples/factory-contract/low-level/build.rs +++ b/examples/factory-contract/low-level/build.rs @@ -14,6 +14,7 @@ fn main() -> Result<(), Box> { let build_opts = BuildOpts::builder() .manifest_path(manifest) + .no_locked(true) .override_nep330_contract_path(nep330_contract_path) // a distinct target is needed to avoid deadlock during build .override_cargo_target_dir("../target/build-rs-status-message-for-low-level-factory") From 9ff59d943a681dce8a2da0d999bc3acf89f1f885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Wed, 6 Nov 2024 13:46:00 +0200 Subject: [PATCH 10/18] test: add near_workspaces test for versioned contract --- .github/workflows/test.yml | 1 + .github/workflows/test_examples.yml | 1 + .github/workflows/test_examples_small.yml | 1 + examples/versioned/Cargo.toml | 7 ++++++- examples/versioned/src/lib.rs | 23 ++++++++++++++++++++++- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2fc4f9b77..8341d3ddc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,7 @@ jobs: runs-on: ${{ matrix.platform.os }} name: "${{ matrix.platform.os }} ${{ matrix.platform.rs }} ${{ matrix.features }}" strategy: + fail-fast: false matrix: platform: - os: ubuntu-latest diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml index fb2363624..d8790f7c8 100644 --- a/.github/workflows/test_examples.yml +++ b/.github/workflows/test_examples.yml @@ -11,6 +11,7 @@ jobs: runs-on: ${{ matrix.platform }} name: "${{ matrix.example }} - ${{ matrix.platform }}" strategy: + fail-fast: false matrix: platform: [ubuntu-latest, macos-latest] toolchain: [stable] diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index f04392245..b4e442ef8 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -11,6 +11,7 @@ jobs: runs-on: ${{ matrix.platform }} name: "${{ matrix.platform }} ${{ matrix.toolchain }}" strategy: + fail-fast: false matrix: platform: [ubuntu-latest, macos-latest] toolchain: [1.81] diff --git a/examples/versioned/Cargo.toml b/examples/versioned/Cargo.toml index 3f07f872f..b75787ec8 100644 --- a/examples/versioned/Cargo.toml +++ b/examples/versioned/Cargo.toml @@ -19,4 +19,9 @@ debug = false panic = "abort" [dev-dependencies] -near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } \ No newline at end of file +near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } +near-workspaces = { version = "0.14.1", features = ["unstable"]} +tokio = { version = "1.14", features = ["full"] } +anyhow = "1.0" +near-abi = "0.4.0" +zstd = "0.13" diff --git a/examples/versioned/src/lib.rs b/examples/versioned/src/lib.rs index 6739de4d2..c950221b8 100644 --- a/examples/versioned/src/lib.rs +++ b/examples/versioned/src/lib.rs @@ -97,9 +97,10 @@ impl VersionedContract { #[cfg(test)] mod tests { use super::*; + use near_abi::AbiRoot; use near_sdk::test_utils::test_env::{alice, bob}; use near_sdk::test_utils::VMContextBuilder; - use near_sdk::testing_env; + use near_sdk::{serde_json, testing_env}; fn set_predecessor_and_deposit(predecessor: AccountId, deposit: NearToken) { testing_env!(VMContextBuilder::new() @@ -143,4 +144,24 @@ mod tests { assert_eq!(contract.get_deposit(&alice()), Some(&NearToken::from_yoctonear(1000))); assert_eq!(contract.get_deposit(&bob()), Some(&NearToken::from_yoctonear(8))); } + + // TODO: add more near_workspaces tests for logic of specifically this contract + // this only tests that contract can be built with ABI and responds to __contract_abi + // view call + #[tokio::test] + async fn embedded_abi_test() -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project("./").await?; + let worker = near_workspaces::sandbox().await?; + let contract = worker.dev_deploy(&wasm).await?; + + let res = contract.view("__contract_abi").await?; + + let abi_root = + serde_json::from_slice::(&zstd::decode_all(&res.result[..])?)?; + + assert_eq!(abi_root.schema_version, "0.4.0"); + assert_eq!(abi_root.metadata.name, Some("versioned".to_string())); + + Ok(()) + } } From 48f244e333cb937d71d69c45800f765c16e9b86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Wed, 6 Nov 2024 23:58:39 +0200 Subject: [PATCH 11/18] chore: common build_contract fn in fungible-token --- examples/fungible-token/tests/workspaces.rs | 32 ++++++++------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index 1e4487d1b..99f5e2fd3 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -22,23 +22,20 @@ async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::R Ok(()) } -// TODO: in order to be able to use a once fixture from rstest (which uses std::sync::OnceLock) +// TODO: in order to be able to use a once fixture from rstest (wich uses std::sync::OnceLock) // https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture // or std::sync::LazyLock as in neardevhub-contract // for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) // `tokio::fs::read` has to be replaced with `std::fs::read` // and the function made NON-async -#[fixture] -#[once] -fn fungible_contract_wasm() -> Vec { +fn build_contract(path: &str, contract_name: &str) -> Vec { let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { manifest_path: Some( - cargo_near_build::camino::Utf8PathBuf::from_str("./ft/Cargo.toml") - .expect("camino PathBuf from str"), + cargo_near_build::camino::Utf8PathBuf::from_str(path).expect("camino PathBuf from str"), ), ..Default::default() }) - .expect("building `fungible-token` contract for tests"); + .expect(&format!("building `{}` contract for tests", contract_name)); let contract_wasm = std::fs::read(&artifact.path) .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) @@ -48,20 +45,15 @@ fn fungible_contract_wasm() -> Vec { #[fixture] #[once] -fn defi_contract_wasm() -> Vec { - let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { - manifest_path: Some( - cargo_near_build::camino::Utf8PathBuf::from_str("./test-contract-defi/Cargo.toml") - .expect("camino PathBuf from str"), - ), - ..Default::default() - }) - .expect("building `defi` contract for tests"); +fn fungible_contract_wasm() -> Vec { + build_contract("./ft/Cargo.toml", "fungible-token") +} - let contract_wasm = std::fs::read(&artifact.path) - .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) - .expect("std::fs::read"); - contract_wasm + +#[fixture] +#[once] +fn defi_contract_wasm() -> Vec { + build_contract("./test-contract-defi/Cargo.toml", "defi") } #[fixture] From c6f8673c084708f65eaae515a8fd61e6df4f82de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 14:11:16 +0200 Subject: [PATCH 12/18] test: lockable-fungible-token example (small) --- .github/workflows/test_examples_small.yml | 6 +- examples/lockable-fungible-token/Cargo.toml | 4 +- .../tests/workspaces.rs | 180 ++++++++++++------ 3 files changed, 131 insertions(+), 59 deletions(-) diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index b4e442ef8..bfe8e27b6 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -25,12 +25,8 @@ jobs: toolchain: ${{ matrix.toolchain }} target: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v1 - - name: Build lockable-fungible-token - env: - RUSTFLAGS: '-C link-arg=-s' - run: cargo +${{ matrix.toolchain }} build --manifest-path=./examples/lockable-fungible-token/Cargo.toml --target wasm32-unknown-unknown --release --all && cp ./examples/lockable-fungible-token/target/wasm32-unknown-unknown/release/*.wasm ./examples/lockable-fungible-token/res/ - name: Test lockable-fungible-token - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/lockable-fungible-token/Cargo.toml --all + run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/lockable-fungible-token/Cargo.toml --workspace - name: Build status-message env: RUSTFLAGS: '-C link-arg=-s' diff --git a/examples/lockable-fungible-token/Cargo.toml b/examples/lockable-fungible-token/Cargo.toml index e2356e017..69b937f2d 100644 --- a/examples/lockable-fungible-token/Cargo.toml +++ b/examples/lockable-fungible-token/Cargo.toml @@ -14,7 +14,9 @@ near-sdk = { path = "../../near-sdk", features = ["legacy"] } anyhow = "1.0" tokio = { version = "1.14", features = ["full"] } near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } -near-workspaces = "0.14" +near-workspaces = { version = "0.14.1", features = ["unstable"] } +cargo-near-build = "0.2.0" +rstest = "0.23.0" [profile.release] codegen-units = 1 diff --git a/examples/lockable-fungible-token/tests/workspaces.rs b/examples/lockable-fungible-token/tests/workspaces.rs index 87370ee39..a48e95aaf 100644 --- a/examples/lockable-fungible-token/tests/workspaces.rs +++ b/examples/lockable-fungible-token/tests/workspaces.rs @@ -1,12 +1,43 @@ +use std::str::FromStr; + use near_sdk::json_types::U128; -use near_workspaces::{Account, Contract, DevNetwork, Worker, types::NearToken}; +use near_workspaces::{types::NearToken, Account, Contract}; +use rstest::{fixture, rstest}; + +#[fixture] +fn initial_balance() -> U128 { + U128::from(NearToken::from_near(10000).as_yoctonear()) +} + +fn build_contract(path: &str, contract_name: &str) -> Vec { + let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { + manifest_path: Some( + cargo_near_build::camino::Utf8PathBuf::from_str(path).expect("camino PathBuf from str"), + ), + ..Default::default() + }) + .expect(&format!("building `{}` contract for tests", contract_name)); + + let contract_wasm = std::fs::read(&artifact.path) + .map_err(|err| format!("accessing {} to read wasm contents: {}", artifact.path, err)) + .expect("std::fs::read"); + contract_wasm +} + +#[fixture] +#[once] +fn lockable_fungible_contract_wasm() -> Vec { + build_contract("./Cargo.toml", "lockable-fungible-token") +} -async fn init( - worker: &Worker, +#[fixture] +async fn initialized_contract( initial_balance: U128, + lockable_fungible_contract_wasm: &Vec, ) -> anyhow::Result<(Contract, Account)> { - let contract = - worker.dev_deploy(include_bytes!("../res/lockable_fungible_token.wasm")).await?; + let worker = near_workspaces::sandbox().await?; + + let contract = worker.dev_deploy(lockable_fungible_contract_wasm).await?; let res = contract .call("new") @@ -28,21 +59,30 @@ async fn init( Ok((contract, alice)) } +#[rstest] +#[rstest] #[tokio::test] -async fn test_owner_initial_state() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _) = init(&worker, initial_balance).await?; +async fn test_owner_initial_state( + initial_balance: U128, + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { + let (contract, _) = initialized_contract.await?; let res = contract.call("get_total_supply").view().await?; assert_eq!(res.json::()?, initial_balance); - let res = - contract.call("get_total_balance").args_json((contract.id(),)).view().await?; + let res = contract + .call("get_total_balance") + .args_json((contract.id(),)) + .view() + .await?; assert_eq!(res.json::()?, initial_balance); - let res = - contract.call("get_unlocked_balance").args_json((contract.id(),)).view().await?; + let res = contract + .call("get_unlocked_balance") + .args_json((contract.id(),)) + .view() + .await?; assert_eq!(res.json::()?, initial_balance); let res = contract @@ -62,12 +102,14 @@ async fn test_owner_initial_state() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_set_allowance() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_set_allowance( + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { let allowance_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice) = init(&worker, initial_balance).await?; + + let (contract, alice) = initialized_contract.await?; let res = contract .call("set_allowance") @@ -95,12 +137,14 @@ async fn test_set_allowance() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_fail_set_allowance_self() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_fail_set_allowance_self( + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { let allowance_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _) = init(&worker, initial_balance).await?; + + let (contract, _) = initialized_contract.await?; let res = contract .call("set_allowance") @@ -114,12 +158,15 @@ async fn test_fail_set_allowance_self() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_lock_owner() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_lock_owner( + initial_balance: U128, + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { let lock_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _) = init(&worker, initial_balance).await?; + + let (contract, _) = initialized_contract.await?; let res = contract .call("lock") @@ -129,8 +176,11 @@ async fn test_lock_owner() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let res = - contract.call("get_unlocked_balance").args_json((contract.id(),)).view().await?; + let res = contract + .call("get_unlocked_balance") + .args_json((contract.id(),)) + .view() + .await?; assert_eq!(res.json::()?.0, initial_balance.0 - lock_amount.0); let res = contract @@ -150,11 +200,12 @@ async fn test_lock_owner() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_fail_lock() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice) = init(&worker, initial_balance).await?; +async fn test_fail_lock( + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { + let (contract, alice) = initialized_contract.await?; let res = contract .call("lock") @@ -166,7 +217,10 @@ async fn test_fail_lock() -> anyhow::Result<()> { let res = contract .call("lock") - .args_json((contract.id(), U128::from(NearToken::from_near(10001).as_yoctonear()))) + .args_json(( + contract.id(), + U128::from(NearToken::from_near(10001).as_yoctonear()), + )) .max_gas() .transact() .await; @@ -174,7 +228,10 @@ async fn test_fail_lock() -> anyhow::Result<()> { let res = alice .call(contract.id(), "lock") - .args_json((contract.id(), U128::from(NearToken::from_near(10).as_yoctonear()))) + .args_json(( + contract.id(), + U128::from(NearToken::from_near(10).as_yoctonear()), + )) .max_gas() .transact() .await; @@ -183,12 +240,15 @@ async fn test_fail_lock() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_unlock_owner() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_unlock_owner( + initial_balance: U128, + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { let lock_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _) = init(&worker, initial_balance).await?; + + let (contract, _) = initialized_contract.await?; let res = contract .call("lock") @@ -206,8 +266,11 @@ async fn test_unlock_owner() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let res = - contract.call("get_unlocked_balance").args_json((contract.id(),)).view().await?; + let res = contract + .call("get_unlocked_balance") + .args_json((contract.id(),)) + .view() + .await?; assert_eq!(res.json::()?.0, initial_balance.0); let res = contract @@ -227,11 +290,12 @@ async fn test_unlock_owner() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_fail_unlock() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, _) = init(&worker, initial_balance).await?; +async fn test_fail_unlock( + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { + let (contract, _) = initialized_contract.await?; let res = contract .call("unlock") @@ -243,7 +307,10 @@ async fn test_fail_unlock() -> anyhow::Result<()> { let res = contract .call("unlock") - .args_json((contract.id(), U128::from(NearToken::from_near(1).as_yoctonear()))) + .args_json(( + contract.id(), + U128::from(NearToken::from_near(1).as_yoctonear()), + )) .max_gas() .transact() .await; @@ -252,12 +319,15 @@ async fn test_fail_unlock() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_simple_transfer() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); +async fn test_simple_transfer( + initial_balance: U128, + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { let transfer_amount = U128::from(NearToken::from_near(100).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice) = init(&worker, initial_balance).await?; + + let (contract, alice) = initialized_contract.await?; let res = contract .call("transfer") @@ -285,11 +355,12 @@ async fn test_simple_transfer() -> anyhow::Result<()> { Ok(()) } +#[rstest] #[tokio::test] -async fn test_fail_transfer() -> anyhow::Result<()> { - let initial_balance = U128::from(NearToken::from_near(10000).as_yoctonear()); - let worker = near_workspaces::sandbox().await?; - let (contract, alice) = init(&worker, initial_balance).await?; +async fn test_fail_transfer( + #[future] initialized_contract: anyhow::Result<(Contract, Account)>, +) -> anyhow::Result<()> { + let (contract, alice) = initialized_contract.await?; let res = contract .call("transfer") @@ -301,7 +372,10 @@ async fn test_fail_transfer() -> anyhow::Result<()> { let res = contract .call("transfer") - .args_json((alice.id(), U128::from(NearToken::from_near(10001).as_yoctonear()))) + .args_json(( + alice.id(), + U128::from(NearToken::from_near(10001).as_yoctonear()), + )) .max_gas() .transact() .await; From ef7a22a6442f947092e5899ab50d357a7e7f4e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 15:44:04 +0200 Subject: [PATCH 13/18] test: status-message example (small) --- .github/workflows/test_examples_small.yml | 6 +----- examples/status-message/Cargo.toml | 5 +++++ examples/status-message/src/lib.rs | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index bfe8e27b6..da6b01b50 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -27,12 +27,8 @@ jobs: - uses: Swatinem/rust-cache@v1 - name: Test lockable-fungible-token run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/lockable-fungible-token/Cargo.toml --workspace - - name: Build status-message - env: - RUSTFLAGS: '-C link-arg=-s' - run: cargo +${{ matrix.toolchain }} build --manifest-path=./examples/status-message/Cargo.toml --target wasm32-unknown-unknown --release --all && cp ./examples/status-message/target/wasm32-unknown-unknown/release/*.wasm ./examples/status-message/res/ - name: Test status-message - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/status-message/Cargo.toml --all + run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/status-message/Cargo.toml --workspace - name: Build mission-control env: RUSTFLAGS: '-C link-arg=-s' diff --git a/examples/status-message/Cargo.toml b/examples/status-message/Cargo.toml index e72a5ba0e..74dba947a 100644 --- a/examples/status-message/Cargo.toml +++ b/examples/status-message/Cargo.toml @@ -12,6 +12,11 @@ near-sdk = { path = "../../near-sdk" } [dev-dependencies] near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } +near-workspaces = { version = "0.14.1", features = ["unstable"]} +tokio = { version = "1.14", features = ["full"] } +anyhow = "1.0" +near-abi = "0.4.0" +zstd = "0.13" [profile.release] codegen-units = 1 diff --git a/examples/status-message/src/lib.rs b/examples/status-message/src/lib.rs index 142c68598..2634f9421 100644 --- a/examples/status-message/src/lib.rs +++ b/examples/status-message/src/lib.rs @@ -37,6 +37,8 @@ mod tests { use super::*; use near_sdk::test_utils::{get_logs, VMContextBuilder}; use near_sdk::{testing_env, VMContext}; + use near_sdk::serde_json; + use near_abi::AbiRoot; fn get_context(is_view: bool) -> VMContext { VMContextBuilder::new() @@ -70,4 +72,24 @@ mod tests { assert_eq!(None, contract.get_status("francis.near".parse().unwrap())); assert_eq!(get_logs(), vec!["get_status for account_id francis.near"]) } + + // TODO: add more near_workspaces tests for logic of specifically this contract + // this only tests that contract can be built with ABI and responds to __contract_abi + // view call + #[tokio::test] + async fn embedded_abi_test() -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project("./").await?; + let worker = near_workspaces::sandbox().await?; + let contract = worker.dev_deploy(&wasm).await?; + + let res = contract.view("__contract_abi").await?; + + let abi_root = + serde_json::from_slice::(&zstd::decode_all(&res.result[..])?)?; + + assert_eq!(abi_root.schema_version, "0.4.0"); + assert_eq!(abi_root.metadata.name, Some("status-message".to_string())); + + Ok(()) + } } From 86f740a5e02aeb07b2a04295b211dc80ac70bad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 16:20:06 +0200 Subject: [PATCH 14/18] test: mission-control example (small) --- .github/workflows/test_examples_small.yml | 6 +---- examples/mission-control/Cargo.toml | 5 +++++ .../mission-control/src/mission_control.rs | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index da6b01b50..586c7587f 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -29,12 +29,8 @@ jobs: run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/lockable-fungible-token/Cargo.toml --workspace - name: Test status-message run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/status-message/Cargo.toml --workspace - - name: Build mission-control - env: - RUSTFLAGS: '-C link-arg=-s' - run: cargo +${{ matrix.toolchain }} build --manifest-path=./examples/mission-control/Cargo.toml --target wasm32-unknown-unknown --release --all && cp ./examples/mission-control/target/wasm32-unknown-unknown/release/*.wasm ./examples/mission-control/res/ - name: Test mission-control - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/mission-control/Cargo.toml --all + run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/mission-control/Cargo.toml --workspace - name: Build test-contract env: RUSTFLAGS: '-C link-arg=-s' diff --git a/examples/mission-control/Cargo.toml b/examples/mission-control/Cargo.toml index d4f9d29b4..b484398c9 100644 --- a/examples/mission-control/Cargo.toml +++ b/examples/mission-control/Cargo.toml @@ -12,6 +12,11 @@ near-sdk = { path = "../../near-sdk" } [dev-dependencies] near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } +near-workspaces = { version = "0.14.1", features = ["unstable"]} +tokio = { version = "1.14", features = ["full"] } +anyhow = "1.0" +near-abi = "0.4.0" +zstd = "0.13" [profile.release] codegen-units = 1 diff --git a/examples/mission-control/src/mission_control.rs b/examples/mission-control/src/mission_control.rs index 74f141a42..6c3167167 100644 --- a/examples/mission-control/src/mission_control.rs +++ b/examples/mission-control/src/mission_control.rs @@ -83,6 +83,8 @@ fn rates_default() -> HashMap { mod tests { use super::*; use near_sdk::env; + use near_abi::AbiRoot; + use near_sdk::serde_json; #[test] fn add_agent() { @@ -96,4 +98,24 @@ mod tests { contract.assets_quantity(account_id.clone(), Asset::MissionTime) ); } + + // TODO: add more near_workspaces tests for logic of specifically this contract + // this only tests that contract can be built with ABI and responds to __contract_abi + // view call + #[tokio::test] + async fn embedded_abi_test() -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project("./").await?; + let worker = near_workspaces::sandbox().await?; + let contract = worker.dev_deploy(&wasm).await?; + + let res = contract.view("__contract_abi").await?; + + let abi_root = + serde_json::from_slice::(&zstd::decode_all(&res.result[..])?)?; + + assert_eq!(abi_root.schema_version, "0.4.0"); + assert_eq!(abi_root.metadata.name, Some("mission-control".to_string())); + + Ok(()) + } } From 9c04d4c2cb83b000b84d4a390cd72d0cc82188d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 18:49:15 +0200 Subject: [PATCH 15/18] test: test-contract example (small) --- .github/workflows/test_examples_small.yml | 9 ++------- examples/test-contract/Cargo.toml | 5 +++++ examples/test-contract/src/lib.rs | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index 586c7587f..60969f982 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -14,8 +14,7 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest] - toolchain: [1.81] - # toolchain: [stable] + toolchain: [stable] steps: - uses: actions/checkout@v3 - name: "${{ matrix.toolchain }} with rustfmt, clippy, and wasm32" @@ -31,9 +30,5 @@ jobs: run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/status-message/Cargo.toml --workspace - name: Test mission-control run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/mission-control/Cargo.toml --workspace - - name: Build test-contract - env: - RUSTFLAGS: '-C link-arg=-s' - run: cargo +${{ matrix.toolchain }} build --manifest-path=./examples/test-contract/Cargo.toml --target wasm32-unknown-unknown --release --all && cp ./examples/test-contract/target/wasm32-unknown-unknown/release/*.wasm ./examples/test-contract/res/ - name: Test test-contract - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/test-contract/Cargo.toml --all + run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/test-contract/Cargo.toml --workspace diff --git a/examples/test-contract/Cargo.toml b/examples/test-contract/Cargo.toml index 423588334..bcd090ebc 100644 --- a/examples/test-contract/Cargo.toml +++ b/examples/test-contract/Cargo.toml @@ -20,3 +20,8 @@ panic = "abort" [dev-dependencies] near-sdk = { path = "../../near-sdk", features = ["unit-testing"] } +near-workspaces = { version = "0.14.1", features = ["unstable"]} +tokio = { version = "1.14", features = ["full"] } +anyhow = "1.0" +near-abi = "0.4.0" +zstd = "0.13" diff --git a/examples/test-contract/src/lib.rs b/examples/test-contract/src/lib.rs index 46444f9fc..ea60bd05f 100644 --- a/examples/test-contract/src/lib.rs +++ b/examples/test-contract/src/lib.rs @@ -37,6 +37,8 @@ impl TestContract { #[cfg(test)] mod tests { use super::*; + use near_abi::AbiRoot; + use near_sdk::serde_json; #[test] #[should_panic(expected = "PANIC!")] @@ -44,4 +46,24 @@ mod tests { let mut contract = TestContract::new(); contract.test_panic_macro(); } + + // TODO: add more near_workspaces tests for logic of specifically this contract + // this only tests that contract can be built with ABI and responds to __contract_abi + // view call + #[tokio::test] + async fn embedded_abi_test() -> anyhow::Result<()> { + let wasm = near_workspaces::compile_project("./").await?; + let worker = near_workspaces::sandbox().await?; + let contract = worker.dev_deploy(&wasm).await?; + + let res = contract.view("__contract_abi").await?; + + let abi_root = + serde_json::from_slice::(&zstd::decode_all(&res.result[..])?)?; + + assert_eq!(abi_root.schema_version, "0.4.0"); + assert_eq!(abi_root.metadata.name, Some("test-contract".to_string())); + + Ok(()) + } } From 8dbfab55072ef971d89b99f0232b5654b5c0364a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 19:07:15 +0200 Subject: [PATCH 16/18] ci: typo --- examples/fungible-token/tests/workspaces.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index 99f5e2fd3..d5441dbb0 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -22,7 +22,7 @@ async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::R Ok(()) } -// TODO: in order to be able to use a once fixture from rstest (wich uses std::sync::OnceLock) +// TODO: in order to be able to use a once fixture from rstest (which uses std::sync::OnceLock) // https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture // or std::sync::LazyLock as in neardevhub-contract // for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) From 7be211e1b2991f6c5b4db78972784dac2f066e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Thu, 7 Nov 2024 20:04:14 +0200 Subject: [PATCH 17/18] ci: replace small examples with matrix --- .github/workflows/test_examples_small.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml index 60969f982..fde99636e 100644 --- a/.github/workflows/test_examples_small.yml +++ b/.github/workflows/test_examples_small.yml @@ -9,12 +9,18 @@ env: jobs: test: runs-on: ${{ matrix.platform }} - name: "${{ matrix.platform }} ${{ matrix.toolchain }}" + name: "${{ matrix.example }} - ${{ matrix.platform }}" strategy: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest] toolchain: [stable] + example: [ + lockable-fungible-token, + status-message, + mission-control, + test-contract, + ] steps: - uses: actions/checkout@v3 - name: "${{ matrix.toolchain }} with rustfmt, clippy, and wasm32" @@ -24,11 +30,7 @@ jobs: toolchain: ${{ matrix.toolchain }} target: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v1 - - name: Test lockable-fungible-token - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/lockable-fungible-token/Cargo.toml --workspace - - name: Test status-message - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/status-message/Cargo.toml --workspace - - name: Test mission-control - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/mission-control/Cargo.toml --workspace - - name: Test test-contract - run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/test-contract/Cargo.toml --workspace + with: + working-directory: ./examples/${{ matrix.example }} + - name: Test + run: cargo +${{ matrix.toolchain }} test --manifest-path=./examples/${{ matrix.example }}/Cargo.toml --workspace From b4b3dff0ef5a3f30841348a80461efc56f97945f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Wed, 13 Nov 2024 18:05:41 +0200 Subject: [PATCH 18/18] todo: remove introduced todos --- examples/fungible-token/tests/workspaces.rs | 6 ------ examples/mission-control/src/mission_control.rs | 1 - examples/status-message/src/lib.rs | 1 - examples/test-contract/src/lib.rs | 1 - examples/versioned/src/lib.rs | 1 - 5 files changed, 10 deletions(-) diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs index d5441dbb0..e8f19d673 100644 --- a/examples/fungible-token/tests/workspaces.rs +++ b/examples/fungible-token/tests/workspaces.rs @@ -22,12 +22,6 @@ async fn register_user(contract: &Contract, account_id: &AccountId) -> anyhow::R Ok(()) } -// TODO: in order to be able to use a once fixture from rstest (which uses std::sync::OnceLock) -// https://docs.rs/rstest/0.16.0/rstest/attr.fixture.html#once-fixture -// or std::sync::LazyLock as in neardevhub-contract -// for [near_workspaces::compile_project](https://github.com/near/near-workspaces-rs/blob/main/workspaces/src/cargo/mod.rs#L8) -// `tokio::fs::read` has to be replaced with `std::fs::read` -// and the function made NON-async fn build_contract(path: &str, contract_name: &str) -> Vec { let artifact = cargo_near_build::build(cargo_near_build::BuildOpts { manifest_path: Some( diff --git a/examples/mission-control/src/mission_control.rs b/examples/mission-control/src/mission_control.rs index 6c3167167..f2c1e2bbc 100644 --- a/examples/mission-control/src/mission_control.rs +++ b/examples/mission-control/src/mission_control.rs @@ -99,7 +99,6 @@ mod tests { ); } - // TODO: add more near_workspaces tests for logic of specifically this contract // this only tests that contract can be built with ABI and responds to __contract_abi // view call #[tokio::test] diff --git a/examples/status-message/src/lib.rs b/examples/status-message/src/lib.rs index 2634f9421..d56d26f85 100644 --- a/examples/status-message/src/lib.rs +++ b/examples/status-message/src/lib.rs @@ -73,7 +73,6 @@ mod tests { assert_eq!(get_logs(), vec!["get_status for account_id francis.near"]) } - // TODO: add more near_workspaces tests for logic of specifically this contract // this only tests that contract can be built with ABI and responds to __contract_abi // view call #[tokio::test] diff --git a/examples/test-contract/src/lib.rs b/examples/test-contract/src/lib.rs index ea60bd05f..e2ac17f40 100644 --- a/examples/test-contract/src/lib.rs +++ b/examples/test-contract/src/lib.rs @@ -47,7 +47,6 @@ mod tests { contract.test_panic_macro(); } - // TODO: add more near_workspaces tests for logic of specifically this contract // this only tests that contract can be built with ABI and responds to __contract_abi // view call #[tokio::test] diff --git a/examples/versioned/src/lib.rs b/examples/versioned/src/lib.rs index c950221b8..7b3d55e12 100644 --- a/examples/versioned/src/lib.rs +++ b/examples/versioned/src/lib.rs @@ -145,7 +145,6 @@ mod tests { assert_eq!(contract.get_deposit(&bob()), Some(&NearToken::from_yoctonear(8))); } - // TODO: add more near_workspaces tests for logic of specifically this contract // this only tests that contract can be built with ABI and responds to __contract_abi // view call #[tokio::test]