diff --git a/packages/connection/Cargo.toml b/packages/connection/Cargo.toml index 4eaf5e0b..8a824658 100644 --- a/packages/connection/Cargo.toml +++ b/packages/connection/Cargo.toml @@ -30,6 +30,8 @@ serde.workspace = true serde-json-wasm.workspace = true thiserror.workspace = true cosmwasm-schema.workspace = true +rstest.workspace = true +ibcx-test-utils.workspace = true hpl-ownable.workspace = true hpl-interface.workspace = true diff --git a/packages/connection/src/lib.rs b/packages/connection/src/lib.rs index 91c7bdbd..ccb95dc0 100644 --- a/packages/connection/src/lib.rs +++ b/packages/connection/src/lib.rs @@ -49,22 +49,44 @@ pub fn handle( )) } SetIsm { ism } => { - let ism_addr = deps.api.addr_validate(&ism)?; - - ISM.save(deps.storage, &ism_addr)?; - - Ok(event_to_resp( - new_event("set_ism").add_attribute("ism", ism), - )) + match ism { + Some(ism) => { + let ism_addr = deps.api.addr_validate(&ism)?; + + ISM.save(deps.storage, &ism_addr)?; + + Ok(event_to_resp( + new_event("set_ism").add_attribute("ism", ism), + )) + } + None => { + ISM.remove(deps.storage); + + Ok(event_to_resp( + new_event("unset_ism") + )) + } + } } SetHook { hook } => { - let hook_addr = deps.api.addr_validate(&hook)?; - - HOOK.save(deps.storage, &hook_addr)?; - - Ok(event_to_resp( - new_event("set_hook").add_attribute("hook", hook), - )) + match hook { + Some(hook) => { + let hook_addr = deps.api.addr_validate(&hook)?; + + HOOK.save(deps.storage, &hook_addr)?; + + Ok(event_to_resp( + new_event("set_hook").add_attribute("hook", hook), + )) + } + None => { + HOOK.remove(deps.storage); + + Ok(event_to_resp( + new_event("unset_hook") + )) + } + } } } } @@ -98,3 +120,88 @@ pub fn get_ism(storage: &dyn Storage) -> StdResult> { pub fn get_hook(storage: &dyn Storage) -> StdResult> { HOOK.may_load(storage) } + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Addr, + }; + use ibcx_test_utils::addr; + use rstest::rstest; + + const OWNER: &str = "owner"; + const NOT_OWNER: &str = "not_owner"; + + #[rstest] + #[case(addr(OWNER), addr("new_ism_address"))] + #[should_panic(expected = "unauthorized")] + #[case(addr(NOT_OWNER), addr("new_ism_address"))] + fn test_set_and_unset_ism(#[case] sender: Addr, #[case] new_ism_addr: Addr) { + let mut deps = mock_dependencies(); + + hpl_ownable::initialize(deps.as_mut().storage, &Addr::unchecked(OWNER)).unwrap(); + + assert!(get_ism(&deps.storage).unwrap().is_none()); + + let msg = ConnectionMsg::SetIsm { + ism: Some(new_ism_addr.to_string()), + }; + let info = mock_info(sender.as_str(), &[]); + + let res = handle(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!( + res, + event_to_resp( + new_event("set_ism").add_attribute("ism", new_ism_addr.to_string()) + ) + ); + + let ism = get_ism(&deps.storage).unwrap().unwrap(); + assert_eq!(ism, new_ism_addr); + + let unset_msg = ConnectionMsg::SetIsm { ism: None }; + let unset_info = mock_info(sender.as_str(), &[]); + + handle(deps.as_mut(), mock_env(), unset_info, unset_msg).unwrap(); + + let ism = get_ism(&deps.storage).unwrap(); + assert!(ism.is_none()); + } + + #[rstest] + #[case(addr(OWNER), addr("new_hook_address"))] + #[should_panic(expected = "unauthorized")] + #[case(addr(NOT_OWNER), addr("new_hook_address"))] + fn test_set_and_unset_hook(#[case] sender: Addr, #[case] new_hook_addr: Addr) { + let mut deps = mock_dependencies(); + + hpl_ownable::initialize(deps.as_mut().storage, &Addr::unchecked(OWNER)).unwrap(); + assert!(get_hook(&deps.storage).unwrap().is_none()); + + let msg = ConnectionMsg::SetHook { + hook: Some(new_hook_addr.to_string()), + }; + let info = mock_info(sender.as_str(), &[]); + + let res = handle(deps.as_mut(), mock_env(), info, msg).unwrap(); + assert_eq!( + res, + event_to_resp( + new_event("set_hook").add_attribute("hook", new_hook_addr.to_string()) + ) + ); + + let hook = get_hook(&deps.storage).unwrap().unwrap(); + assert_eq!(hook, new_hook_addr); + + let unset_msg = ConnectionMsg::SetHook { hook: None }; + let unset_info = mock_info(sender.as_str(), &[]); + + handle(deps.as_mut(), mock_env(), unset_info, unset_msg).unwrap(); + + let hook = get_hook(&deps.storage).unwrap(); + assert!(hook.is_none()); + } +} diff --git a/packages/interface/src/connection.rs b/packages/interface/src/connection.rs index 7e655037..fdca8a9b 100644 --- a/packages/interface/src/connection.rs +++ b/packages/interface/src/connection.rs @@ -4,9 +4,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; pub enum ConnectionMsg { SetMailbox { mailbox: String }, - SetHook { hook: String }, + SetHook { hook: Option }, - SetIsm { ism: String }, + SetIsm { ism: Option }, } #[cw_serde]