From 3c9d30aae96fa08fd26c0b8bc2026f9336d9b463 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Tue, 21 Mar 2023 13:39:05 +0100 Subject: [PATCH] test(law): add test for store_reply --- Cargo.lock | 1 + Cargo.toml | 1 + contracts/cw-law-stone/Cargo.toml | 1 + contracts/cw-law-stone/src/contract.rs | 185 ++++++++++++++++++++++++- contracts/cw-law-stone/src/error.rs | 4 + 5 files changed, 186 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aba1fa50..34c9abe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,7 @@ dependencies = [ "cw-multi-test", "cw-storage", "cw-storage-plus", + "cw-utils", "cw2", "logic-bindings", "schemars", diff --git a/Cargo.toml b/Cargo.toml index c6658315..f6e69258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ cosmwasm-std = "1.2.2" cosmwasm-storage = "1.2.2" cw-multi-test = "0.15.1" cw-storage-plus = "0.15.1" +cw-utils = "1.0.1" cw2 = "0.15.1" schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } diff --git a/contracts/cw-law-stone/Cargo.toml b/contracts/cw-law-stone/Cargo.toml index d79ed6a4..8307f567 100644 --- a/contracts/cw-law-stone/Cargo.toml +++ b/contracts/cw-law-stone/Cargo.toml @@ -32,6 +32,7 @@ cosmwasm-std.workspace = true cosmwasm-storage.workspace = true cw-storage = { path = "../cw-storage" } cw-storage-plus.workspace = true +cw-utils.worksapce = true cw2.workspace = true logic-bindings = { version = "0.2", path = "../../packages/logic-bindings" } schemars.workspace = true diff --git a/contracts/cw-law-stone/src/contract.rs b/contracts/cw-law-stone/src/contract.rs index 05c54354..041a00f8 100644 --- a/contracts/cw-law-stone/src/contract.rs +++ b/contracts/cw-law-stone/src/contract.rs @@ -2,11 +2,12 @@ use crate::ContractError::NotImplemented; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, SubMsg, - WasmMsg, + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, + SubMsg, WasmMsg, }; use cw2::set_contract_version; -use cw_storage::msg::ExecuteMsg as StorageMsg; +use cw_storage::msg::{ExecuteMsg as StorageMsg, ObjectResponse}; +use cw_utils::parse_reply_execute_data; use logic_bindings::LogicCustomQuery; use crate::error::ContractError; @@ -59,11 +60,40 @@ pub fn query(_deps: Deps<'_>, _env: Env, _msg: QueryMsg) -> StdResult { Err(StdError::generic_err("Not implemented")) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply( + deps: DepsMut<'_, LogicCustomQuery>, + env: Env, + msg: Reply, +) -> Result { + match msg.id { + STORE_PROGRAM_REPLY_ID => reply::store_program_reply(deps, env, msg), + _ => Err(StdError::generic_err("Not implemented").into()), + } +} + +pub mod reply { + use super::*; + + pub fn store_program_reply( + _deps: DepsMut<'_, LogicCustomQuery>, + _env: Env, + msg: Reply, + ) -> Result { + let _ = parse_reply_execute_data(msg)?; + Err(StdError::generic_err("Not implemented").into()) + } +} + #[cfg(test)] mod tests { use super::*; + use crate::state::{DEPENDENCIES, PROGRAM}; use cosmwasm_std::testing::{mock_env, mock_info, MockQuerierCustomHandlerResult}; - use cosmwasm_std::{from_binary, to_binary, CosmosMsg, SystemError, SystemResult}; + use cosmwasm_std::{ + from_binary, to_binary, CosmosMsg, Order, SubMsgResponse, SubMsgResult, SystemError, + SystemResult, + }; use logic_bindings::testing::mock::mock_dependencies_with_logic_handler; use logic_bindings::{ Answer, AskResponse, LogicCustomQuery, Result as LogicResult, Substitution, Term, @@ -108,7 +138,7 @@ mod tests { #[test] fn proper_initialization() { let mut deps = mock_dependencies_with_logic_handler(|request| { - custom_logic_handler_with_dependencies(vec!["file1".to_string()], request) + custom_logic_handler_with_dependencies(vec![], request) }); let program = to_binary("foo(_) :- true.").unwrap(); @@ -132,7 +162,7 @@ mod tests { match result { StorageMsg::StoreObject { data, pin } => { assert_eq!(data, program); - assert!(pin); + assert!(pin, "the main program should be pinned"); } _ => assert!(false, "storage message should be a StoreObject message"), } @@ -142,4 +172,147 @@ mod tests { _ => assert!(false, "cosmos sub message should be a Wasm message execute"), } } + + struct StoreTestCase { + dependencies: Vec<(String, String, String)>, // URI, contract address, object id + object_id: String, + } + + #[test] + fn store_program_reply() { + let cases = vec![ + StoreTestCase { + dependencies: vec![ + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), + "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string() + ), + ], + object_id: "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271" + .to_string(), + }, + StoreTestCase { + dependencies: vec![], + object_id: "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05" + .to_string(), + }, + StoreTestCase { + dependencies: vec![ + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%224cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), // contract addr + "4cbe36399aabfcc7158ee7a66cbfffa525bb0ceab33d1ff2cff08759fe0a9b05".to_string() // object id + ), + ( + "cosmwasm:okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s?query=%7B%22object_data%22%3A%7B%22id%22%3A%20%220689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271%22%7D%7D".to_string(), + "okp41dclchlcttf2uektxyryg0c6yau63eml5q9uq03myg44ml8cxpxnqavca4s".to_string(), // contract addr + "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271".to_string() // object id + ), + ], + object_id: "0689c526187c6785dfcce28f8df19138da292598dc19548a852de1792062f271" + .to_string(), + }, + ]; + + for case in cases { + let uris = Box::new( + case.dependencies + .clone() + .into_iter() + .map(|(uri, _, _)| uri) + .collect::>(), + ); + let mut deps = mock_dependencies_with_logic_handler(move |request| { + custom_logic_handler_with_dependencies(uris.to_vec(), request) + }); + + let object = ObjectResponse { + id: case.object_id.clone(), + owner: "creator".to_string(), + is_pinned: true, + size: Default::default(), + }; + let reply = Reply { + id: STORE_PROGRAM_REPLY_ID, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some(to_binary(&object).unwrap()), + }), + }; + let res = reply::store_program_reply(deps.as_mut(), mock_env(), reply).unwrap(); + + let program = PROGRAM.load(&deps.storage).unwrap(); + assert_eq!(case.object_id.clone(), program.object_id); + + let deps_len_requirement = case.dependencies.len(); + + if deps_len_requirement > 0 { + assert_eq!( + deps_len_requirement, + DEPENDENCIES + .keys_raw(&deps.storage, None, None, Order::Ascending) + .count() + ); + for (_, contract_addr, object_id) in case.dependencies { + let o = DEPENDENCIES.load(&deps.storage, object_id.as_str()); + assert!( + o.is_ok(), + "dependencies should contains each object id dependencies as key" + ); + let o = o.unwrap(); + assert_eq!( + o.object_id, object_id, + "dependencies should contains each object id dependencies as key" + ); + assert_eq!( + o.storage_address, contract_addr, + "dependencies should contains each object id dependencies as key" + ); + } + } + + assert_eq!( + deps_len_requirement, + res.messages.len(), + "response should contains any sub message as dependencies" + ); + + let objects_pinned: Vec = res + .messages + .into_iter() + .flat_map(|sub_msg| -> Option { + match &sub_msg.msg { + CosmosMsg::Wasm(wasm_msg) => match wasm_msg { + WasmMsg::Execute { msg, .. } => { + let result: StorageMsg = from_binary(msg).unwrap(); + match result { + StorageMsg::PinObject { id } => Some(id), + _ => { + assert!(false, "should contains only PinObject message(s)"); + None + } + } + } + _ => { + assert!(false, "wasm message should be a Storage message"); + None + } + }, + _ => { + assert!(false, "cosmos sub message should be a Wasm message execute"); + None + } + } + }) + .collect(); + + for object in objects_pinned { + assert!( + DEPENDENCIES.has(&deps.storage, object.as_str()), + "each dependencies should be pinned by a PinObject message" + ) + } + } + } } diff --git a/contracts/cw-law-stone/src/error.rs b/contracts/cw-law-stone/src/error.rs index 1f8c2bd4..80abc91e 100644 --- a/contracts/cw-law-stone/src/error.rs +++ b/contracts/cw-law-stone/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::StdError; +use cw_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug)] @@ -8,4 +9,7 @@ pub enum ContractError { #[error("Not implemented")] NotImplemented {}, + + #[error("{0}")] + Parse(#[from] ParseReplyError), }