Skip to content

Commit

Permalink
added more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrei Zavgorodnii committed Feb 27, 2025
1 parent 1c6d467 commit be6367b
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/Basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
target: wasm32-unknown-unknown
override: true

- name: Run unit tests
- name: Run unit testing
uses: actions-rs/cargo@v1
with:
command: unit-test
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ serde = { version = "1.0.197", default-features = false, features = ["derive"] }
thiserror = { version = "2.0.11" }

neutron-std = { git = "https://github.com/neutron-org/neutron-std", branch = "feat/respect-gogoproto-nullable" }
prost = "0.12.6"

[dev-dependencies]
cw-multi-test = "2.0.0"
62 changes: 1 addition & 61 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,66 +226,6 @@ pub fn validate_price(
}

/// Returns a string key for a currency pair.
fn pair_key(pair: &CurrencyPair) -> String {
pub(crate) fn pair_key(pair: &CurrencyPair) -> String {
format!("{}-{}", pair.base, pair.quote)
}

/// ------------------------------------------------------------------------------------------------
/// TESTS
/// ------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env};

#[test]
fn test_instantiate_success() {
// Set up a mock environment with default values
let mut deps = mock_dependencies();
let env = mock_env();
let info = message_info(&deps.api.addr_make("creator"), &[]);

// Create a sample InstantiateMsg
let instantiate_msg = InstantiateMsg {
owner: deps.api.addr_make("owner_addr").into_string(),
caller: deps.api.addr_make("caller_addr").into_string(),
pairs: vec![
CurrencyPair {
base: "untrn".to_string(),
quote: "usd".to_string(),
},
CurrencyPair {
base: "uatom".to_string(),
quote: "usd".to_string(),
},
],
update_period: 10,
max_blocks_old: 100,
history_size: 5,
};

// Call the instantiate function
instantiate(deps.as_mut(), env.clone(), info, instantiate_msg.clone())
.expect("contract initialization should succeed");

// Verify that the stored config matches the input data
let config = CONFIG.load(&deps.storage).expect("config must be saved");
assert_eq!(config.owner, deps.api.addr_make("owner_addr"));
assert_eq!(config.caller, deps.api.addr_make("caller_addr"));
assert_eq!(config.pairs, instantiate_msg.pairs);
assert_eq!(config.update_period, instantiate_msg.update_period);
assert_eq!(config.max_blocks_old, instantiate_msg.max_blocks_old);
assert_eq!(config.history_size, instantiate_msg.history_size);
assert_eq!(config.last_update, env.block.height);

// Verify that ring buffer pointers are initialized for each pair with 0
for pair in &instantiate_msg.pairs {
let key = pair_key(pair);
let idx = LAST_INDEX
.load(&deps.storage, &key)
.expect("pair index must be saved");
assert_eq!(idx, 0);
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ pub mod contract;
mod error;
pub mod msg;
pub mod state;
#[cfg(test)]
mod testing;

pub use crate::error::ContractError;
123 changes: 123 additions & 0 deletions src/testing/mock_querier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage};
use cosmwasm_std::{
from_json, Binary, ContractResult, Empty, GrpcQuery, OwnedDeps, Querier, QuerierResult,
QueryRequest, SystemError, SystemResult,
};
use neutron_std::types::slinky::oracle::v1::{GetPriceRequest, GetPriceResponse, QuotePrice};
use prost::Message;
use std::collections::HashMap;
use std::marker::PhantomData;

// A convenience struct to store mock price data for each pair.
#[derive(Clone, Debug)]
pub struct MockOraclePriceData {
pub price: Option<QuotePrice>,
pub nonce: u64,
pub decimals: u64,
pub id: u64,
}

impl Default for MockOraclePriceData {
fn default() -> Self {
Self {
price: None,
nonce: 0,
decimals: 0,
id: 0,
}
}
}

pub fn mock_dependencies() -> OwnedDeps<MockStorage, MockApi, WasmMockQuerier> {
let custom_storage = MockStorage::default();
let custom_querier = WasmMockQuerier::new(MockQuerier::new(&[]));

OwnedDeps {
storage: custom_storage,
api: MockApi::default(),
querier: custom_querier,
custom_query_type: PhantomData,
}
}

/// A custom mock querier that can store and return per-pair oracle data.
pub struct WasmMockQuerier {
base: MockQuerier,
// Map from "pair_key" => MockOraclePriceData
oracle_data: HashMap<String, MockOraclePriceData>,
}

impl WasmMockQuerier {
pub fn new(base: MockQuerier) -> Self {
WasmMockQuerier {
base,
oracle_data: HashMap::new(),
}
}

/// Allows tests to insert mock price data for a given pair key
pub fn update_oracle_data(&mut self, key: &str, data: MockOraclePriceData) {
self.oracle_data.insert(key.to_string(), data);
}

fn handle_grpc_query(&self, path: &str, data: &[u8]) -> SystemResult<ContractResult<Binary>> {
match path {
GetPriceRequest::TYPE_URL => {
// Decode the request so we can check which pair is queried
let req = match GetPriceRequest::decode(data) {
Ok(r) => r,
Err(e) => {
return SystemResult::Err(SystemError::InvalidRequest {
error: format!("Failed to decode GetPriceRequest: {}", e),
request: data.into(),
});
}
};

// The request should contain an optional currency pair
if let Some(pair) = req.currency_pair {
let key = format!("{}-{}", pair.base, pair.quote);
// Lookup mock data from the HashMap
let mock_data = self.oracle_data.get(&key).cloned().unwrap_or_default();

let resp = GetPriceResponse {
price: mock_data.price,
nonce: mock_data.nonce,
decimals: mock_data.decimals,
id: mock_data.id,
};
SystemResult::Ok(ContractResult::Ok(Binary::from(resp.to_proto_bytes())))
} else {
// If no pair is provided, return default empty
let resp = GetPriceResponse::default();
SystemResult::Ok(ContractResult::Ok(Binary::from(resp.to_proto_bytes())))
}
}
_ => SystemResult::Err(SystemError::UnsupportedRequest {
kind: format!("Unhandled path in mock_querier: {}", path),
}),
}
}

pub fn handle_query(&self, request: &QueryRequest<Empty>) -> QuerierResult {
match &request {
QueryRequest::Grpc(GrpcQuery { data, path }) => self.handle_grpc_query(path, data),
_ => self.base.handle_query(request),
}
}
}

impl Querier for WasmMockQuerier {
fn raw_query(&self, bin_request: &[u8]) -> QuerierResult {
let request: QueryRequest<Empty> = match from_json(bin_request) {
Ok(v) => v,
Err(e) => {
return QuerierResult::Err(SystemError::InvalidRequest {
error: format!("Parsing query request: {}", e),
request: bin_request.into(),
});
}
};
self.handle_query(&request)
}
}
2 changes: 2 additions & 0 deletions src/testing/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod mock_querier;
mod tests;
Loading

0 comments on commit be6367b

Please sign in to comment.