#cosmwasm #integration #neutron #testing #mocking #integration-tests #tube

neutron-test-tube

Neutron 的 CosmWasm 集成测试

6 个版本 (3 个稳定版)

4.2.0 2024 年 8 月 8 日
4.2.0-1 2024 年 8 月 12 日
4.1.0 2024 年 7 月 31 日
4.0.1 2024 年 7 月 21 日
4.0.1-rc22024 年 7 月 26 日

#436 in 魔法豆

Download history 232/week @ 2024-07-20 212/week @ 2024-07-27 109/week @ 2024-08-03 113/week @ 2024-08-10

666 每月下载量

MIT/Apache

140KB
3K SLoC

Rust 2.5K SLoC // 0.0% comments Go 599 SLoC // 0.1% comments

neutron-test-tube

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

请参阅 CHANGELOG 了解功能和更新信息。

目录

入门

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

以下是设置测试的步骤

use cosmwasm_std::Coin;
use neutron_test_tube::NeutronTestApp;

// create new neutron appchain instance.
let app = NeutronTestApp::new();

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

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

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

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

use cosmwasm_std::Coin;
use neutron_test_tube::NeutronTestApp;

let app = NeutronTestApp::new();

let account = app.init_account(&[
    Coin::new(1_000_000_000_000u128, "usdt"),
    Coin::new(1_000_000_000_000u128, "untrn"),
]);

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

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

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

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

let app = NeutronTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000u128, "usdt"),
            Coin::new(1_000_000_000_000u128, "untrn"),
        ],
        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 neutron_test_tube::{Account, Module, NeutronTestApp, Wasm};

let app = NeutronTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000u128, "usdt"),
            Coin::new(1_000_000_000_000u128, "untrn"),
        ],
        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("Test label"), // 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 neutron_test_tube::{Account, Module, NeutronTestApp, Wasm};

let app = NeutronTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000u128, "usdt"),
            Coin::new(1_000_000_000_000u128, "untrn"),
        ],
        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("Test label"), // 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 禁用了此功能,但 NeutronTestApp 允许 stdout 输出,这样您可以在运行测试时调试您的智能合约。

使用模块包装器

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

让我们尝试与 Exchange 模块交互

use cosmwasm_std::Coin;
use margined_neutron_std::shim::Any;
use margined_neutron_std::types::{
    cosmos::bank::v1beta1::{MsgSend, QueryBalanceRequest, SendAuthorization},
    cosmos::base::v1beta1::Coin as BaseCoin,
    neutron::dex as DexTypes,
};
use prost::Message;

use neutron_test_tube::{Account, Bank, Dex, NeutronTestApp};
use test_tube_ntrn::Module;

let app = NeutronTestApp::new();
let signer = app
    .init_account(&[
        Coin::new(1_000_000_000_000_000_000_000_000u128, "untrn"),
        Coin::new(1_000_000_000_000u128, "usdc"),
    ])
    .unwrap();
let receiver = app
    .init_account(&[Coin::new(1_000_000_000_000u128, "untrn")])
    .unwrap();
let dex = Dex::new(&app);
let bank = Bank::new(&app);

let scale_factor = 1_000_000_000_000_000_000u128;

let res = dex
    .place_limit_order(
        DexTypes::MsgPlaceLimitOrder {
            creator: signer.address().clone(),
            receiver: signer.address().clone(),
            token_in: "untrn".to_string(),
            token_out: "usdc".to_string(),
            tick_index_in_to_out: 0,
            amount_in: (10_000_000_000_000_000_00u128).to_string(),
            order_type: 0,
            expiration_time: None,
            max_amount_out: "".to_string(),
            limit_sell_price: (10u128 * scale_factor).to_string(),
        },
        &signer,
    )
    .unwrap();

let res = dex
    .tick_liquidity_all(&DexTypes::QueryAllTickLiquidityRequest {
        pair_id: "untrn<>usdc".to_string(),
        token_in: "untrn".to_string(),
        pagination: None,
    })
    .unwrap();

app.increase_time(1u64);

更多示例可以在 模块 目录中找到。

版本控制

neutron-test-tube 的版本遵循 Neutron 主网发布的版本。

依赖项

~25–45MB
~761K SLoC