Files
nym/common/cosmwasm-smart-contracts/contracts-common-testing/src/helpers.rs
T
Jędrzej Stuczyński 827c13b69e moved nym-gateway-probe to monorepo and updated rust-edition to 2024 (#6094)
dont build netstack in CI

additional rust 2024 fixes

fixes

removed temp.rs

first round of cleanup

removed duplicated NS types

moved gateway probe to the monorepo
2025-10-08 16:17:43 +01:00

208 lines
5.6 KiB
Rust

// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, message_info};
use cosmwasm_std::{
Addr, BankMsg, CosmosMsg, Decimal, Empty, Env, MemoryStorage, MessageInfo, Order, OwnedDeps,
Response, StdResult, Storage, coins,
};
use cw_storage_plus::{KeyDeserialize, Map, Prefix, PrimaryKey};
use nym_contracts_common::events::may_find_attribute;
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
use std::str::FromStr;
pub const TEST_DENOM: &str = "unym";
pub const TEST_PREFIX: &str = "n";
pub trait FindAttribute {
fn attribute<E, S>(&self, event_type: E, attribute: &str) -> String
where
E: Into<Option<S>>,
S: Into<String>;
fn any_attribute(&self, attribute: &str) -> String {
self.attribute::<_, String>(None, attribute)
}
fn any_parsed_attribute<T>(&self, attribute: &str) -> T
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.parsed_attribute::<_, String, T>(None, attribute)
}
fn parsed_attribute<E, S, T>(&self, event_type: E, attribute: &str) -> T
where
E: Into<Option<S>>,
S: Into<String>,
T: FromStr,
<T as FromStr>::Err: Debug;
fn decimal<E, S>(&self, event_type: E, attribute: &str) -> Decimal
where
E: Into<Option<S>>,
S: Into<String>,
{
self.parsed_attribute(event_type, attribute)
}
}
#[track_caller]
pub fn find_attribute<S: Into<String>>(
event_type: Option<S>,
attribute: &str,
response: &Response,
) -> String {
let event_type = event_type.map(Into::into);
for event in &response.events {
if let Some(typ) = &event_type {
if &event.ty != typ {
continue;
}
}
if let Some(attr) = may_find_attribute(event, attribute) {
return attr;
}
}
// this is only used in tests so panic here is fine
panic!("did not find the attribute")
}
impl FindAttribute for Response {
fn attribute<E, S>(&self, event_type: E, attribute: &str) -> String
where
E: Into<Option<S>>,
S: Into<String>,
{
find_attribute(event_type.into(), attribute, self)
}
fn parsed_attribute<E, S, T>(&self, event_type: E, attribute: &str) -> T
where
E: Into<Option<S>>,
S: Into<String>,
T: FromStr,
<T as FromStr>::Err: Debug,
{
find_attribute(event_type.into(), attribute, self)
.parse()
.unwrap()
}
}
pub fn mock_api() -> MockApi {
MockApi::default().with_prefix(TEST_PREFIX)
}
pub fn mock_dependencies() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
OwnedDeps {
storage: MockStorage::default(),
api: mock_api(),
querier: MockQuerier::default(),
custom_query_type: Default::default(),
}
}
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
rand_chacha::ChaCha20Rng::from_seed(dummy_seed)
}
pub fn deps_with_balance(env: &Env) -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
deps.querier = MockQuerier::<Empty>::new(&[(
env.contract.address.as_str(),
coins(100000000000, TEST_DENOM).as_slice(),
)]);
deps
}
pub fn generate_sorted_addresses(n: usize) -> Vec<Addr> {
let mut rng = test_rng();
let mut addrs = Vec::with_capacity(n);
for i in 0..n {
addrs.push(mock_api().addr_make(&format!("addr{i}{}", rng.next_u64())));
}
addrs.sort();
addrs
}
pub fn addr<S: AsRef<str>>(raw: S) -> Addr {
mock_api().addr_make(raw.as_ref())
}
pub fn sender<S: AsRef<str>>(raw: S) -> MessageInfo {
message_info(&addr(raw), &[])
}
pub trait ExtractBankMsg {
fn unwrap_bank_msg(self) -> Option<BankMsg>;
}
impl ExtractBankMsg for Response {
fn unwrap_bank_msg(self) -> Option<BankMsg> {
for msg in self.messages {
match msg.msg {
CosmosMsg::Bank(bank_msg) => return Some(bank_msg),
_ => continue,
}
}
None
}
}
pub trait FullReader<'a> {
type Key;
type Value: Serialize + DeserializeOwned;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>>;
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>>;
}
impl<'a, K, T> FullReader<'a> for Map<K, T>
where
T: Serialize + DeserializeOwned,
K: PrimaryKey<'a> + KeyDeserialize,
K::Output: 'static,
{
type Key = K::Output;
type Value = T;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>> {
self.range(store, None, None, Order::Ascending)
.map(|record| record.map(|r| r.1))
.collect()
}
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>> {
self.range(store, None, None, Order::Ascending).collect()
}
}
impl<'a, K, T, B> FullReader<'a> for Prefix<K, T, B>
where
K: KeyDeserialize + 'static,
T: Serialize + DeserializeOwned,
B: PrimaryKey<'a>,
{
type Key = K::Output;
type Value = T;
fn all_values(&self, store: &dyn Storage) -> StdResult<Vec<Self::Value>> {
self.range(store, None, None, Order::Ascending)
.map(|record| record.map(|r| r.1))
.collect()
}
fn all_key_values(&self, store: &dyn Storage) -> StdResult<Vec<(Self::Key, Self::Value)>> {
self.range(store, None, None, Order::Ascending).collect()
}
}