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

test: add test case for contract migration with non-trivial migration data #113

Merged
merged 1 commit into from
Dec 12, 2024
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
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 _;
milapsheth marked this conversation as resolved.
Show resolved Hide resolved

#[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
Loading