use-ink/ink

Off-Chain Test (endowment) Does Not Work

sulnong opened this issue · 5 comments

Environment:
cargo-contract : 0.17.0-unknown-x86_64-macos
ink version: 3.0.0-rc8

Describe the bug
Following is my contract code:

/// Deposit
#[ink(message, payable)]
pub fn deposit(&mut self) {
    let caller = self.env().caller();
    let deposited = self.env().transferred_value();
    let mut balance = self.balance_of(&caller);
    balance += deposited;
    self.balances.insert(caller, balance);
}

In off-chain test (run with command cargo +nightly test), i got self.env().transferred_value() correctly, but account (who call this execution) balance have no change.
That makes test failed.

Following is test code:

#[ink::test]
fn deposit_works() {
    // Use Alice as contract owner
    let accounts = default_accounts();
    let alice = accounts.alice;
    let bob = accounts.bob;
    let eve = accounts.eve;
    set_sender(alice, None);
    let mut contract = Bytepay::new();

    // Bob Deposit
    set_balance(bob, 200);
    set_sender(bob, Some(100));
    contract.deposit();
    assert_eq!(contract.get(), 100);

    // Eve havn't deposit
    set_balance(eve, 200);
    set_sender(eve, None);
    assert_eq!(contract.get(), 0);
    assert_eq!(get_balance(eve), 200);
}

fn set_sender(sender: AccountId, endowment: Option<Balance>) {
    let callee = ink_env::account_id::<ink_env::DefaultEnvironment>();
    test::push_execution_context::<Environment>(
        sender,
        callee,
        1000000,
        endowment.unwrap_or(0),
        test::CallData::new(call::Selector::new([0x00; 4])), // dummy
    );
}

fn default_accounts() -> ink_env::test::DefaultAccounts<ink_env::DefaultEnvironment> {
    ink_env::test::default_accounts::<ink_env::DefaultEnvironment>()
        .expect("Off-chain environment should have been initialized already")
}

fn set_balance(account_id: AccountId, balance: Balance) {
    ink_env::test::set_account_balance::<ink_env::DefaultEnvironment>(account_id, balance)
        .expect("Cannot set account balance");
}

fn get_balance(account_id: AccountId) -> Balance {
    ink_env::test::get_account_balance::<ink_env::DefaultEnvironment>(account_id)
        .expect("Cannot set account balance")
}
}

I wander if i coding incorrect or this is off-chain test problem.

@sulnong can you post the full error that you're getting?

/// Deposit
#[ink(message, payable)]
pub fn deposit(&mut self) {
    let caller = self.env().caller();
    let deposited = self.env().transferred_value();
    println!("deposited: {}", deposited);
    let mut balance = self.balance_of(&caller);
    balance += deposited;
    self.balances.insert(caller, balance);
}

#[ink::test]
fn deposit_works() {
    // Use Alice as contract owner
    let accounts = default_accounts();
    let alice = accounts.alice;
    let bob = accounts.bob;
    let eve = accounts.eve;
    set_sender(alice, None);
    let mut contract = Bytepay::new();

    // Bob Deposit
    set_balance(bob, 200);
    set_sender(bob, Some(100));
    contract.deposit();
    assert_eq!(contract.get(), 100);
    assert_eq!(get_balance(bob), 100);

    // Eve havn't deposit
    set_balance(eve, 200);
    set_sender(eve, None);
    assert_eq!(contract.get(), 0);
    assert_eq!(get_balance(eve), 200);
}
running 5 tests
test bytepay::tests::set_whitelist_works ... ok
test bytepay::tests::contract_init_works ... ok
test bytepay::tests::transfer_works ... ok
test bytepay::tests::withdraw_works ... FAILED
test bytepay::tests::deposit_works ... FAILED

failures:

---- bytepay::tests::withdraw_works stdout ----
deposited: 100
thread 'bytepay::tests::withdraw_works' panicked at 'assertion failed: self.env().transfer(caller, amount).is_ok()', lib.rs:70:13

---- bytepay::tests::deposit_works stdout ----
deposited: 100
thread 'bytepay::tests::deposit_works' panicked at 'assertion failed: `(left == right)`
  left: `200`,
 right: `100`', lib.rs:162:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    bytepay::tests::deposit_works
    bytepay::tests::withdraw_works

test result: FAILED. 3 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

After call contract.deposit(), bob'account balance shoule be 100, we can see in the console deposited: 100
But assert_eq!(get_balance(bob), 100) failed.

Tbh this looks fine to me. Even looking at the contract-transfer example it doesn't seem like the contract_id and eve's balances get updated correctly, so maybe there are some quirks around transfers in the test environment.

@cmichi can you take a look since you know about this better than me?

Further to this, testing does not seem to work at all using ink_env::test module. I followed these steps to run tests related with balances and set_caller:

add ink-experimental-engine = ["ink_env/ink-experimental-engine"] to Cargo.toml

add #[cfg(feature = "ink-experimental-engine")] above mod tests {}

despite this, ink_env::tests::default_accounts() is not found, nor is ink_env::tests::set_caller() working. It is only possible to test pure rust parts of the smart contracts that don't involve transfers or various accounts other than the constructor caller.

error[E0425]: cannot find function set_caller in module ink_env::test
--> pixelland_map/lib.rs:194:28
|
194 | ink_env::test::set_caller::<ink_env::DefaultEnvironment>(accounts.bob);
| ^^^^^^^^^^ not found in ink_env::test

error[E0609]: no field bob on type std::result::Result<DefaultAccounts<DefaultEnvironment>, ink_env::Error>
--> pixelland_map/lib.rs:194:79
|
194 | ink_env::test::set_caller::<ink_env::DefaultEnvironment>(accounts.bob);
| ^^^

error[E0609]: no field bob on type std::result::Result<DefaultAccounts<DefaultEnvironment>, ink_env::Error>
--> pixelland_map/lib.rs:202:42
|
202 | assert_eq!(result3, accounts.bob);
| ^^^