#cosmwasm #integration #testing #injective #chain #mocking #integration-tests

injective-test-tube

为Injective进行的CosmWasm集成测试

11个版本 (稳定)

1.13.0 2024年8月6日
1.2.1 2024年6月18日
1.1.8 2024年3月20日
1.1.6 2023年10月22日
1.1.4 2023年7月4日

#180 in 神奇豆子

Download history 17/week @ 2024-04-24 151/week @ 2024-05-01 7/week @ 2024-05-08 6/week @ 2024-05-15 16/week @ 2024-05-22 14/week @ 2024-05-29 9/week @ 2024-06-05 153/week @ 2024-06-12 87/week @ 2024-06-19 71/week @ 2024-06-26 105/week @ 2024-07-03 21/week @ 2024-07-10 69/week @ 2024-07-17 55/week @ 2024-07-24 133/week @ 2024-07-31 253/week @ 2024-08-07

每月下载量512次

MIT/Apache

160KB
4K SLoC

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

injective-test-tube

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

dev分支依赖于目前私有的仓库,但您可以使用已发布的版本。请参阅CHANGELOG以获取功能和更新信息。

目录

入门

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

以下是设置测试的步骤

use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;

// create new injective appchain instance.
let app = InjectiveTestApp::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, "inj"),
        ],
        2,
    )
    .unwrap();

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

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

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

use cosmwasm_std::Coin;
use injective_test_tube::InjectiveTestApp;

let app = InjectiveTestApp::new();

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

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

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

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

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

let app = InjectiveTestApp::new();
let accs = app
    .init_accounts(
        &[
            Coin::new(1_000_000_000_000u128, "usdt"),
            Coin::new(1_000_000_000_000u128, "inj"),
        ],
        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并找到您的 wasm 文件,在target/wasm32-unknown-unknown/release/<contract_name>.wasm中。

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

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

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

使用模块包装器

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

让我们尝试与Exchange模块交互

use cosmwasm_std::{Addr, Coin};
use injective_std::types::injective::exchange::v1beta1::{
    MarketStatus, MsgInstantSpotMarketLaunch,
    QuerySpotMarketsRequest, QuerySpotMarketsResponse, SpotMarket,
};
use injective_test_tube::{Account, Exchange, InjectiveTestApp};
use test_tube_inj::Module;

let app = InjectiveTestApp::new();
let signer = app
    .init_account(&[
        Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
        Coin::new(100_000_000_000_000_000_000u128, "usdt"),
    ])
    .unwrap();
let trader = app
    .init_account(&[
        Coin::new(10_000_000_000_000_000_000_000u128, "inj"),
        Coin::new(100_000_000_000_000_000_000u128, "usdt"),
    ])
    .unwrap();
let exchange = Exchange::new(&app);

exchange
    .instant_spot_market_launch(
        MsgInstantSpotMarketLaunch {
            sender: signer.address(),
            ticker: "INJ/USDT".to_owned(),
            base_denom: "inj".to_owned(),
            quote_denom: "usdt".to_owned(),
            min_price_tick_size: "10000".to_owned(),
            min_quantity_tick_size: "100000".to_owned(),
            min_notional: "1".to_owned(),
        },
        &signer,
    )
    .unwrap();

exchange
    .instant_spot_market_launch(
        MsgInstantSpotMarketLaunch {
            sender: signer.address(),
            ticker: "INJ/USDT".to_owned(),
            base_denom: "inj".to_owned(),
            quote_denom: "usdt".to_owned(),
            min_price_tick_size: "10000".to_owned(),
            min_quantity_tick_size: "100000".to_owned(),
            min_notional: "1".to_owned(),
        },
        &signer,
    )
    .unwrap_err();

app.increase_time(1u64);

let spot_markets = exchange
    .query_spot_markets(&QuerySpotMarketsRequest {
        status: "Active".to_owned(),
        market_ids: vec![],
    })
    .unwrap();

let expected_response = QuerySpotMarketsResponse {
    markets: vec![SpotMarket {
        ticker: "INJ/USDT".to_string(),
        base_denom: "inj".to_string(),
        quote_denom: "usdt".to_string(),
        maker_fee_rate: "-100000000000000".to_string(),
        taker_fee_rate: "1000000000000000".to_string(),
        relayer_fee_share_rate: "400000000000000000".to_string(),
        market_id: "0xd5a22be807011d5e42d5b77da3f417e22676efae494109cd01c242ad46630115"
            .to_string(),
        status: MarketStatus::Active.into(),
        min_price_tick_size: "10000".to_string(),
        min_quantity_tick_size: "100000".to_string(),
        min_notional: "1".to_string(),
        admin: "".to_string(),
        admin_permissions: 0u32,
    }],
};
assert_eq!(spot_markets, expected_response);

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

版本控制

injective-test-tube的版本由它所遵循的injective-core版本确定。对test-tube或injective-test-tube所做的更改将通过新的发布候选标记进行通知,例如1.13.2-rc1

依赖项

~22–38MB
~660K SLoC