1 个不稳定版本
0.1.0-rc1 | 2024年4月29日 |
---|
#7 in #defi
145KB
3K SLoC
provwasm-test-tube
一个 CosmWasm x ProvWasm 集成测试库,与 cw-multi-test
不同,它允许您将您的 ProvWasm 合同与真实链的逻辑进行测试,而不是使用模拟。
目录
兼容性
provwasm-test-tube | provwasm | provenance |
---|---|---|
0.1.0 | 2.2.0 | 1.18.0 |
入门指南
为了演示 provwasm-test-tube
的工作原理,让我们使用一个简单的示例合同: marker。
以下是设置测试的方法
use cosmwasm_std::Coin;
use provwasm_test_tube::{ProvwasmTestApp, RunnerError};
#[test]
fn test() -> Result<(), RunnerError> {
// create new provenance appchain instance.
let app = ProvwasmTestApp::new();
// create new account with initial funds
let accs = app.init_accounts(&[Coin::new(1_000_000_000_000, "nhash")], 2)?;
let admin = &accs[0];
let new_admin = &accs[1];
Ok(())
}
现在我们有了 appchain 实例和账户,它们具有一些初始余额,并且可以与 appchain 交互。这不需要运行 Docker 实例或外部进程,它只是将 appchain 的代码作为库加载,并创建一个内存实例。
请注意,init_accounts
是一个便利函数,用于创建具有相同初始余额的多个账户。如果您只想创建一个账户,可以使用 init_account
代替。
use cosmwasm_std::Coin;
use provwasm_test_tube::{ProvwasmTestApp, RunnerError};
fn test() -> Result<(), RunnerError> {
let app = ProvwasmTestApp::new();
let account = app.init_account(&[Coin::new(1_000_000_000_000, "nhash")])?;
Ok(())
}
现在,如果我们想测试一个 provwasm 合同,我们需要
- 构建 wasm 文件
- 存储代码
- 实例化
然后我们可以开始与我们的合同交互
use cosmwasm_std::Coin;
use provwasm_test_tube::wasm::Wasm;
use provwasm_test_tube::{Module, ProvwasmTestApp, RunnerError};
fn test() -> Result<(), RunnerError> {
let app = ProvwasmTestApp::default();
let accs = app.init_accounts(&[Coin::new(100_000_000_000_000, "nhash")], 1)?;
let admin = &accs[0];
let wasm = Wasm::new(&app);
let wasm_byte_code = std::fs::read("../contracts/marker/artifacts/marker.wasm").unwrap();
let store_res = wasm.store_code(&wasm_byte_code, None, admin);
let code_id = store_res?.data.code_id;
Ok(())
}
现在让我们执行合同并验证合同的状态是否已正确更新。
use cosmwasm_std::{Coin, Uint128};
use provwasm_test_tube::wasm::Wasm;
use provwasm_test_tube::{Account, Module, ProvwasmTestApp, RunnerError};
use marker::msg::{ExecuteMsg, InitMsg, QueryMsg};
use marker::types::Marker;
#[test]
fn create_and_withdraw() -> Result<(), RunnerError> {
let app = ProvwasmTestApp::default();
let accs = app.init_accounts(&[Coin::new(100_000_000_000_000, "nhash")], 1)?;
let admin = &accs[0];
let wasm = Wasm::new(&app);
let wasm_byte_code = std::fs::read("../contracts/marker/artifacts/marker.wasm").unwrap();
let store_res = wasm.store_code(&wasm_byte_code, None, admin);
let code_id = store_res?.data.code_id;
assert_eq!(code_id, 1);
// let init_admins = vec![admin.address()];
let contract_addr = wasm
.instantiate(
code_id,
&InitMsg {
name: "marker-test.sc.pb".to_string(),
},
Some(&admin.address()),
None,
&[],
admin,
)?
.data
.address;
wasm.execute::<ExecuteMsg>(
&contract_addr,
&ExecuteMsg::Create {
supply: Uint128::new(100),
denom: "spy".into(),
allow_forced_transfer: false,
},
&[],
admin,
)?;
wasm.execute::<ExecuteMsg>(
&contract_addr,
&ExecuteMsg::GrantAccess {
denom: "spy".into(),
},
&[],
admin,
)?;
wasm.execute::<ExecuteMsg>(
&contract_addr,
&ExecuteMsg::Finalize {
denom: "spy".into(),
},
&[],
admin,
)?;
wasm.execute::<ExecuteMsg>(
&contract_addr,
&ExecuteMsg::Activate {
denom: "spy".into(),
},
&[],
admin,
)?;
wasm.execute::<ExecuteMsg>(
&contract_addr,
&ExecuteMsg::Withdraw {
amount: Uint128::new(20),
denom: "spy".into(),
},
&[],
admin,
)?;
let marker = wasm.query::<QueryMsg, Marker>(
&contract_addr,
&QueryMsg::GetByDenom {
denom: "spy".into(),
},
)?;
assert_eq!(marker.marker_account.denom, "spy");
Ok(())
}
调试
在您的合同代码中,如果您想调试,可以使用 deps.api.debug(..)
,它将调试消息打印到 stdout。 wasmd
默认禁用了此功能,但 ProvwasmTestApp
允许 stdout 输出,以便您可以在运行测试的同时调试智能合约。
使用模块包装器
在某些情况下,您可能需要直接与 appchain 逻辑交互以设置环境或查询 appchain 的状态。模块包装器提供了方便的函数来与 appchain 的模块交互。
让我们尝试与 Marker
模块交互
use std::convert::TryFrom;
use cosmwasm_std::{Coin, Uint128};
use provwasm_test_tube::{Account, Module, ProvwasmTestApp, RunnerError};
use provwasm_std::types::provenance::marker::v1::{
Access, AccessGrant, MarkerAccount, MarkerStatus, MarkerType, MsgAddMarkerRequest,
QueryMarkerRequest,
};
#[test]
fn create_and_withdraw() -> Result<(), RunnerError> {
let app = ProvwasmTestApp::default();
let accs = app.init_accounts(&[Coin::new(100_000_000_000_000, "nhash")], 1)?;
let admin = &accs[0];
let marker_module = provwasm_test_tube::marker::Marker::new(&app);
marker_module.add_marker(
MsgAddMarkerRequest {
amount: Some(
Coin {
amount: Uint128::new(100),
denom: "spy".to_string(),
}
.into(),
),
manager: admin.address(),
from_address: admin.address(),
status: MarkerStatus::Proposed.into(),
marker_type: MarkerType::Coin.into(),
access_list: vec![AccessGrant {
address: admin.address(),
permissions: vec![
Access::Admin.into(),
Access::Burn.into(),
Access::Deposit.into(),
Access::Delete.into(),
Access::Mint.into(),
Access::Withdraw.into(),
],
}],
supply_fixed: false,
allow_governance_control: false,
allow_forced_transfer: false,
required_attributes: vec![],
usd_cents: 0,
volume: 0,
usd_mills: 0,
},
admin,
)?;
let marker_response = marker_module.query_marker(&QueryMarkerRequest {
id: "spy".to_string(),
})?;
assert_eq!(
MarkerAccount::try_from(marker_response.marker.unwrap())
.unwrap()
.denom,
"spy"
);
Ok(())
}
自定义模块包装器
您可能找不到想要使用的包装器,或者提供的包装器过于冗长。好消息是,创建自己的包装器非常简单。
以下是作为库用户如何重新定义Hold
模块包装器的方法
use provwasm_test_tube::{fn_execute, fn_query, Module, Runner};
use provwasm_std::types::provenance::hold::v1::{
AccountHold, GetAllHoldsRequest, GetAllHoldsResponse, GetHoldsRequest, GetHoldsResponse,
};
pub struct Hold<'a, R: Runner<'a>> {
runner: &'a R,
}
impl<'a, R: Runner<'a>> Module<'a, R> for Hold<'a, R> {
fn new(runner: &'a R) -> Self {
Self { runner }
}
}
impl<'a, R> Hold<'a, R>
where
R: Runner<'a>,
{
fn_execute! {
pub account_hold: AccountHold["/provenance.hold.v1.AccountHold"] => ()
}
fn_query! {
pub query_get_holds ["/provenance.hold.v1.Query/GetHolds"]: GetHoldsRequest => GetHoldsResponse
}
fn_query! {
pub query_get_all_holds ["/provenance.hold.v1.Query/GetAllHolds"]: GetAllHoldsRequest => GetAllHoldsResponse
}
}
如果生成的宏函数不够好,您可以手动编写自己的函数。请参阅模块目录以获取更多灵感。
依赖项
~31–47MB
~1M SLoC