Skip to content

Commit

Permalink
test: add test case for contract migration with non-trivial migration…
Browse files Browse the repository at this point in the history
… data
  • Loading branch information
cgorenflo committed Dec 11, 2024
1 parent 8798898 commit 8fbc4f7
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 15 deletions.
6 changes: 3 additions & 3 deletions packages/axelar-soroban-std-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn derive_operatable(input: TokenStream) -> TokenStream {
let name = &input.ident;

quote! {
use axelar_soroban_std::interfaces::OperatableInterface;
use axelar_soroban_std::interfaces::OperatableInterface as _;

#[soroban_sdk::contractimpl]
impl axelar_soroban_std::interfaces::OperatableInterface for #name {
Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn derive_ownable(input: TokenStream) -> TokenStream {
let name = &input.ident;

quote! {
use axelar_soroban_std::interfaces::OwnableInterface;
use axelar_soroban_std::interfaces::OwnableInterface as _;

#[soroban_sdk::contractimpl]
impl axelar_soroban_std::interfaces::OwnableInterface for #name {
Expand Down Expand Up @@ -195,7 +195,7 @@ pub fn derive_upgradable(input: TokenStream) -> TokenStream {
.map_or_else(|| quote! { () }, |ty| quote! { #ty });

quote! {
use axelar_soroban_std::interfaces::{UpgradableInterface, MigratableInterface};
use axelar_soroban_std::interfaces::{UpgradableInterface as _, MigratableInterface as _};

#[soroban_sdk::contractimpl]
impl axelar_soroban_std::interfaces::UpgradableInterface for #name {
Expand Down
1 change: 1 addition & 0 deletions packages/axelar-soroban-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ hex = { workspace = true, optional = true }
soroban-sdk = { workspace = true }

[dev-dependencies]
axelar-soroban-std-derive = { workspace = true }
goldie = { workspace = true }
soroban-sdk = { workspace = true, features = ["testutils"] }

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// this is only needed in this crate itself, any crate that imports this one doesn't have to do this manual import resolution
use crate as axelar_soroban_std;

use crate::interfaces::testdata::contract_trivial_migration::DataKey;
use crate::interfaces::{operatable, ownable, MigratableInterface};
use axelar_soroban_std_derive::{Ownable, Upgradable};
use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, Env, String};

#[derive(Upgradable, Ownable)]
#[migratable(with_type = MigrationData)]
#[contract]
pub struct ContractNonTrivial;

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MigrationData {
pub data1: String,
pub data2: bool,
pub data3: u32,
}

#[contractimpl]
impl ContractNonTrivial {
pub fn __constructor(_env: Env, owner: Option<Address>, operator: Option<Address>) {
if let Some(owner) = owner {
ownable::set_owner(&_env, &owner);
}

if let Some(operator) = operator {
operatable::set_operator(&_env, &operator);
}
}

pub fn migration_data(env: &Env) -> Option<String> {
env.storage().instance().get(&DataKey::Data)
}

fn run_migration(env: &Env, migration_data: MigrationData) {
env.storage()
.instance()
.set(&DataKey::Data, &migration_data.data1);
}
}

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ContractError {
MigrationNotAllowed = 1,
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ impl Contract {
#[contractimpl]
impl MigratableInterface for Contract {
type MigrationData = ();
type Error = ContractError;
type Error = TrivialContractError;

fn migrate(env: &Env, migration_data: ()) -> Result<(), ContractError> {
fn migrate(env: &Env, migration_data: ()) -> Result<(), TrivialContractError> {
upgradable::migrate::<Self>(env, || Self::run_migration(env, migration_data))
.map_err(|_| ContractError::SomeFailure)
.map_err(|_| TrivialContractError::SomeFailure)
}
}

Expand Down Expand Up @@ -85,7 +85,7 @@ pub enum DataKey {

#[contracterror]
#[derive(Debug)]
pub enum ContractError {
pub enum TrivialContractError {
SomeFailure = 1,
}

Expand Down
6 changes: 4 additions & 2 deletions packages/axelar-soroban-std/src/interfaces/testdata/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod contract;
mod contract_non_trivial_migration;
mod contract_trivial_migration;

pub use contract::{Contract, ContractClient};
pub use contract_non_trivial_migration::{ContractNonTrivialClient, MigrationData};
pub use contract_trivial_migration::{Contract, ContractClient};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M)
topics: (Symbol(upgraded))
data: (String(0.1.0))
33 changes: 30 additions & 3 deletions packages/axelar-soroban-std/src/interfaces/upgradable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,13 @@ mod test {
use crate::interfaces::upgradable::UpgradedEvent;
use crate::{assert_invoke_auth_err, assert_invoke_auth_ok, events};

use crate::interfaces::testdata::ContractClient;
use crate::interfaces::testdata::{ContractClient, ContractNonTrivialClient, MigrationData};
use crate::interfaces::{testdata, upgradable};
use soroban_sdk::testutils::Address as _;
use soroban_sdk::{Address, BytesN, Env, String};

const WASM: &[u8] = include_bytes!("testdata/contract.wasm");
const WASM: &[u8] = include_bytes!("testdata/contract_trivial_migration.wasm");
const WASM_NON_TRIVIAL: &[u8] = include_bytes!("testdata/contract_non_trivial_migration.wasm");

fn prepare_client_and_bytecode(
env: &Env,
Expand Down Expand Up @@ -192,7 +193,7 @@ mod test {
}

#[test]
fn migrate_succeeds_if_owner_is_authenticated_and_called_after_upgrade() {
fn trivial_migrate_succeeds_if_owner_is_authenticated_and_called_after_upgrade() {
let env = Env::default();
let owner = Address::generate(&env);
let (client, hash) = prepare_client_and_bytecode(&env, Some(owner.clone()));
Expand All @@ -211,6 +212,32 @@ mod test {
goldie::assert!(events::fmt_last_emitted_event::<UpgradedEvent>(&env))
}

#[test]
fn non_trivial_migrate_succeeds_if_owner_is_authenticated_and_called_after_upgrade() {
let env = Env::default();
let owner = Address::generate(&env);
let operator = Address::generate(&env);
let contract_id = env.register(testdata::Contract, (owner.clone(), operator));
let hash = env.deployer().upload_contract_wasm(WASM_NON_TRIVIAL);
let client = ContractNonTrivialClient::new(&env, &contract_id);

assert_invoke_auth_ok!(owner, client.try_upgrade(&hash));

assert!(client.migration_data().is_none());

let data = MigrationData {
data1: String::from_str(&env, "migrated_non_trivial"),
data2: true,
data3: 42,
};

assert_invoke_auth_ok!(owner, client.try_migrate(&data));

assert_eq!(client.migration_data(), Some(data.data1));

goldie::assert!(events::fmt_last_emitted_event::<UpgradedEvent>(&env))
}

// Because migration happens on a contract loaded from WASM, code coverage analysis doesn't recognize
// the migration code as covered. This test repeats the migration test with mocked setup
#[test]
Expand Down

0 comments on commit 8fbc4f7

Please sign in to comment.