Skip to content
Merged
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
21 changes: 21 additions & 0 deletions near-plugins-derive/tests/contracts/upgradable_2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "upgradable_2"
version = "0.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
near-plugins = { path = "../../../../near-plugins" }
near-sdk = "4.1.0"

[profile.release]
codegen-units = 1
opt-level = "z"
lto = true
debug = false
panic = "abort"
overflow-checks = true

[workspace]
8 changes: 8 additions & 0 deletions near-plugins-derive/tests/contracts/upgradable_2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build:
cargo build --target wasm32-unknown-unknown --release

# Helpful for debugging. Requires `cargo-expand`.
expand:
cargo expand > expanded.rs

.PHONY: build expand
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "1.66.1"
components = ["clippy", "rustfmt"]
22 changes: 22 additions & 0 deletions near-plugins-derive/tests/contracts/upgradable_2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! A simple contract to be deployed via `Upgradable`.

use near_plugins::{Ownable, Upgradable};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::{near_bindgen, PanicOnDefault};

/// The struct is the same as in the initial contract `../upgradable`, so no [state migration] is
/// required.
///
/// [state migration]: https://docs.near.org/develop/upgrade#migrating-the-state
#[near_bindgen]
#[derive(Ownable, Upgradable, PanicOnDefault, BorshDeserialize, BorshSerialize)]
pub struct Contract;

#[near_bindgen]
impl Contract {
/// A method that is _not_ defined in the initial contract, so its existence proves the
/// contract defined in this file was deployed.
pub fn is_upgraded() -> bool {
true
}
}
57 changes: 52 additions & 5 deletions near-plugins-derive/tests/upgradable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ pub mod common;
use anyhow::Ok;
use common::upgradable_contract::UpgradableContract;
use common::utils::{
assert_failure_with, assert_only_owner_permission_failure, assert_success_with_unit_return,
fast_forward_beyond, get_transaction_block, sdk_duration_from_secs,
assert_failure_with, assert_only_owner_permission_failure, assert_success_with,
assert_success_with_unit_return, fast_forward_beyond, get_transaction_block,
sdk_duration_from_secs,
};
use near_sdk::serde_json::json;
use near_sdk::{CryptoHash, Duration, Timestamp};
use std::path::Path;
use workspaces::network::Sandbox;
use workspaces::result::ExecutionFinalResult;
use workspaces::{Account, AccountId, Worker};
use workspaces::{Account, AccountId, Contract, Worker};

const PROJECT_PATH: &str = "./tests/contracts/upgradable";
const PROJECT_PATH_2: &str = "./tests/contracts/upgradable_2";

const ERR_MSG_NO_STAGING_TS: &str = "Upgradable: staging timestamp isn't set";
const ERR_MSG_DEPLOY_CODE_TOO_EARLY: &str = "Upgradable: Deploy code too early: staging ends on";
Expand All @@ -27,6 +29,8 @@ const ERR_MSG_UPDATE_DURATION_TOO_EARLY: &str =
struct Setup {
/// The worker interacting with the current sandbox.
worker: Worker<Sandbox>,
/// A deployed instance of the contract.
contract: Contract,
/// Wrapper around the deployed contract that facilitates interacting with methods provided by
/// the `Upgradable` plugin.
upgradable_contract: UpgradableContract,
Expand Down Expand Up @@ -64,6 +68,7 @@ impl Setup {
let unauth_account = worker.dev_create_account().await?;
Ok(Self {
worker,
contract,
upgradable_contract,
unauth_account,
})
Expand Down Expand Up @@ -143,6 +148,17 @@ impl Setup {
.expect("Call to up_get_delay_status should succeed");
assert_eq!(status.new_staging_duration_timestamp, expected_timestamp);
}

async fn call_is_upgraded(&self, caller: &Account) -> workspaces::Result<ExecutionFinalResult> {
// `is_upgraded` could be called via `view`, however here it is called via `transact` so we
// get an `ExecutionFinalResult` that can be passed to `assert_*` methods from
// `common::utils`. It is acceptable since all we care about is whether the method exists.
caller
.call(self.contract.id(), "is_upgraded")
.max_gas()
.transact()
.await
}
}

/// Panics if the conversion fails.
Expand Down Expand Up @@ -379,8 +395,39 @@ async fn test_deploy_code_without_delay() -> anyhow::Result<()> {
Ok(())
}

/// TODO stage code that corresponds to a valid contract. Call a method of that contract to verify
/// the staged code was deployed.
/// Verifies the upgrade was successful by calling a method that's available only on the upgraded
/// contract. Ensures the new contract can be deployed and state migration succeeds.
#[tokio::test]
async fn test_deploy_code_and_call_method() -> anyhow::Result<()> {
let worker = workspaces::sandbox().await?;
let owner = worker.dev_create_account().await?;
let setup = Setup::new(worker.clone(), Some(owner.id().clone()), None).await?;

// Verify function `is_upgraded` is not defined in the initial contract.
let res = setup.call_is_upgraded(&setup.unauth_account).await?;
assert_failure_with(res, "Action #0: MethodResolveError(MethodNotFound)");

// Compile the other version of the contract and stage its code.
let code = common::repo::compile_project(Path::new(PROJECT_PATH_2), "upgradable_2").await?;
let res = setup
.upgradable_contract
.up_stage_code(&owner, code.clone())
.await?;
assert_success_with_unit_return(res);
setup.assert_staged_code(Some(code)).await;

// Deploy staged code.
let res = setup.upgradable_contract.up_deploy_code(&owner).await?;
assert_success_with_unit_return(res);

// The newly deployed contract defines the function `is_upgraded`. Calling it successfully
// verifies the staged contract is deployed and there are no issues with state migration.
let res = setup.call_is_upgraded(&setup.unauth_account).await?;
assert_success_with(res, true);

Ok(())
}

#[tokio::test]
async fn test_deploy_code_with_delay() -> anyhow::Result<()> {
let worker = workspaces::sandbox().await?;
Expand Down