Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Creation of test functions for contract near-contract-standards #1229

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/fungible-token/ft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ crate-type = ["cdylib"]
[dependencies]
near-sdk = { path = "../../../near-sdk" }
near-contract-standards = { path = "../../../near-contract-standards" }

[dev-dependencies]
near-contract-standards = { path = "../../../near-contract-standards", default-features = false, features = ["unit-testing"]}
27 changes: 27 additions & 0 deletions examples/fungible-token/ft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,33 @@ mod tests {
builder
}

#[test]
fn test_fungible_token_core() {
let mut context = get_context(accounts(2));
testing_env!(context.build());
let mut contract = Contract::new_default_meta(accounts(2).into(), TOTAL_SUPPLY.into());
testing_env!(context
.storage_usage(env::storage_usage())
.attached_deposit(contract.storage_balance_bounds().min.into())
.predecessor_account_id(accounts(1))
.build());
// Paying for account registration, aka storage deposit
contract.storage_deposit(None, None);

testing_env!(context
.storage_usage(env::storage_usage())
.attached_deposit(NearToken::from_yoctonear(1))
.predecessor_account_id(accounts(2))
.build());
near_contract_standards::fungible_token::core::tests::test(&mut contract);
}

#[test]
fn test_fungible_token_metadata() {
let mut contract = Contract::new_default_meta(accounts(2).into(), TOTAL_SUPPLY.into());
near_contract_standards::fungible_token::metadata::tests::test(&mut contract);
}

#[test]
fn test_new() {
let mut context = get_context(accounts(1));
Expand Down
1 change: 1 addition & 0 deletions near-contract-standards/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ near-sdk = { path = "../near-sdk", default-features = false, features = ["unit-t
[features]
default = []
abi = ["near-sdk/abi"]
unit-testing = []
35 changes: 35 additions & 0 deletions near-contract-standards/src/fungible_token/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,38 @@ pub trait FungibleTokenCore {
/// Returns the balance of the account. If the account doesn't exist must returns `"0"`.
fn ft_balance_of(&self, account_id: AccountId) -> U128;
}

#[cfg(all(not(target_arch = "wasm32"), feature = "unit-testing"))]
pub mod tests {

use near_sdk::test_utils::accounts;

use super::*;

fn test_transfer_ok(contract: &mut impl FungibleTokenCore) {
assert_eq!(contract.ft_balance_of(accounts(2)), U128(1_000_000_000_000_000));
assert_eq!(contract.ft_balance_of(accounts(1)), U128(0));
let amount = contract.ft_total_supply().0 / 4;
contract.ft_transfer(accounts(1), amount.into(), None);
assert_eq!(contract.ft_balance_of(accounts(2)).0, contract.ft_total_supply().0 - amount);
assert_eq!(contract.ft_balance_of(accounts(1)).0, amount);
}

fn test_transfer_call_ok(contract: &mut impl FungibleTokenCore) {
let amount = contract.ft_total_supply().0 / 4;
let result = contract.ft_transfer_call(accounts(1), amount.into(), None, "".to_string());
match result {
PromiseOrValue::Promise(_promise) => {
println!("Handle promise here");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@frol I am having a hard time here trying to get the data from the Promise, I read documentation on how to resolve promises, and they need a callback, the problem is that I dont know if I should make the call back function in here or where I am calling this function.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't be able to read the content of _promise, instead, use near_sdk::test_utils::get_created_receipts

}
_ => panic!("Expected Promise variant"),
}
assert_eq!(contract.ft_balance_of(accounts(2)).0, contract.ft_total_supply().0 / 2);
assert_eq!(contract.ft_balance_of(accounts(1)).0, contract.ft_total_supply().0 / 2);
}

pub fn test(contract: &mut impl FungibleTokenCore) {
test_transfer_ok(contract);
test_transfer_call_ok(contract);
Comment on lines +134 to +135
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need negative tests, such as:

  1. Attempt to transfer more tokens that sender account has on its balance - should fail and balance remain the same for both accounts
  2. Attempt to transfer to a non-registered account - should fail since storage cost is not covered
  3. Attempt to transfer from a non-registered account

}
}
21 changes: 21 additions & 0 deletions near-contract-standards/src/fungible_token/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,24 @@ impl FungibleTokenMetadata {
}
}
}

#[cfg(all(not(target_arch = "wasm32"), feature = "unit-testing"))]
pub mod tests {
use super::FungibleTokenMetadataProvider;
use crate::fungible_token::metadata::FT_METADATA_SPEC;

fn ft_metadata_ok(contract: &mut impl FungibleTokenMetadataProvider) {
let metadata = contract.ft_metadata();
assert_eq!(metadata.spec, FT_METADATA_SPEC.to_string());
assert_eq!(metadata.name, "Example NEAR fungible token");
assert_eq!(metadata.symbol, "EXAMPLE".to_string());
assert_eq!(metadata.icon, Some("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 288 288'%3E%3Cg id='l' data-name='l'%3E%3Cpath d='M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z'/%3E%3C/g%3E%3C/svg%3E".to_string()));
assert_eq!(metadata.reference, None);
assert_eq!(metadata.reference_hash, None);
assert_eq!(metadata.decimals, 24);
Comment on lines +40 to +46
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should only validate the structure and logic. The contract is not required to hold specific metadata values. In the case of metadata, we may just test that calling the method does not crash, so just calling it will be enough and the rest is handled by Rust (we already know that the contract implements FungibleTokenMetadataProvider, and as such we know that the returned value is standard-compliant.

}

pub fn test(contract: &mut impl FungibleTokenMetadataProvider) {
ft_metadata_ok(contract);
}
}