Skip to content

Commit

Permalink
added basic suite tests for delegations
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Oct 17, 2024
1 parent 2fcbdfe commit a6ae9e7
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 6 deletions.
7 changes: 7 additions & 0 deletions contracts/delegation/dao-vote-delegation/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ pub fn instantiate(
)?;
VP_CAP_PERCENT.save(deps.storage, &msg.vp_cap_percent, env.block.height)?;

// initialize voting power changed hook callers
if let Some(vp_hook_callers) = msg.vp_hook_callers {
for caller in vp_hook_callers {
VOTING_POWER_HOOK_CALLERS.save(deps.storage, deps.api.addr_validate(&caller)?, &())?;
}
}

// sync proposal modules with no limit if not disabled. this should succeed
// for most DAOs as the query will not run out of gas with only a few
// proposal modules.
Expand Down
4 changes: 2 additions & 2 deletions contracts/delegation/dao-vote-delegation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod hooks;
pub mod msg;
pub mod state;

// #[cfg(test)]
// mod testing;
#[cfg(test)]
mod testing;

pub use crate::error::ContractError;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod tests;
174 changes: 174 additions & 0 deletions contracts/delegation/dao-vote-delegation/src/testing/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use cosmwasm_std::{Addr, Decimal, Empty, Uint128};
use cw_multi_test::{Contract, ContractWrapper};
use dao_testing::{DaoTestingSuite, DaoTestingSuiteBase, Executor, ADDR0, ADDR1, ADDR2};
use dao_voting::delegation::{DelegationResponse, DelegationsResponse};

pub fn dao_vote_delegation_contract() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
crate::contract::execute,
crate::contract::instantiate,
crate::contract::query,
)
.with_migrate(crate::contract::migrate);
Box::new(contract)
}

#[test]
fn test_setup() {
let mut base = DaoTestingSuiteBase::base();
let mut suite = base.cw4();
let dao = suite.dao();

let code_id = suite.base.app.store_code(dao_vote_delegation_contract());
let delegation_addr = suite
.base
.app
.instantiate_contract(
code_id,
dao.core_addr.clone(),
&crate::msg::InstantiateMsg {
dao: None,
vp_hook_callers: Some(vec![dao.x.group_addr.to_string()]),
no_sync_proposal_modules: None,
vp_cap_percent: Some(Decimal::percent(50)),
delegation_validity_blocks: Some(100),
},
&[],
"delegation",
None,
)
.unwrap();

// register addr0 as a delegate
suite
.base
.app
.execute_contract(
Addr::unchecked(ADDR0),
delegation_addr.clone(),
&crate::msg::ExecuteMsg::Register {},
&[],
)
.unwrap();

// delegate 100% of addr1's voting power to addr0
suite
.base
.app
.execute_contract(
Addr::unchecked(ADDR1),
delegation_addr.clone(),
&crate::msg::ExecuteMsg::Delegate {
delegate: ADDR0.to_string(),
percent: Decimal::percent(100),
},
&[],
)
.unwrap();

// delegations take effect on the next block
suite.base.advance_block();

let delegations: DelegationsResponse = suite
.querier()
.query_wasm_smart(
&delegation_addr,
&crate::msg::QueryMsg::Delegations {
delegator: ADDR1.to_string(),
height: None,
offset: None,
limit: None,
},
)
.unwrap();

assert_eq!(delegations.delegations.len(), 1);
assert_eq!(
delegations.delegations[0],
DelegationResponse {
delegate: Addr::unchecked(ADDR0),
percent: Decimal::percent(100),
active: true,
}
);

// propose a proposal
let (proposal_module, id1, p1) =
dao.propose_single_choice(&mut suite.base.app, ADDR0, "test proposal 1", vec![]);

// ensure delegation is correctly applied to proposal
let udvp: dao_voting::delegation::UnvotedDelegatedVotingPowerResponse = suite
.querier()
.query_wasm_smart(
&delegation_addr,
&crate::msg::QueryMsg::UnvotedDelegatedVotingPower {
delegate: ADDR0.to_string(),
proposal_module: proposal_module.to_string(),
proposal_id: id1,
height: p1.start_height,
},
)
.unwrap();
assert_eq!(
udvp.effective,
Uint128::from(suite.members[1].weight as u128)
);

// set delegation to 50%
suite
.base
.app
.execute_contract(
Addr::unchecked(ADDR1),
delegation_addr.clone(),
&crate::msg::ExecuteMsg::Delegate {
delegate: ADDR0.to_string(),
percent: Decimal::percent(50),
},
&[],
)
.unwrap();

// delegations take effect on the next block
suite.base.advance_block();

// propose a proposal
let (_, id2, p2) =
dao.propose_single_choice(&mut suite.base.app, ADDR2, "test proposal 2", vec![]);

// ensure delegation is correctly applied to new proposal
let udvp: dao_voting::delegation::UnvotedDelegatedVotingPowerResponse = suite
.querier()
.query_wasm_smart(
&delegation_addr,
&crate::msg::QueryMsg::UnvotedDelegatedVotingPower {
delegate: ADDR0.to_string(),
proposal_module: proposal_module.to_string(),
proposal_id: id2,
height: p2.start_height,
},
)
.unwrap();
assert_eq!(
udvp.effective,
Uint128::from((suite.members[1].weight / 2) as u128)
);

// ensure old delegation is still applied to old proposal
let udvp: dao_voting::delegation::UnvotedDelegatedVotingPowerResponse = suite
.querier()
.query_wasm_smart(
&delegation_addr,
&crate::msg::QueryMsg::UnvotedDelegatedVotingPower {
delegate: ADDR0.to_string(),
proposal_module: proposal_module.to_string(),
proposal_id: id1,
height: p1.start_height,
},
)
.unwrap();
assert_eq!(
udvp.effective,
Uint128::from(suite.members[1].weight as u128)
);
}
86 changes: 82 additions & 4 deletions packages/dao-testing/src/suite/base.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::{to_json_binary, Addr, Empty, QuerierWrapper, Timestamp};
use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, Empty, QuerierWrapper, Timestamp};
use cw20::Cw20Coin;
use cw_multi_test::{App, Executor};
use cw_utils::Duration;
Expand All @@ -10,10 +10,65 @@ use crate::contracts::*;
pub struct TestDao<Extra = Empty> {
pub core_addr: Addr,
pub voting_module_addr: Addr,
pub proposal_modules: Vec<dao_interface::state::ProposalModule>,
/// proposal modules in the form (pre-propose module, proposal module). if
/// the pre-propose module is None, then it does not exist.
pub proposal_modules: Vec<(Option<Addr>, Addr)>,
pub x: Extra,
}

impl<T> TestDao<T> {
/// propose a single choice proposal and return the proposal module address,
/// proposal ID, and proposal
pub fn propose_single_choice(
&self,
app: &mut App,
proposer: impl Into<String>,
title: impl Into<String>,
msgs: Vec<CosmosMsg>,
) -> (
Addr,
u64,
dao_proposal_single::proposal::SingleChoiceProposal,
) {
let pre_propose_msg = dao_pre_propose_single::ExecuteMsg::Propose {
msg: dao_pre_propose_single::ProposeMessage::Propose {
title: title.into(),
description: "".to_string(),
msgs,
vote: None,
},
};

let (pre_propose_module, proposal_module) = &self.proposal_modules[0];

app.execute_contract(
Addr::unchecked(proposer.into()),
pre_propose_module.as_ref().unwrap().clone(),
&pre_propose_msg,
&[],
)
.unwrap();

let proposal_id: u64 = app
.wrap()
.query_wasm_smart(
proposal_module.clone(),
&dao_proposal_single::msg::QueryMsg::ProposalCount {},
)
.unwrap();

let res: dao_proposal_single::query::ProposalResponse = app
.wrap()
.query_wasm_smart(
proposal_module.clone(),
&dao_proposal_single::msg::QueryMsg::Proposal { proposal_id },
)
.unwrap();

(proposal_module.clone(), proposal_id, res.proposal)
}
}

pub struct DaoTestingSuiteBase {
pub app: App,

Expand Down Expand Up @@ -97,6 +152,7 @@ pub trait DaoTestingSuite<Extra = Empty> {
},
close_proposal_on_execution_failure: true,
veto: None,
delegation_module: None,
})
.unwrap(),
admin: Some(dao_interface::state::Admin::CoreModule {}),
Expand Down Expand Up @@ -134,6 +190,7 @@ pub trait DaoTestingSuite<Extra = Empty> {
},
close_proposal_on_execution_failure: true,
veto: None,
delegation_module: None,
})
.unwrap(),
admin: Some(dao_interface::state::Admin::CoreModule {}),
Expand Down Expand Up @@ -300,7 +357,7 @@ impl DaoTestingSuiteBase {
let core = Addr::unchecked(instantiate_event.attributes[0].value.clone());

// get voting module address
let voting_module: Addr = self
let voting_module_addr: Addr = self
.app
.wrap()
.query_wasm_smart(&core, &dao_interface::msg::QueryMsg::VotingModule {})
Expand All @@ -319,9 +376,30 @@ impl DaoTestingSuiteBase {
)
.unwrap();

let proposal_modules = proposal_modules
.into_iter()
.map(|p| -> (Option<Addr>, Addr) {
let pre_propose_module: dao_voting::pre_propose::ProposalCreationPolicy = self
.app
.wrap()
.query_wasm_smart(
&p.address,
&dao_proposal_single::msg::QueryMsg::ProposalCreationPolicy {},
)
.unwrap();

match pre_propose_module {
dao_voting::pre_propose::ProposalCreationPolicy::Anyone {} => (None, p.address),
dao_voting::pre_propose::ProposalCreationPolicy::Module { addr } => {
(Some(addr), p.address)
}
}
})
.collect::<Vec<_>>();

TestDao {
core_addr: core,
voting_module_addr: voting_module,
voting_module_addr,
proposal_modules,
x: Empty::default(),
}
Expand Down

0 comments on commit a6ae9e7

Please sign in to comment.