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魔法豆

Download history 1017/week @ 2024-04-14 1102/week @ 2024-04-21 923/week @ 2024-04-28 760/week @ 2024-05-05 995/week @ 2024-05-12 727/week @ 2024-05-19 551/week @ 2024-05-26 1470/week @ 2024-06-02 944/week @ 2024-06-09 996/week @ 2024-06-16 1335/week @ 2024-06-23 763/week @ 2024-06-30 907/week @ 2024-07-07 1024/week @ 2024-07-14 1020/week @ 2024-07-21 737/week @ 2024-07-28

每月下载 3,705
17 仓库中使用 17 个(直接使用6个)

MIT/Apache

180KB
4.5K SLoC

Rust 3.5K SLoC // 0.0% comments Go 702 SLoC // 0.1% comments

osmosis-test-tube

osmosis-test-tube on crates.io Docs

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

目录

入门

为了演示osmosis-test-tube的工作原理,让我们使用一个简单的示例合约:来自cw-pluscw-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