diff --git a/src/lib.rs b/src/lib.rs index 0f061d20..bb11f1b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,8 +93,6 @@ impl Contract { /// in this call. `self.tokens.mint` will also require it to be Some, since /// `StorageKey::TokenMetadata` was provided at initialization. /// - /// `self.tokens.mint` will enforce `predecessor_account_id` to equal the `owner_id` given in - /// initialization call to `new`. #[payable] pub fn nft_mint( &mut self, diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 00000000..19ab4452 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,51 @@ +use near_contract_standards::non_fungible_token::metadata::TokenMetadata; +use near_contract_standards::non_fungible_token::TokenId; + +use near_sdk::serde_json::json; +use near_sdk::AccountId; +use near_workspaces::types::NearToken; +use near_workspaces::{Account, Contract}; + +pub async fn mint_nft( + minter: &Account, + contract_id: &AccountId, + token_id: TokenId, + token_owner_id: &AccountId, +) -> anyhow::Result<()> { + let token_metadata = TokenMetadata { + title: Some(format!("Title for {token_id}")), + description: Some(format!("Description for {token_id}")), + media: None, + media_hash: None, + copies: Some(1u64), + issued_at: None, + expires_at: None, + starts_at: None, + updated_at: None, + extra: None, + reference: None, + reference_hash: None, + }; + let res = minter + .call(contract_id, "nft_mint") + .args_json(json!({"token_id": token_id, "token_owner_id": token_owner_id, "token_metadata": token_metadata})) + .max_gas() + .deposit(NearToken::from_millinear(7)) + .transact() + .await?; + assert!(res.is_success()); + + Ok(()) +} + +pub async fn init_nft_contract(contract: &Contract) -> anyhow::Result<()> { + let res = contract + .call("new_default_meta") + .args_json((contract.id(),)) + .max_gas() + .transact() + .await?; + assert!(res.is_success()); + + Ok(()) +} diff --git a/tests/contracts/approval-receiver/src/lib.rs b/tests/contracts/approval-receiver/src/lib.rs index 975e798e..5d2a9b16 100644 --- a/tests/contracts/approval-receiver/src/lib.rs +++ b/tests/contracts/approval-receiver/src/lib.rs @@ -3,30 +3,20 @@ A stub contract that implements nft_on_approve for e2e testing nft_approve. */ use near_contract_standards::non_fungible_token::approval::NonFungibleTokenApprovalReceiver; use near_contract_standards::non_fungible_token::TokenId; -use near_sdk::{env, log, near, require, AccountId, Gas, PanicOnDefault, PromiseOrValue}; +use near_sdk::{env, log, near, require, AccountId, Gas, PromiseOrValue}; /// It is estimated that we need to attach 5 TGas for the code execution and 5 TGas for cross-contract call const GAS_FOR_NFT_ON_APPROVE: Gas = Gas::from_tgas(10); #[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct ApprovalReceiver { - non_fungible_token_account_id: AccountId, -} +#[derive(Default)] +pub struct ApprovalReceiver {} // Have to repeat the same trait for our own implementation. pub trait ValueReturnTrait { fn ok_go(&self, msg: String) -> PromiseOrValue; } -#[near] -impl ApprovalReceiver { - #[init] - pub fn new(non_fungible_token_account_id: AccountId) -> Self { - Self { non_fungible_token_account_id: non_fungible_token_account_id.into() } - } -} - #[near] impl NonFungibleTokenApprovalReceiver for ApprovalReceiver { /// Could do anything useful to the approval-receiving contract, such as store the given @@ -43,10 +33,6 @@ impl NonFungibleTokenApprovalReceiver for ApprovalReceiver { msg: String, ) -> PromiseOrValue { // Verifying that we were called by non-fungible token contract that we expect. - require!( - env::predecessor_account_id() == self.non_fungible_token_account_id, - "Only supports the one non-fungible token contract" - ); log!( "in nft_on_approve; sender_id={}, previous_owner_id={}, token_id={}, msg={}", &token_id, diff --git a/tests/contracts/token-receiver/src/lib.rs b/tests/contracts/token-receiver/src/lib.rs index ccedf988..1bc630de 100644 --- a/tests/contracts/token-receiver/src/lib.rs +++ b/tests/contracts/token-receiver/src/lib.rs @@ -3,30 +3,20 @@ A stub contract that implements nft_on_transfer for simulation testing nft_trans */ use near_contract_standards::non_fungible_token::core::NonFungibleTokenReceiver; use near_contract_standards::non_fungible_token::TokenId; -use near_sdk::{env, log, near, require, AccountId, Gas, PanicOnDefault, PromiseOrValue}; +use near_sdk::{env, log, near, require, AccountId, Gas, PromiseOrValue}; /// It is estimated that we need to attach 5 TGas for the code execution and 5 TGas for cross-contract call const GAS_FOR_NFT_ON_TRANSFER: Gas = Gas::from_tgas(10); #[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct TokenReceiver { - non_fungible_token_account_id: AccountId, -} +#[derive(Default)] +pub struct TokenReceiver {} // Have to repeat the same trait for our own implementation. pub trait ValueReturnTrait { fn ok_go(&self, return_it: bool) -> PromiseOrValue; } -#[near] -impl TokenReceiver { - #[init] - pub fn new(non_fungible_token_account_id: AccountId) -> Self { - Self { non_fungible_token_account_id } - } -} - #[near] impl NonFungibleTokenReceiver for TokenReceiver { /// Returns true if token should be returned to `sender_id` @@ -44,10 +34,6 @@ impl NonFungibleTokenReceiver for TokenReceiver { msg: String, ) -> PromiseOrValue { // Verifying that we were called by non-fungible token contract that we expect. - require!( - env::predecessor_account_id() == self.non_fungible_token_account_id, - "Only supports the one non-fungible token contract" - ); log!( "in nft_on_transfer; sender_id={}, previous_owner_id={}, token_id={}, msg={}", &sender_id, diff --git a/tests/init.rs b/tests/init.rs deleted file mode 100644 index 00360c58..00000000 --- a/tests/init.rs +++ /dev/null @@ -1,122 +0,0 @@ -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}; - -pub const TOKEN_ID: &str = "0"; - -pub async fn helper_mint( - nft_contract: &Contract, - token_id: TokenId, - title: String, - desc: String, -) -> anyhow::Result<()> { - let token_metadata = TokenMetadata { - title: Some(title), - description: Some(desc), - media: None, - media_hash: None, - copies: Some(1u64), - issued_at: None, - expires_at: None, - starts_at: None, - updated_at: None, - extra: None, - reference: None, - reference_hash: None, - }; - let res = nft_contract - .call("nft_mint") - .args_json((token_id, nft_contract.id(), token_metadata)) - .max_gas() - .deposit(NearToken::from_millinear(7)) - .transact() - .await?; - assert!(res.is_success()); - - Ok(()) -} - -/// 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, -) -> anyhow::Result<(Contract, Contract, Contract, Account)> { - let nft_wasm = near_workspaces::compile_project(".").await?; - let nft_contract = worker.dev_deploy(&nft_wasm).await?; - - let res = nft_contract - .call("new_default_meta") - .args_json((nft_contract.id(),)) - .max_gas() - .transact() - .await?; - assert!(res.is_success()); - - let token_metadata = TokenMetadata { - title: Some("Olympus Mons".into()), - description: Some("The tallest mountain in the charted solar system".into()), - media: None, - media_hash: None, - copies: Some(1u64), - issued_at: None, - expires_at: None, - starts_at: None, - updated_at: None, - extra: None, - reference: None, - reference_hash: None, - }; - let res = nft_contract - .call("nft_mint") - .args_json((TOKEN_ID, nft_contract.id(), token_metadata)) - .max_gas() - .deposit(NearToken::from_millinear(7)) - .transact() - .await?; - assert!(res.is_success()); - - let res = nft_contract - .as_account() - .create_subaccount("alice") - .initial_balance(NearToken::from_near(10)) - .transact() - .await?; - assert!(res.is_success()); - let alice = res.result; - - let token_receiver_wasm = - near_workspaces::compile_project("./tests/contracts/token-receiver").await?; - let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; - - let res = token_receiver_contract - .call("new") - .args_json((nft_contract.id(),)) - .max_gas() - .transact() - .await?; - assert!(res.is_success()); - - let approval_receiver_wasm = - near_workspaces::compile_project("./tests/contracts/approval-receiver").await?; - let approval_receiver_contract = worker.dev_deploy(&approval_receiver_wasm).await?; - - let res = approval_receiver_contract - .call("new") - .args_json((nft_contract.id(),)) - .max_gas() - .transact() - .await?; - assert!(res.is_success()); - - return Ok(( - nft_contract, - approval_receiver_contract, - token_receiver_contract, - alice, - )); -} diff --git a/tests/main.rs b/tests/main.rs index ee78f7f2..908799a7 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,2 +1,2 @@ -mod init; +mod common; mod tests; diff --git a/tests/tests/approval.rs b/tests/tests/approval.rs index 2789ba51..a594349c 100644 --- a/tests/tests/approval.rs +++ b/tests/tests/approval.rs @@ -1,6 +1,8 @@ -use crate::init::init; +use crate::common; use near_contract_standards::non_fungible_token::Token; +use near_workspaces::network::Sandbox; +use near_workspaces::Worker; use near_workspaces::{types::NearToken, AccountId}; use std::collections::HashMap; @@ -12,9 +14,52 @@ const ONE_NEAR: NearToken = NearToken::from_near(1); const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); #[tokio::test] -async fn test_simple_approve() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, alice) = init(&worker).await?; +async fn approval() -> anyhow::Result<()> { + let nft_wasm = near_workspaces::compile_project(".").await.unwrap(); + let token_receiver_wasm = near_workspaces::compile_project("./tests/contracts/token-receiver") + .await + .unwrap(); + let approval_receiver_wasm = + near_workspaces::compile_project("./tests/contracts/approval-receiver") + .await + .unwrap(); + let worker: near_workspaces::Worker = + near_workspaces::sandbox().await?; + + let simple_approval = test_simple_approve(&worker, &nft_wasm, &token_receiver_wasm); + let approval_with_call = test_approval_with_call(&worker, &nft_wasm, &approval_receiver_wasm); + let approved_account_transfers_token = + test_approved_account_transfers_token(&worker, &nft_wasm); + let revoke = test_revoke(&worker, &nft_wasm, &token_receiver_wasm); + let revoke_all = test_revoke_all(&worker, &nft_wasm, &token_receiver_wasm); + + // make sure they all pass + simple_approval.await?; + approval_with_call.await?; + approved_account_transfers_token.await?; + revoke.await?; + revoke_all.await?; + + Ok(()) +} + +pub async fn test_simple_approve( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + let alice = worker.dev_create_account().await?; // root approves alice let res = nft_contract @@ -107,10 +152,22 @@ async fn test_simple_approve() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_approval_with_call() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, approval_receiver_contract, _, _) = init(&worker).await?; +pub async fn test_approval_with_call( + worker: &Worker, + nft_wasm: &Vec, + approval_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + + let approval_receiver_contract = worker.dev_deploy(&approval_receiver_wasm).await?; let res = nft_contract .call("nft_approve") @@ -141,10 +198,21 @@ async fn test_approval_with_call() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_approved_account_transfers_token() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, alice) = init(&worker).await?; +pub async fn test_approved_account_transfers_token( + worker: &Worker, + nft_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + + let alice = worker.dev_create_account().await?; // root approves alice let res = nft_contract @@ -183,10 +251,23 @@ async fn test_approved_account_transfers_token() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_revoke() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, alice) = init(&worker).await?; +pub async fn test_revoke( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + let alice = worker.dev_create_account().await?; // root approves alice let res = nft_contract @@ -268,13 +349,41 @@ async fn test_revoke() -> anyhow::Result<()> { .json::()?; assert!(!token_receiver_approved); + // alice tries to send it to self and fails + let res = alice + .call(nft_contract.id(), "nft_transfer") + .args_json(( + alice.id(), + TOKEN_ID, + Some(1u64), + Some("gotcha! bahahaha".to_string()), + )) + .max_gas() + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_failure()); + Ok(()) } -#[tokio::test] -async fn test_revoke_all() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, alice) = init(&worker).await?; +pub async fn test_revoke_all( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + let alice = worker.dev_create_account().await?; // root approves alice let res = nft_contract @@ -328,5 +437,36 @@ async fn test_revoke_all() -> anyhow::Result<()> { .json::()?; assert!(!token_receiver_approved); + // alice tries to send it to self and fails + let res = alice + .call(nft_contract.id(), "nft_transfer") + .args_json(( + alice.id(), + TOKEN_ID, + Some(1u64), + Some("gotcha! bahahaha".to_string()), + )) + .max_gas() + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_failure()); + + // so does token_receiver + let res = token_receiver_contract + .as_account() + .call(nft_contract.id(), "nft_transfer") + .args_json(( + alice.id(), + TOKEN_ID, + Some(1u64), + Some("gotcha! bahahaha".to_string()), + )) + .max_gas() + .deposit(ONE_YOCTO) + .transact() + .await?; + assert!(res.is_failure()); + Ok(()) } diff --git a/tests/tests/core.rs b/tests/tests/core.rs index 7dfbb87b..f89ee656 100644 --- a/tests/tests/core.rs +++ b/tests/tests/core.rs @@ -1,14 +1,78 @@ -use crate::init::{init, TOKEN_ID}; +use crate::common; use near_contract_standards::non_fungible_token::Token; -use near_workspaces::types::NearToken; +use near_workspaces::{network::Sandbox, types::NearToken, Worker}; const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); +const TOKEN_ID: &str = "id-0"; #[tokio::test] -async fn test_simple_transfer() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, alice) = init(&worker).await?; +async fn core() -> anyhow::Result<()> { + let nft_wasm = near_workspaces::compile_project(".").await.unwrap(); + let token_receiver_wasm = near_workspaces::compile_project("./tests/contracts/token-receiver") + .await + .unwrap(); + + let worker: near_workspaces::Worker = + near_workspaces::sandbox().await?; + + let simple_transfer = test_simple_transfer(&worker, &nft_wasm); + let transfer_call_fast_return_to_sender = test_transfer_call_fast_return_to_sender( + &worker, + &nft_wasm, + &token_receiver_wasm, + ); + let transfer_call_slow_return_to_sender = test_transfer_call_slow_return_to_sender( + &worker, + &nft_wasm, + &token_receiver_wasm, + ); + let transfer_call_fast_keep_with_sender = test_transfer_call_fast_keep_with_sender( + &worker, + &nft_wasm, + &token_receiver_wasm, + ); + let transfer_call_slow_keep_with_sender = test_transfer_call_slow_keep_with_sender( + &worker, + &nft_wasm, + &token_receiver_wasm, + ); + let transfer_call_receiver_panics = test_transfer_call_receiver_panics( + &worker, + &nft_wasm, + &token_receiver_wasm, + ); + let transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no_log_if_not_enough_gas = test_transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no_log_if_not_enough_gas(&worker, &nft_wasm, &token_receiver_wasm); + let simple_transfer_no_logs_on_failure = test_simple_transfer_no_logs_on_failure( + &worker, + &nft_wasm, + ); + + simple_transfer.await?; + transfer_call_fast_return_to_sender.await?; + transfer_call_slow_return_to_sender.await?; + transfer_call_fast_keep_with_sender.await?; + transfer_call_slow_keep_with_sender.await?; + transfer_call_receiver_panics.await?; + transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no_log_if_not_enough_gas + .await?; + simple_transfer_no_logs_on_failure.await?; + + Ok(()) +} + +async fn test_simple_transfer(worker: &Worker, nft_wasm: &Vec) -> anyhow::Result<()> { + let alice = worker.dev_create_account().await?; + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let token = nft_contract .call("nft_token") @@ -46,10 +110,22 @@ async fn test_simple_transfer() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_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 test_transfer_call_fast_return_to_sender( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -77,10 +153,22 @@ async fn test_transfer_call_fast_return_to_sender() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_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 test_transfer_call_slow_return_to_sender( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -108,10 +196,22 @@ async fn test_transfer_call_slow_return_to_sender() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_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 test_transfer_call_fast_keep_with_sender( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -143,10 +243,22 @@ async fn test_transfer_call_fast_keep_with_sender() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_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 test_transfer_call_slow_keep_with_sender( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -177,10 +289,22 @@ async fn test_transfer_call_slow_keep_with_sender() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] -async fn test_transfer_call_receiver_panics() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; +async fn test_transfer_call_receiver_panics( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -211,11 +335,22 @@ async fn test_transfer_call_receiver_panics() -> anyhow::Result<()> { Ok(()) } -#[tokio::test] async fn test_transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no_log_if_not_enough_gas( + worker: &Worker, + nft_wasm: &Vec, + token_receiver_wasm: &Vec, ) -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, token_receiver_contract, _) = init(&worker).await?; + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + let token_receiver_contract = worker.dev_deploy(&token_receiver_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; let res = nft_contract .call("nft_transfer_call") @@ -246,10 +381,21 @@ async fn test_transfer_call_receiver_panics_and_nft_resolve_transfer_produces_no Ok(()) } -#[tokio::test] -async fn test_simple_transfer_no_logs_on_failure() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; +async fn test_simple_transfer_no_logs_on_failure( + worker: &Worker, + nft_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + + common::init_nft_contract(&nft_contract).await?; + common::mint_nft( + nft_contract.as_account(), + nft_contract.id(), + TOKEN_ID.into(), + nft_contract.id(), + ) + .await?; + let res = nft_contract .call("nft_transfer") diff --git a/tests/tests/enumeration.rs b/tests/tests/enumeration.rs index 712f79a0..b9f41348 100644 --- a/tests/tests/enumeration.rs +++ b/tests/tests/enumeration.rs @@ -1,51 +1,98 @@ -use crate::init::{helper_mint, init}; +use crate::common; use near_contract_standards::non_fungible_token::Token; use near_sdk::json_types::U128; -use near_workspaces::Contract; - -async fn mint_more(nft_contract: &Contract) -> anyhow::Result<()> { - helper_mint( - nft_contract, - "1".to_string(), - "Black as the Night".to_string(), - "In charcoal".to_string(), +use near_workspaces::{network::Sandbox, Worker}; + +#[tokio::test] +async fn enumeration() -> anyhow::Result<()> { + let nft_wasm = near_workspaces::compile_project(".").await.unwrap(); + let worker = near_workspaces::sandbox().await?; + + let enum_total_supply = test_enum_total_supply(&worker, &nft_wasm); + let enum_nft_tokens = test_enum_nft_tokens(&worker, &nft_wasm); + let enum_nft_supply_for_owner = test_enum_nft_supply_for_owner(&worker, &nft_wasm); + let enum_nft_tokens_for_owner = test_enum_nft_tokens_for_owner(&worker, &nft_wasm); + + enum_total_supply.await?; + enum_nft_tokens.await?; + enum_nft_supply_for_owner.await?; + enum_nft_tokens_for_owner.await?; + + Ok(()) +} + +async fn test_enum_total_supply( + worker: &Worker, + nft_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-0".into(), + nft_contract.id(), ) .await?; - helper_mint( - nft_contract, - "2".to_string(), - "Hamakua".to_string(), - "Vintage recording".to_string(), + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-1".into(), + nft_contract.id(), ) .await?; - helper_mint( - nft_contract, - "3".to_string(), - "Aloha ke akua".to_string(), - "Original with piano".to_string(), + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-2".into(), + nft_contract.id(), ) .await?; + let total_supply: U128 = nft_contract.call("nft_total_supply").view().await?.json()?; + assert_eq!(total_supply, U128::from(3)); + Ok(()) } -#[tokio::test] -async fn test_enum_total_supply() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; - mint_more(&nft_contract).await?; +async fn test_enum_nft_tokens(worker: &Worker, nft_wasm: &Vec) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; - let total_supply: U128 = nft_contract.call("nft_total_supply").view().await?.json()?; - assert_eq!(total_supply, U128::from(4)); + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-0".into(), + nft_contract.id(), + ) + .await?; - Ok(()) -} + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-1".into(), + nft_contract.id(), + ) + .await?; -#[tokio::test] -async fn test_enum_nft_tokens() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, _) = init(&worker).await?; - mint_more(&nft_contract).await?; + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-2".into(), + nft_contract.id(), + ) + .await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-3".into(), + nft_contract.id(), + ) + .await?; // No optional args should return all let mut tokens: Vec = nft_contract @@ -55,6 +102,7 @@ async fn test_enum_nft_tokens() -> anyhow::Result<()> { .await? .json()?; assert_eq!(tokens.len(), 4); + // Start at "1", with no limit arg tokens = nft_contract .call("nft_tokens") @@ -63,9 +111,9 @@ async fn test_enum_nft_tokens() -> anyhow::Result<()> { .await? .json()?; assert_eq!(tokens.len(), 3); - assert_eq!(tokens.get(0).unwrap().token_id, "1".to_string()); - assert_eq!(tokens.get(1).unwrap().token_id, "2".to_string()); - assert_eq!(tokens.get(2).unwrap().token_id, "3".to_string()); + assert_eq!(tokens.get(0).unwrap().token_id, "id-1".to_string()); + assert_eq!(tokens.get(1).unwrap().token_id, "id-2".to_string()); + assert_eq!(tokens.get(2).unwrap().token_id, "id-3".to_string()); // Start at "2", with limit 1 tokens = nft_contract @@ -75,7 +123,7 @@ async fn test_enum_nft_tokens() -> anyhow::Result<()> { .await? .json()?; assert_eq!(tokens.len(), 1); - assert_eq!(tokens.get(0).unwrap().token_id, "2".to_string()); + assert_eq!(tokens.get(0).unwrap().token_id, "id-2".to_string()); // Don't specify from_index, but limit 2 tokens = nft_contract @@ -85,16 +133,20 @@ async fn test_enum_nft_tokens() -> anyhow::Result<()> { .await? .json()?; assert_eq!(tokens.len(), 2); - assert_eq!(tokens.get(0).unwrap().token_id, "0".to_string()); - assert_eq!(tokens.get(1).unwrap().token_id, "1".to_string()); + assert_eq!(tokens.get(0).unwrap().token_id, "id-0".to_string()); + assert_eq!(tokens.get(1).unwrap().token_id, "id-1".to_string()); Ok(()) } -#[tokio::test] -async fn test_enum_nft_supply_for_owner() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, alice) = init(&worker).await?; +async fn test_enum_nft_supply_for_owner( + worker: &Worker, + nft_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + + let alice = worker.dev_create_account().await?; // Get number from account with no NFTs let owner_num_tokens: U128 = nft_contract @@ -111,9 +163,25 @@ async fn test_enum_nft_supply_for_owner() -> anyhow::Result<()> { .view() .await? .json()?; - assert_eq!(owner_num_tokens, U128::from(1)); + assert_eq!(owner_num_tokens, U128::from(0)); - mint_more(&nft_contract).await?; + common::mint_nft(&nft_contract.as_account(), nft_contract.id(), "id-0".into(), nft_contract.id()).await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-1".into(), + nft_contract.id(), + ) + .await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-2".into(), + alice.id(), + ) + .await?; let owner_num_tokens: U128 = nft_contract .call("nft_supply_for_owner") @@ -121,16 +189,59 @@ async fn test_enum_nft_supply_for_owner() -> anyhow::Result<()> { .view() .await? .json()?; - assert_eq!(owner_num_tokens, U128::from(4)); + assert_eq!(owner_num_tokens, U128::from(2)); + let alice_num_tokens: U128 = nft_contract + .call("nft_supply_for_owner") + .args_json((alice.id(),)) + .view() + .await? + .json()?; + assert_eq!(alice_num_tokens, U128::from(1)); + Ok(()) } -#[tokio::test] -async fn test_enum_nft_tokens_for_owner() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox().await?; - let (nft_contract, _, _, alice) = init(&worker).await?; - mint_more(&nft_contract).await?; +async fn test_enum_nft_tokens_for_owner( + worker: &Worker, + nft_wasm: &Vec, +) -> anyhow::Result<()> { + let nft_contract = worker.dev_deploy(&nft_wasm).await?; + common::init_nft_contract(&nft_contract).await?; + + let alice = worker.dev_create_account().await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-0".into(), + nft_contract.id(), + ) + .await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-1".into(), + nft_contract.id(), + ) + .await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-2".into(), + nft_contract.id(), + ) + .await?; + + common::mint_nft( + &nft_contract.as_account(), + nft_contract.id(), + "id-3".into(), + nft_contract.id(), + ) + .await?; // Get tokens from account with no NFTs let owner_tokens: Vec = nft_contract @@ -158,8 +269,8 @@ async fn test_enum_nft_tokens_for_owner() -> anyhow::Result<()> { .await? .json()?; assert_eq!(owner_tokens.len(), 2); - assert_eq!(owner_tokens.get(0).unwrap().token_id, "2".to_string()); - assert_eq!(owner_tokens.get(1).unwrap().token_id, "3".to_string()); + assert_eq!(owner_tokens.get(0).unwrap().token_id, "id-2".to_string()); + assert_eq!(owner_tokens.get(1).unwrap().token_id, "id-3".to_string()); // With from_index and limit 1 let owner_tokens: Vec = nft_contract @@ -169,7 +280,7 @@ async fn test_enum_nft_tokens_for_owner() -> anyhow::Result<()> { .await? .json()?; assert_eq!(owner_tokens.len(), 1); - assert_eq!(owner_tokens.get(0).unwrap().token_id, "1".to_string()); + assert_eq!(owner_tokens.get(0).unwrap().token_id, "id-1".to_string()); // No from_index but limit 3 let owner_tokens: Vec = nft_contract @@ -179,9 +290,9 @@ async fn test_enum_nft_tokens_for_owner() -> anyhow::Result<()> { .await? .json()?; assert_eq!(owner_tokens.len(), 3); - assert_eq!(owner_tokens.get(0).unwrap().token_id, "0".to_string()); - assert_eq!(owner_tokens.get(1).unwrap().token_id, "1".to_string()); - assert_eq!(owner_tokens.get(2).unwrap().token_id, "2".to_string()); + assert_eq!(owner_tokens.get(0).unwrap().token_id, "id-0".to_string()); + assert_eq!(owner_tokens.get(1).unwrap().token_id, "id-1".to_string()); + assert_eq!(owner_tokens.get(2).unwrap().token_id, "id-2".to_string()); Ok(()) } diff --git a/tests/tests/mod.rs b/tests/tests/mod.rs index 928ebc4f..5b524272 100644 --- a/tests/tests/mod.rs +++ b/tests/tests/mod.rs @@ -1,3 +1,3 @@ mod approval; -mod core; mod enumeration; +mod core;