22个版本 (11个主要版本)
25.0.0 | 2024年5月17日 |
---|---|
24.0.1 | 2024年4月17日 |
22.1.0 | 2024年1月24日 |
21.0.0 | 2023年12月15日 |
0.1.0 | 2022年12月21日 |
#508 在 魔法豆
每月下载 3,705 次
在 17 个 仓库中使用 17 个(直接使用6个)
180KB
4.5K SLoC
osmosis-test-tube
CosmWasm x Osmosis集成测试库,与cw-multi-test
不同,它允许您测试您的cosmwasm合约与真实链的逻辑,而不是模拟。
目录
入门
为了演示osmosis-test-tube
的工作原理,让我们使用一个简单的示例合约:来自cw-plus
的cw-whitelist。
以下是设置测试的步骤
use cosmwasm_std::Coin;
use osmosis_test_tube::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_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "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_test_tube::OsmosisTestApp;
let app = OsmosisTestApp::new();
let account = app.init_account(&[
Coin::new(1_000_000_000_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "uosmo"),
]);
现在,如果我们想测试一个cosmwasm合约,我们需要
- 构建wasm文件
- 存储代码
- 实例化
然后我们可以开始与我们的合约交互。让我们这么做。
use cosmwasm_std::Coin;
use cw1_whitelist::msg::{InstantiateMsg}; // for instantiating cw1_whitelist contract
use osmosis_test_tube::{Account, Module, OsmosisTestApp, Wasm};
let app = OsmosisTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "uosmo"),
],
2,
)
.unwrap();
let admin = &accs[0];
let new_admin = &accs[1];
// ============= NEW CODE ================
// `Wasm` is the module we use to interact with cosmwasm related 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_test_tube::{Account, Module, OsmosisTestApp, Wasm};
let app = OsmosisTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "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
Some("cw1_whitelist"), // 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_test_tube::{Account, Module, OsmosisTestApp, Wasm};
let app = OsmosisTestApp::new();
let accs = app
.init_accounts(
&[
Coin::new(1_000_000_000_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "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
Some("cw1_whitelist"), // 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(..)
,这将把调试信息打印到 stdout。默认情况下,wasmd
禁用了这个功能,但 OsmosisTestApp
允许 stdout 输出,这样你就可以在测试运行时调试智能合约。
使用模块包装器
在某些情况下,你可能想直接与 appchain 逻辑交互来设置环境或查询 appchain 的状态。模块包装器提供方便的函数来与 appchain 的模块交互。
让我们尝试与 Gamm
模块交互
use cosmwasm_std::Coin;
use osmosis_test_tube::{Account, Module, OsmosisTestApp, Gamm};
let app = OsmosisTestApp::default();
let alice = app
.init_account(&[
Coin::new(1_000_000_000_000u128, "uatom"),
Coin::new(1_000_000_000_000u128, "uosmo"),
])
.unwrap();
// create Gamm Module Wrapper
let gamm = Gamm::new(&app);
// create balancer pool with basic configuration
let pool_liquidity = vec![Coin::new(1_000u128, "uatom"), Coin::new(1_000u128, "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},
};
use osmosis_std::types::osmosis::gamm::v2::{QuerySpotPriceRequest, QuerySpotPriceResponse};
use osmosis_test_tube::{fn_execute, fn_query};
use osmosis_test_tube::{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_spot_price ["/osmosis.gamm.v2.Query/SpotPrice"]: QuerySpotPriceRequest => QuerySpotPriceResponse
}
}
如果宏生成的函数不足以满足你的需求,你可以手动编写函数。有关更多灵感,请参阅 模块目录。
版本控制
osmosis-test-tube 的版本由其依赖项、osmosis 和 test-tube 的版本以及其自身的更改决定。版本以 A.B.C 格式表示,其中
- A 是 osmosis 的主版本号,
- B 是 test-tube 的次版本号,
- C 是 osmosis-test-tube 自身的补丁号。
当 osmosis 发布新版本并包含破坏性更改时,如果 test-tube 也进行了破坏性更改,我们还将发布破坏性更改,并增加 osmosis-test-tube 的主版本号。这样,可以清楚地看出新版本的 osmosis-test-tube 与旧版本不兼容。
当向 osmosis-test-tube 添加向后兼容的新功能时,将增加次版本号。
当修复错误或进行其他更改,这些更改是 osmosis-test-tube
专有的且向后兼容时,将增加补丁号。
请查看升级指南以升级包,如果有破坏性更改
请注意,我们独立于依赖项的版本跟踪包的版本。
依赖项
~21–38MB
~629K SLoC