#cosmwasm #testing #integration-tests #osmosis #contract #chain #module

osmosis-testing

CosmWasm 对 Osmosis 的集成测试

6 个版本

0.13.2 2022 年 12 月 9 日
0.13.1 2022 年 12 月 8 日
0.12.0 2022 年 9 月 30 日
0.1.8 2022 年 9 月 23 日

#9 in #osmosis

Download history 3/week @ 2024-03-10 25/week @ 2024-03-31 4/week @ 2024-04-07 1/week @ 2024-05-19

261 每月下载次数

MIT/Apache

135KB
1.5K SLoC

Rust 1.5K SLoC // 0.0% comments Go 348 SLoC // 0.1% comments

osmosis-testing

osmosis-testing on crates.io Docs

这是一个 CosmWasm x Osmosis 集成测试库,与 cw-multi-test 不同,它允许您测试您的 cosmwasm 合约与真实链的逻辑而不是模拟。

目录

入门

为了演示 osmosis-testing 的工作原理,让我们使用一个简单的示例合约:来自 cw-pluscw-whitelist

以下是设置测试的方法

use cosmwasm_std::Coin;
use osmosis_testing::OsmosisTestApp;

// create new osmosis appchain instance.
let app = OsmosisTestApp::new();

// create new account with initial funds
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "uatom"),
            Coin::new(1_000_000_000_000, "uosmo"),
        ],
        2,
    )
    .unwrap();

let admin = &accs[0];
let new_admin = &accs[1];

现在我们有 appchain 实例和账户,它们有一些初始余额,并且可以与 appchain 交互。这不会运行 Docker 实例或外部进程,它只是将 appchain 的代码作为库加载并创建一个内存中的实例。

请注意,init_accounts 是一个便利函数,它创建具有相同初始余额的多个账户。如果您只想创建一个账户,可以使用 init_account 代替。

use cosmwasm_std::Coin;
use osmosis_testing::OsmosisTestApp;

let app = OsmosisTestApp::new();

let account = app.init_account(&[
    Coin::new(1_000_000_000_000, "uatom"),
    Coin::new(1_000_000_000_000, "uosmo"),
]);

现在,如果我们想测试一个 cosmwasm 合约,我们需要

  • 构建 wasm 文件
  • 存储代码
  • 实例化

然后我们可以开始与我们的合约交互。让我们这样做。

use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg}; // for instantiating cw1_whitelist contract
use osmosis_testing::{Account, Module, OsmosisTestApp, Wasm};

let app = OsmosisTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "uatom"),
            Coin::new(1_000_000_000_000, "uosmo"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

// ============= NEW CODE ================

// `Wasm` is the module we use to interact with cosmwasm releated logic on the appchain
// it implements `Module` trait which you will see more later.
let wasm = Wasm::new(&app);

// Load compiled wasm bytecode
let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;

请注意,在这个示例中,它加载了来自 cw-plus 发布版 的 wasm 字节码,仅用于简单演示。您可能想运行 cargo wasm 并在 target/wasm32-unknown-unknown/release/<contract_name>.wasm 中找到您的 wasm 文件。

use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, AdminListResponse};
use osmosis_testing::{Account, Module, OsmosisTestApp, Wasm};

let app = OsmosisTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "uatom"),
            Coin::new(1_000_000_000_000, "uosmo"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

let wasm = Wasm::new(&app);


let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;

// ============= NEW CODE ================

// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
    .instantiate(
        code_id,
        &InstantiateMsg {
            admins: init_admins.clone(),
            mutable: true,
        },
        None, // contract admin used for migration, not the same as cw1_whitelist admin
        None, // contract label
        &[], // funds
        admin, // signer
    )
    .unwrap()
    .data
    .address;

// query contract state to check if contract instantiation works properly
let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);

现在让我们执行合约并验证合约的状态是否已正确更新。

use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg, QueryMsg, ExecuteMsg, AdminListResponse};
use osmosis_testing::{Account, Module, OsmosisTestApp, Wasm};

let app = OsmosisTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000, "uatom"),
            Coin::new(1_000_000_000_000, "uosmo"),
        ],
        2,
    )
    .unwrap();
let admin = &accs[0];
let new_admin = &accs[1];

let wasm = Wasm::new(&app);


let wasm_byte_code = std::fs::read("./test_artifacts/cw1_whitelist.wasm").unwrap();
let code_id = wasm
    .store_code(&wasm_byte_code, None, admin)
    .unwrap()
    .data
    .code_id;

// instantiate contract with initial admin and make admin list mutable
let init_admins = vec![admin.address()];
let contract_addr = wasm
    .instantiate(
        code_id,
        &InstantiateMsg {
            admins: init_admins.clone(),
            mutable: true,
        },
        None, // contract admin used for migration, not the same as cw1_whitelist admin
        None, // contract label
        &[], // funds
        admin, // signer
    )
    .unwrap()
    .data
    .address;

let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, init_admins);
assert!(admin_list.mutable);

// ============= NEW CODE ================

// update admin list and rechec the state
let new_admins = vec![new_admin.address()];
wasm.execute::<ExecuteMsg>(
    &contract_addr,
    &ExecuteMsg::UpdateAdmins {
        admins: new_admins.clone(),
    },
    &[],
    admin,
)
.unwrap();

let admin_list = wasm
    .query::<QueryMsg, AdminListResponse>(&contract_addr, &QueryMsg::AdminList {})
    .unwrap();

assert_eq!(admin_list.admins, new_admins);
assert!(admin_list.mutable);

调试

在您的合约代码中,如果您想进行调试,可以使用 deps.api.debug(..),这将把调试信息打印到标准输出。默认情况下,wasmd 禁用了这个功能,但 OsmosisTestApp 允许输出标准输出,这样您就可以在测试运行时调试智能合约。

使用模块包装器

在某些情况下,您可能想直接与appchain逻辑交互来设置环境或查询appchain的状态。模块包装器提供了方便的函数来与appchain的模块交互。

让我们尝试与 Gamm 模块交互

use cosmwasm_std::Coin;
use osmosis_testing::{Account, Module, OsmosisTestApp, Gamm};

let app = OsmosisTestApp::default();
let alice = app
    .init_account(&[
        Coin::new(1_000_000_000_000, "uatom"),
        Coin::new(1_000_000_000_000, "uosmo"),
    ])
    .unwrap();

// create Gamm Module Wrapper
let gamm = Gamm::new(&app);

// create balancer pool with basic configuration
let pool_liquidity = vec![Coin::new(1_000, "uatom"), Coin::new(1_000, "uosmo")];
let pool_id = gamm
    .create_basic_pool(&pool_liquidity, &alice)
    .unwrap()
    .data
    .pool_id;

// query pool and assert if the pool is created successfully
let pool = gamm.query_pool(pool_id).unwrap();
assert_eq!(
    pool_liquidity
        .into_iter()
        .map(|c| c.into())
        .collect::<Vec<osmosis_std::types::cosmos::base::v1beta1::Coin>>(),
    pool.pool_assets
        .into_iter()
        .map(|a| a.token.unwrap())
        .collect::<Vec<osmosis_std::types::cosmos::base::v1beta1::Coin>>(),
);

自定义模块包装器

您可能找不到想要的包装器,或者提供的包装器过于冗长。好消息是,创建自己的包装器非常简单。

以下是作为库用户如何重新定义 Gamm 模块包装器的示例

use osmosis_std::types::osmosis::gamm::{
    poolmodels::balancer::v1beta1::{MsgCreateBalancerPool, MsgCreateBalancerPoolResponse},
    v1beta1::{QueryPoolRequest, QueryPoolResponse},
};

use osmosis_testing::{fn_execute, fn_query};
use osmosis_testing::{Module, Runner};


// Boilerplate code, copy and rename should just do the trick
pub struct Gamm<'a, R: Runner<'a>> {
    runner: &'a R,
}

impl<'a, R: Runner<'a>> Module<'a, R> for Gamm<'a, R> {
    fn new(runner: &'a R) -> Self {
        Self { runner }
    }
}
// End Boilerplate code

impl<'a, R> Gamm<'a, R>
where
    R: Runner<'a>,
{
    // macro for creating execute function
    fn_execute! {
        // (pub)? <fn_name>: <request_type> => <response_type>
        pub create_balancer_pool: MsgCreateBalancerPool => MsgCreateBalancerPoolResponse
    }

    // macro for creating query function
    fn_query! {
        // (pub)? <fn_name> [<method_path>]: <request_type> => <response_type>
        pub query_pool ["/osmosis.gamm.v1beta1.Query/Pool"]: QueryPoolRequest => QueryPoolResponse
    }
}

如果生成的宏函数不足以满足您的要求,您可以手动编写自己的函数。有关更多灵感,请参阅 模块目录

依赖关系

~20MB
~408K SLoC