mod.rs 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. pub(crate) mod mock;
  2. use crate::*;
  3. use mock::*;
  4. use codec::Encode;
  5. use rstd::rc::Rc;
  6. use sr_primitives::traits::{DispatchResult, OnFinalize, OnInitialize};
  7. use srml_support::{StorageDoubleMap, StorageMap, StorageValue};
  8. use system::RawOrigin;
  9. use system::{EventRecord, Phase};
  10. use srml_support::traits::Currency;
  11. pub(crate) fn increase_total_balance_issuance_using_account_id(account_id: u64, balance: u64) {
  12. let initial_balance = Balances::total_issuance();
  13. {
  14. let _ = <Test as stake::Trait>::Currency::deposit_creating(&account_id, balance);
  15. }
  16. assert_eq!(Balances::total_issuance(), initial_balance + balance);
  17. }
  18. struct ProposalParametersFixture {
  19. parameters: ProposalParameters<u64, u64>,
  20. }
  21. impl ProposalParametersFixture {
  22. fn with_required_stake(&self, required_stake: BalanceOf<Test>) -> Self {
  23. ProposalParametersFixture {
  24. parameters: ProposalParameters {
  25. required_stake: Some(required_stake),
  26. ..self.parameters
  27. },
  28. }
  29. }
  30. fn with_grace_period(&self, grace_period: u64) -> Self {
  31. ProposalParametersFixture {
  32. parameters: ProposalParameters {
  33. grace_period,
  34. ..self.parameters
  35. },
  36. }
  37. }
  38. fn params(&self) -> ProposalParameters<u64, u64> {
  39. self.parameters.clone()
  40. }
  41. }
  42. impl Default for ProposalParametersFixture {
  43. fn default() -> Self {
  44. ProposalParametersFixture {
  45. parameters: ProposalParameters {
  46. voting_period: 3,
  47. approval_quorum_percentage: 60,
  48. approval_threshold_percentage: 60,
  49. slashing_quorum_percentage: 60,
  50. slashing_threshold_percentage: 60,
  51. grace_period: 0,
  52. required_stake: None,
  53. },
  54. }
  55. }
  56. }
  57. #[derive(Clone)]
  58. struct DummyProposalFixture {
  59. parameters: ProposalParameters<u64, u64>,
  60. account_id: u64,
  61. proposer_id: u64,
  62. proposal_code: Vec<u8>,
  63. title: Vec<u8>,
  64. description: Vec<u8>,
  65. stake_balance: Option<BalanceOf<Test>>,
  66. }
  67. impl Default for DummyProposalFixture {
  68. fn default() -> Self {
  69. let title = b"title".to_vec();
  70. let description = b"description".to_vec();
  71. let dummy_proposal =
  72. mock::proposals::Call::<Test>::dummy_proposal(title.clone(), description.clone());
  73. DummyProposalFixture {
  74. parameters: ProposalParameters {
  75. voting_period: 3,
  76. approval_quorum_percentage: 60,
  77. approval_threshold_percentage: 60,
  78. slashing_quorum_percentage: 60,
  79. slashing_threshold_percentage: 60,
  80. grace_period: 0,
  81. required_stake: None,
  82. },
  83. account_id: 1,
  84. proposer_id: 1,
  85. proposal_code: dummy_proposal.encode(),
  86. title,
  87. description,
  88. stake_balance: None,
  89. }
  90. }
  91. }
  92. impl DummyProposalFixture {
  93. fn with_title_and_body(self, title: Vec<u8>, description: Vec<u8>) -> Self {
  94. DummyProposalFixture {
  95. title,
  96. description,
  97. ..self
  98. }
  99. }
  100. fn with_parameters(self, parameters: ProposalParameters<u64, u64>) -> Self {
  101. DummyProposalFixture { parameters, ..self }
  102. }
  103. fn with_account_id(self, account_id: u64) -> Self {
  104. DummyProposalFixture { account_id, ..self }
  105. }
  106. fn with_stake(self, stake_balance: BalanceOf<Test>) -> Self {
  107. DummyProposalFixture {
  108. stake_balance: Some(stake_balance),
  109. ..self
  110. }
  111. }
  112. fn with_proposal_code(self, proposal_code: Vec<u8>) -> Self {
  113. DummyProposalFixture {
  114. proposal_code,
  115. ..self
  116. }
  117. }
  118. fn create_proposal_and_assert(self, result: Result<u32, Error>) -> Option<u32> {
  119. let proposal_id_result = ProposalsEngine::create_proposal(
  120. self.account_id,
  121. self.proposer_id,
  122. self.parameters,
  123. self.title,
  124. self.description,
  125. self.stake_balance,
  126. self.proposal_code,
  127. );
  128. assert_eq!(proposal_id_result, result);
  129. proposal_id_result.ok()
  130. }
  131. }
  132. struct CancelProposalFixture {
  133. origin: RawOrigin<u64>,
  134. proposal_id: u32,
  135. proposer_id: u64,
  136. }
  137. impl CancelProposalFixture {
  138. fn new(proposal_id: u32) -> Self {
  139. CancelProposalFixture {
  140. proposal_id,
  141. origin: RawOrigin::Signed(1),
  142. proposer_id: 1,
  143. }
  144. }
  145. fn with_origin(self, origin: RawOrigin<u64>) -> Self {
  146. CancelProposalFixture { origin, ..self }
  147. }
  148. fn with_proposer(self, proposer_id: u64) -> Self {
  149. CancelProposalFixture {
  150. proposer_id,
  151. ..self
  152. }
  153. }
  154. fn cancel_and_assert(self, expected_result: DispatchResult<Error>) {
  155. assert_eq!(
  156. ProposalsEngine::cancel_proposal(
  157. self.origin.into(),
  158. self.proposer_id,
  159. self.proposal_id
  160. ),
  161. expected_result
  162. );
  163. }
  164. }
  165. struct VetoProposalFixture {
  166. origin: RawOrigin<u64>,
  167. proposal_id: u32,
  168. }
  169. impl VetoProposalFixture {
  170. fn new(proposal_id: u32) -> Self {
  171. VetoProposalFixture {
  172. proposal_id,
  173. origin: RawOrigin::Root,
  174. }
  175. }
  176. fn with_origin(self, origin: RawOrigin<u64>) -> Self {
  177. VetoProposalFixture { origin, ..self }
  178. }
  179. fn veto_and_assert(self, expected_result: DispatchResult<Error>) {
  180. assert_eq!(
  181. ProposalsEngine::veto_proposal(self.origin.into(), self.proposal_id,),
  182. expected_result
  183. );
  184. }
  185. }
  186. struct VoteGenerator {
  187. proposal_id: u32,
  188. current_account_id: u64,
  189. current_voter_id: u64,
  190. pub auto_increment_voter_id: bool,
  191. }
  192. impl VoteGenerator {
  193. fn new(proposal_id: u32) -> Self {
  194. VoteGenerator {
  195. proposal_id,
  196. current_voter_id: 0,
  197. current_account_id: 0,
  198. auto_increment_voter_id: true,
  199. }
  200. }
  201. fn vote_and_assert_ok(&mut self, vote_kind: VoteKind) {
  202. self.vote_and_assert(vote_kind, Ok(()));
  203. }
  204. fn vote_and_assert(&mut self, vote_kind: VoteKind, expected_result: DispatchResult<Error>) {
  205. assert_eq!(self.vote(vote_kind.clone()), expected_result);
  206. }
  207. fn vote(&mut self, vote_kind: VoteKind) -> DispatchResult<Error> {
  208. if self.auto_increment_voter_id {
  209. self.current_account_id += 1;
  210. self.current_voter_id += 1;
  211. }
  212. ProposalsEngine::vote(
  213. system::RawOrigin::Signed(self.current_account_id).into(),
  214. self.current_voter_id,
  215. self.proposal_id,
  216. vote_kind,
  217. )
  218. }
  219. }
  220. struct EventFixture;
  221. impl EventFixture {
  222. fn assert_events(expected_raw_events: Vec<RawEvent<u32, u64, u64, u64, u64>>) {
  223. let expected_events = expected_raw_events
  224. .iter()
  225. .map(|ev| EventRecord {
  226. phase: Phase::ApplyExtrinsic(0),
  227. event: TestEvent::engine(ev.clone()),
  228. topics: vec![],
  229. })
  230. .collect::<Vec<EventRecord<_, _>>>();
  231. assert_eq!(System::events(), expected_events);
  232. }
  233. }
  234. // Recommendation from Parity on testing on_finalize
  235. // https://substrate.dev/docs/en/next/development/module/tests
  236. fn run_to_block(n: u64) {
  237. while System::block_number() < n {
  238. <System as OnFinalize<u64>>::on_finalize(System::block_number());
  239. <ProposalsEngine as OnFinalize<u64>>::on_finalize(System::block_number());
  240. System::set_block_number(System::block_number() + 1);
  241. <System as OnInitialize<u64>>::on_initialize(System::block_number());
  242. <ProposalsEngine as OnInitialize<u64>>::on_initialize(System::block_number());
  243. }
  244. }
  245. fn run_to_block_and_finalize(n: u64) {
  246. run_to_block(n);
  247. <ProposalsEngine as OnFinalize<u64>>::on_finalize(n);
  248. }
  249. #[test]
  250. fn create_dummy_proposal_succeeds() {
  251. initial_test_ext().execute_with(|| {
  252. let dummy_proposal = DummyProposalFixture::default();
  253. dummy_proposal.create_proposal_and_assert(Ok(1));
  254. });
  255. }
  256. #[test]
  257. fn vote_succeeds() {
  258. initial_test_ext().execute_with(|| {
  259. let dummy_proposal = DummyProposalFixture::default();
  260. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  261. let mut vote_generator = VoteGenerator::new(proposal_id);
  262. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  263. });
  264. }
  265. #[test]
  266. fn vote_fails_with_insufficient_rights() {
  267. initial_test_ext().execute_with(|| {
  268. assert_eq!(
  269. ProposalsEngine::vote(system::RawOrigin::None.into(), 1, 1, VoteKind::Approve),
  270. Err(Error::Other("RequireSignedOrigin"))
  271. );
  272. });
  273. }
  274. #[test]
  275. fn proposal_execution_succeeds() {
  276. initial_test_ext().execute_with(|| {
  277. let parameters_fixture = ProposalParametersFixture::default();
  278. let dummy_proposal =
  279. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  280. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  281. // internal active proposal counter check
  282. assert_eq!(<ActiveProposalCount>::get(), 1);
  283. let mut vote_generator = VoteGenerator::new(proposal_id);
  284. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  285. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  286. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  287. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  288. run_to_block_and_finalize(1);
  289. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  290. assert_eq!(
  291. proposal,
  292. Proposal {
  293. parameters: parameters_fixture.params(),
  294. proposer_id: 1,
  295. created_at: 1,
  296. status: ProposalStatus::approved(ApprovedProposalStatus::Executed, 1),
  297. title: b"title".to_vec(),
  298. description: b"description".to_vec(),
  299. voting_results: VotingResults {
  300. abstentions: 0,
  301. approvals: 4,
  302. rejections: 0,
  303. slashes: 0,
  304. },
  305. }
  306. );
  307. // internal active proposal counter check
  308. assert_eq!(<ActiveProposalCount>::get(), 0);
  309. });
  310. }
  311. #[test]
  312. fn proposal_execution_failed() {
  313. initial_test_ext().execute_with(|| {
  314. let parameters_fixture = ProposalParametersFixture::default();
  315. let faulty_proposal = mock::proposals::Call::<Test>::faulty_proposal(
  316. b"title".to_vec(),
  317. b"description".to_vec(),
  318. );
  319. let dummy_proposal = DummyProposalFixture::default()
  320. .with_parameters(parameters_fixture.params())
  321. .with_proposal_code(faulty_proposal.encode());
  322. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  323. let mut vote_generator = VoteGenerator::new(proposal_id);
  324. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  325. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  326. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  327. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  328. run_to_block_and_finalize(2);
  329. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  330. assert_eq!(
  331. proposal,
  332. Proposal {
  333. parameters: parameters_fixture.params(),
  334. proposer_id: 1,
  335. created_at: 1,
  336. status: ProposalStatus::approved(
  337. ApprovedProposalStatus::failed_execution("ExecutionFailed"),
  338. 1
  339. ),
  340. title: b"title".to_vec(),
  341. description: b"description".to_vec(),
  342. voting_results: VotingResults {
  343. abstentions: 0,
  344. approvals: 4,
  345. rejections: 0,
  346. slashes: 0,
  347. },
  348. }
  349. )
  350. });
  351. }
  352. #[test]
  353. fn voting_results_calculation_succeeds() {
  354. initial_test_ext().execute_with(|| {
  355. let parameters = ProposalParameters {
  356. voting_period: 3,
  357. approval_quorum_percentage: 50,
  358. approval_threshold_percentage: 50,
  359. slashing_quorum_percentage: 60,
  360. slashing_threshold_percentage: 60,
  361. grace_period: 0,
  362. required_stake: None,
  363. };
  364. let dummy_proposal = DummyProposalFixture::default().with_parameters(parameters);
  365. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  366. let mut vote_generator = VoteGenerator::new(proposal_id);
  367. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  368. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  369. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  370. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  371. run_to_block_and_finalize(2);
  372. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  373. assert_eq!(
  374. proposal.voting_results,
  375. VotingResults {
  376. abstentions: 1,
  377. approvals: 2,
  378. rejections: 1,
  379. slashes: 0,
  380. }
  381. )
  382. });
  383. }
  384. #[test]
  385. fn rejected_voting_results_and_remove_proposal_id_from_active_succeeds() {
  386. initial_test_ext().execute_with(|| {
  387. // internal active proposal counter check
  388. assert_eq!(<ActiveProposalCount>::get(), 0);
  389. let dummy_proposal = DummyProposalFixture::default();
  390. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  391. let mut vote_generator = VoteGenerator::new(proposal_id);
  392. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  393. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  394. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  395. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  396. assert!(<ActiveProposalIds<Test>>::exists(proposal_id));
  397. // internal active proposal counter check
  398. assert_eq!(<ActiveProposalCount>::get(), 1);
  399. run_to_block_and_finalize(2);
  400. let proposal = <Proposals<Test>>::get(proposal_id);
  401. assert_eq!(
  402. proposal.voting_results,
  403. VotingResults {
  404. abstentions: 2,
  405. approvals: 0,
  406. rejections: 2,
  407. slashes: 0,
  408. }
  409. );
  410. assert_eq!(
  411. proposal.status,
  412. ProposalStatus::finalized_successfully(ProposalDecisionStatus::Rejected, 1),
  413. );
  414. assert!(!<ActiveProposalIds<Test>>::exists(proposal_id));
  415. // internal active proposal counter check
  416. assert_eq!(<ActiveProposalCount>::get(), 0);
  417. });
  418. }
  419. #[test]
  420. fn create_proposal_fails_with_invalid_body_or_title() {
  421. initial_test_ext().execute_with(|| {
  422. let mut dummy_proposal =
  423. DummyProposalFixture::default().with_title_and_body(Vec::new(), b"body".to_vec());
  424. dummy_proposal.create_proposal_and_assert(Err(Error::EmptyTitleProvided.into()));
  425. dummy_proposal =
  426. DummyProposalFixture::default().with_title_and_body(b"title".to_vec(), Vec::new());
  427. dummy_proposal.create_proposal_and_assert(Err(Error::EmptyDescriptionProvided.into()));
  428. let too_long_title = vec![0; 200];
  429. dummy_proposal =
  430. DummyProposalFixture::default().with_title_and_body(too_long_title, b"body".to_vec());
  431. dummy_proposal.create_proposal_and_assert(Err(Error::TitleIsTooLong.into()));
  432. let too_long_body = vec![0; 11000];
  433. dummy_proposal =
  434. DummyProposalFixture::default().with_title_and_body(b"title".to_vec(), too_long_body);
  435. dummy_proposal.create_proposal_and_assert(Err(Error::DescriptionIsTooLong.into()));
  436. });
  437. }
  438. #[test]
  439. fn vote_fails_with_expired_voting_period() {
  440. initial_test_ext().execute_with(|| {
  441. let dummy_proposal = DummyProposalFixture::default();
  442. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  443. run_to_block_and_finalize(6);
  444. let mut vote_generator = VoteGenerator::new(proposal_id);
  445. vote_generator.vote_and_assert(VoteKind::Approve, Err(Error::ProposalFinalized));
  446. });
  447. }
  448. #[test]
  449. fn vote_fails_with_not_active_proposal() {
  450. initial_test_ext().execute_with(|| {
  451. let dummy_proposal = DummyProposalFixture::default();
  452. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  453. let mut vote_generator = VoteGenerator::new(proposal_id);
  454. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  455. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  456. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  457. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  458. run_to_block_and_finalize(2);
  459. let mut vote_generator_to_fail = VoteGenerator::new(proposal_id);
  460. vote_generator_to_fail.vote_and_assert(VoteKind::Approve, Err(Error::ProposalFinalized));
  461. });
  462. }
  463. #[test]
  464. fn vote_fails_with_absent_proposal() {
  465. initial_test_ext().execute_with(|| {
  466. let mut vote_generator = VoteGenerator::new(2);
  467. vote_generator.vote_and_assert(VoteKind::Approve, Err(Error::ProposalNotFound));
  468. });
  469. }
  470. #[test]
  471. fn vote_fails_on_double_voting() {
  472. initial_test_ext().execute_with(|| {
  473. let dummy_proposal = DummyProposalFixture::default();
  474. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  475. let mut vote_generator = VoteGenerator::new(proposal_id);
  476. vote_generator.auto_increment_voter_id = false;
  477. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  478. vote_generator.vote_and_assert(VoteKind::Approve, Err(Error::AlreadyVoted));
  479. });
  480. }
  481. #[test]
  482. fn cancel_proposal_succeeds() {
  483. initial_test_ext().execute_with(|| {
  484. let parameters_fixture = ProposalParametersFixture::default();
  485. let dummy_proposal =
  486. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  487. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  488. // internal active proposal counter check
  489. assert_eq!(<ActiveProposalCount>::get(), 1);
  490. let cancel_proposal = CancelProposalFixture::new(proposal_id);
  491. cancel_proposal.cancel_and_assert(Ok(()));
  492. // internal active proposal counter check
  493. assert_eq!(<ActiveProposalCount>::get(), 0);
  494. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  495. assert_eq!(
  496. proposal,
  497. Proposal {
  498. parameters: parameters_fixture.params(),
  499. proposer_id: 1,
  500. created_at: 1,
  501. status: ProposalStatus::finalized_successfully(ProposalDecisionStatus::Canceled, 1),
  502. title: b"title".to_vec(),
  503. description: b"description".to_vec(),
  504. voting_results: VotingResults::default(),
  505. }
  506. )
  507. });
  508. }
  509. #[test]
  510. fn cancel_proposal_fails_with_not_active_proposal() {
  511. initial_test_ext().execute_with(|| {
  512. let dummy_proposal = DummyProposalFixture::default();
  513. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  514. run_to_block_and_finalize(6);
  515. let cancel_proposal = CancelProposalFixture::new(proposal_id);
  516. cancel_proposal.cancel_and_assert(Err(Error::ProposalFinalized));
  517. });
  518. }
  519. #[test]
  520. fn cancel_proposal_fails_with_not_existing_proposal() {
  521. initial_test_ext().execute_with(|| {
  522. let cancel_proposal = CancelProposalFixture::new(2);
  523. cancel_proposal.cancel_and_assert(Err(Error::ProposalNotFound));
  524. });
  525. }
  526. #[test]
  527. fn cancel_proposal_fails_with_insufficient_rights() {
  528. initial_test_ext().execute_with(|| {
  529. let dummy_proposal = DummyProposalFixture::default();
  530. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  531. let cancel_proposal = CancelProposalFixture::new(proposal_id)
  532. .with_origin(RawOrigin::Signed(2))
  533. .with_proposer(2);
  534. cancel_proposal.cancel_and_assert(Err(Error::NotAuthor));
  535. });
  536. }
  537. #[test]
  538. fn veto_proposal_succeeds() {
  539. initial_test_ext().execute_with(|| {
  540. // internal active proposal counter check
  541. assert_eq!(<ActiveProposalCount>::get(), 0);
  542. let parameters_fixture = ProposalParametersFixture::default();
  543. let dummy_proposal =
  544. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  545. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  546. // internal active proposal counter check
  547. assert_eq!(<ActiveProposalCount>::get(), 1);
  548. let veto_proposal = VetoProposalFixture::new(proposal_id);
  549. veto_proposal.veto_and_assert(Ok(()));
  550. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  551. assert_eq!(
  552. proposal,
  553. Proposal {
  554. parameters: parameters_fixture.params(),
  555. proposer_id: 1,
  556. created_at: 1,
  557. status: ProposalStatus::finalized_successfully(ProposalDecisionStatus::Vetoed, 1),
  558. title: b"title".to_vec(),
  559. description: b"description".to_vec(),
  560. voting_results: VotingResults::default(),
  561. }
  562. );
  563. // internal active proposal counter check
  564. assert_eq!(<ActiveProposalCount>::get(), 0);
  565. });
  566. }
  567. #[test]
  568. fn veto_proposal_fails_with_not_active_proposal() {
  569. initial_test_ext().execute_with(|| {
  570. let dummy_proposal = DummyProposalFixture::default();
  571. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  572. run_to_block_and_finalize(6);
  573. let veto_proposal = VetoProposalFixture::new(proposal_id);
  574. veto_proposal.veto_and_assert(Err(Error::ProposalFinalized));
  575. });
  576. }
  577. #[test]
  578. fn veto_proposal_fails_with_not_existing_proposal() {
  579. initial_test_ext().execute_with(|| {
  580. let veto_proposal = VetoProposalFixture::new(2);
  581. veto_proposal.veto_and_assert(Err(Error::ProposalNotFound));
  582. });
  583. }
  584. #[test]
  585. fn veto_proposal_fails_with_insufficient_rights() {
  586. initial_test_ext().execute_with(|| {
  587. let dummy_proposal = DummyProposalFixture::default();
  588. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  589. let veto_proposal = VetoProposalFixture::new(proposal_id).with_origin(RawOrigin::Signed(2));
  590. veto_proposal.veto_and_assert(Err(Error::RequireRootOrigin));
  591. });
  592. }
  593. #[test]
  594. fn create_proposal_event_emitted() {
  595. initial_test_ext().execute_with(|| {
  596. let dummy_proposal = DummyProposalFixture::default();
  597. dummy_proposal.create_proposal_and_assert(Ok(1));
  598. EventFixture::assert_events(vec![RawEvent::ProposalCreated(1, 1)]);
  599. });
  600. }
  601. #[test]
  602. fn veto_proposal_event_emitted() {
  603. initial_test_ext().execute_with(|| {
  604. let dummy_proposal = DummyProposalFixture::default();
  605. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  606. let veto_proposal = VetoProposalFixture::new(proposal_id);
  607. veto_proposal.veto_and_assert(Ok(()));
  608. EventFixture::assert_events(vec![
  609. RawEvent::ProposalCreated(1, 1),
  610. RawEvent::ProposalStatusUpdated(
  611. 1,
  612. ProposalStatus::finalized_successfully(ProposalDecisionStatus::Vetoed, 1),
  613. ),
  614. ]);
  615. });
  616. }
  617. #[test]
  618. fn cancel_proposal_event_emitted() {
  619. initial_test_ext().execute_with(|| {
  620. let dummy_proposal = DummyProposalFixture::default();
  621. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  622. let cancel_proposal = CancelProposalFixture::new(proposal_id);
  623. cancel_proposal.cancel_and_assert(Ok(()));
  624. EventFixture::assert_events(vec![
  625. RawEvent::ProposalCreated(1, 1),
  626. RawEvent::ProposalStatusUpdated(
  627. 1,
  628. ProposalStatus::Finalized(FinalizationData {
  629. proposal_status: ProposalDecisionStatus::Canceled,
  630. encoded_unstaking_error_due_to_broken_runtime: None,
  631. stake_data_after_unstaking_error: None,
  632. finalized_at: 1,
  633. }),
  634. ),
  635. ]);
  636. });
  637. }
  638. #[test]
  639. fn vote_proposal_event_emitted() {
  640. initial_test_ext().execute_with(|| {
  641. let dummy_proposal = DummyProposalFixture::default();
  642. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  643. let mut vote_generator = VoteGenerator::new(proposal_id);
  644. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  645. EventFixture::assert_events(vec![
  646. RawEvent::ProposalCreated(1, 1),
  647. RawEvent::Voted(1, 1, VoteKind::Approve),
  648. ]);
  649. });
  650. }
  651. #[test]
  652. fn create_proposal_and_expire_it() {
  653. initial_test_ext().execute_with(|| {
  654. let parameters_fixture = ProposalParametersFixture::default();
  655. let dummy_proposal =
  656. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  657. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  658. run_to_block_and_finalize(8);
  659. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  660. assert_eq!(
  661. proposal,
  662. Proposal {
  663. parameters: parameters_fixture.params(),
  664. proposer_id: 1,
  665. created_at: 1,
  666. status: ProposalStatus::finalized_successfully(ProposalDecisionStatus::Expired, 4),
  667. title: b"title".to_vec(),
  668. description: b"description".to_vec(),
  669. voting_results: VotingResults::default(),
  670. }
  671. )
  672. });
  673. }
  674. #[test]
  675. fn proposal_execution_postponed_because_of_grace_period() {
  676. initial_test_ext().execute_with(|| {
  677. let parameters_fixture = ProposalParametersFixture::default().with_grace_period(2);
  678. let dummy_proposal =
  679. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  680. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  681. let mut vote_generator = VoteGenerator::new(proposal_id);
  682. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  683. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  684. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  685. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  686. run_to_block_and_finalize(1);
  687. run_to_block_and_finalize(2);
  688. // check internal cache for proposal_id presense
  689. assert!(<PendingExecutionProposalIds<Test>>::enumerate()
  690. .find(|(x, _)| *x == proposal_id)
  691. .is_some());
  692. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  693. assert_eq!(
  694. proposal,
  695. Proposal {
  696. parameters: parameters_fixture.params(),
  697. proposer_id: 1,
  698. created_at: 1,
  699. status: ProposalStatus::approved(ApprovedProposalStatus::PendingExecution, 1),
  700. title: b"title".to_vec(),
  701. description: b"description".to_vec(),
  702. voting_results: VotingResults {
  703. abstentions: 0,
  704. approvals: 4,
  705. rejections: 0,
  706. slashes: 0,
  707. },
  708. }
  709. );
  710. });
  711. }
  712. #[test]
  713. fn proposal_execution_vetoed_successfully_during_the_grace_period() {
  714. initial_test_ext().execute_with(|| {
  715. let parameters_fixture = ProposalParametersFixture::default().with_grace_period(2);
  716. let dummy_proposal =
  717. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  718. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  719. let mut vote_generator = VoteGenerator::new(proposal_id);
  720. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  721. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  722. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  723. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  724. run_to_block_and_finalize(1);
  725. run_to_block_and_finalize(2);
  726. // check internal cache for proposal_id presense
  727. assert!(<PendingExecutionProposalIds<Test>>::enumerate()
  728. .find(|(x, _)| *x == proposal_id)
  729. .is_some());
  730. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  731. assert_eq!(
  732. proposal,
  733. Proposal {
  734. parameters: parameters_fixture.params(),
  735. proposer_id: 1,
  736. created_at: 1,
  737. status: ProposalStatus::approved(ApprovedProposalStatus::PendingExecution, 1),
  738. title: b"title".to_vec(),
  739. description: b"description".to_vec(),
  740. voting_results: VotingResults {
  741. abstentions: 0,
  742. approvals: 4,
  743. rejections: 0,
  744. slashes: 0,
  745. },
  746. }
  747. );
  748. let veto_proposal = VetoProposalFixture::new(proposal_id);
  749. veto_proposal.veto_and_assert(Ok(()));
  750. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  751. assert_eq!(
  752. proposal,
  753. Proposal {
  754. parameters: parameters_fixture.params(),
  755. proposer_id: 1,
  756. created_at: 1,
  757. status: ProposalStatus::finalized_successfully(ProposalDecisionStatus::Vetoed, 2),
  758. title: b"title".to_vec(),
  759. description: b"description".to_vec(),
  760. voting_results: VotingResults {
  761. abstentions: 0,
  762. approvals: 4,
  763. rejections: 0,
  764. slashes: 0,
  765. },
  766. }
  767. );
  768. // check internal cache for proposal_id presense
  769. assert!(<PendingExecutionProposalIds<Test>>::enumerate()
  770. .find(|(x, _)| *x == proposal_id)
  771. .is_none());
  772. });
  773. }
  774. #[test]
  775. fn proposal_execution_succeeds_after_the_grace_period() {
  776. initial_test_ext().execute_with(|| {
  777. let parameters_fixture = ProposalParametersFixture::default().with_grace_period(1);
  778. let dummy_proposal =
  779. DummyProposalFixture::default().with_parameters(parameters_fixture.params());
  780. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  781. let mut vote_generator = VoteGenerator::new(proposal_id);
  782. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  783. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  784. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  785. vote_generator.vote_and_assert_ok(VoteKind::Approve);
  786. run_to_block_and_finalize(1);
  787. // check internal cache for proposal_id presence
  788. assert!(<PendingExecutionProposalIds<Test>>::enumerate()
  789. .find(|(x, _)| *x == proposal_id)
  790. .is_some());
  791. let mut proposal = <crate::Proposals<Test>>::get(proposal_id);
  792. let mut expected_proposal = Proposal {
  793. parameters: parameters_fixture.params(),
  794. proposer_id: 1,
  795. created_at: 1,
  796. status: ProposalStatus::approved(ApprovedProposalStatus::PendingExecution, 1),
  797. title: b"title".to_vec(),
  798. description: b"description".to_vec(),
  799. voting_results: VotingResults {
  800. abstentions: 0,
  801. approvals: 4,
  802. rejections: 0,
  803. slashes: 0,
  804. },
  805. };
  806. assert_eq!(proposal, expected_proposal);
  807. run_to_block_and_finalize(2);
  808. proposal = <crate::Proposals<Test>>::get(proposal_id);
  809. expected_proposal.status = ProposalStatus::approved(ApprovedProposalStatus::Executed, 1);
  810. assert_eq!(proposal, expected_proposal);
  811. // check internal cache for proposal_id absense
  812. assert!(<PendingExecutionProposalIds<Test>>::enumerate()
  813. .find(|(x, _)| *x == proposal_id)
  814. .is_none());
  815. });
  816. }
  817. #[test]
  818. fn create_proposal_fails_on_exceeding_max_active_proposals_count() {
  819. initial_test_ext().execute_with(|| {
  820. for idx in 1..101 {
  821. let dummy_proposal = DummyProposalFixture::default();
  822. dummy_proposal.create_proposal_and_assert(Ok(idx));
  823. // internal active proposal counter check
  824. assert_eq!(<ActiveProposalCount>::get(), idx);
  825. }
  826. let dummy_proposal = DummyProposalFixture::default();
  827. dummy_proposal
  828. .create_proposal_and_assert(Err(Error::MaxActiveProposalNumberExceeded.into()));
  829. // internal active proposal counter check
  830. assert_eq!(<ActiveProposalCount>::get(), 100);
  831. });
  832. }
  833. #[test]
  834. fn voting_internal_cache_exists_after_proposal_finalization() {
  835. initial_test_ext().execute_with(|| {
  836. let dummy_proposal = DummyProposalFixture::default();
  837. dummy_proposal.create_proposal_and_assert(Ok(1));
  838. // last created proposal id equals current proposal count
  839. let proposal_id = <ProposalCount>::get();
  840. let mut vote_generator = VoteGenerator::new(proposal_id);
  841. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  842. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  843. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  844. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  845. // cache exists
  846. assert!(<crate::VoteExistsByProposalByVoter<Test>>::exists(
  847. proposal_id,
  848. 1
  849. ));
  850. run_to_block_and_finalize(2);
  851. // cache still exists and is not cleared
  852. assert!(<crate::VoteExistsByProposalByVoter<Test>>::exists(
  853. proposal_id,
  854. 1
  855. ));
  856. });
  857. }
  858. #[test]
  859. fn create_dummy_proposal_succeeds_with_stake() {
  860. initial_test_ext().execute_with(|| {
  861. let account_id = 1;
  862. let required_stake = 200;
  863. let parameters_fixture =
  864. ProposalParametersFixture::default().with_required_stake(required_stake);
  865. let dummy_proposal = DummyProposalFixture::default()
  866. .with_parameters(parameters_fixture.params())
  867. .with_account_id(account_id)
  868. .with_stake(200);
  869. let _imbalance = <Test as stake::Trait>::Currency::deposit_creating(&account_id, 500);
  870. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  871. let proposal = <crate::Proposals<Test>>::get(proposal_id);
  872. assert_eq!(
  873. proposal,
  874. Proposal {
  875. parameters: parameters_fixture.params(),
  876. proposer_id: 1,
  877. created_at: 1,
  878. status: ProposalStatus::Active(Some(ActiveStake {
  879. stake_id: 0, // valid stake_id
  880. source_account_id: 1
  881. })),
  882. title: b"title".to_vec(),
  883. description: b"description".to_vec(),
  884. voting_results: VotingResults::default(),
  885. }
  886. )
  887. });
  888. }
  889. #[test]
  890. fn create_dummy_proposal_fail_with_stake_on_empty_account() {
  891. initial_test_ext().execute_with(|| {
  892. let account_id = 1;
  893. let required_stake = 200;
  894. let parameters_fixture =
  895. ProposalParametersFixture::default().with_required_stake(required_stake);
  896. let dummy_proposal = DummyProposalFixture::default()
  897. .with_parameters(parameters_fixture.params())
  898. .with_account_id(account_id)
  899. .with_stake(required_stake);
  900. dummy_proposal
  901. .create_proposal_and_assert(Err(Error::Other("too few free funds in account")));
  902. });
  903. }
  904. #[test]
  905. fn create_proposal_fais_with_invalid_stake_parameters() {
  906. initial_test_ext().execute_with(|| {
  907. let parameters_fixture = ProposalParametersFixture::default();
  908. let mut dummy_proposal = DummyProposalFixture::default()
  909. .with_parameters(parameters_fixture.params())
  910. .with_stake(200);
  911. dummy_proposal.create_proposal_and_assert(Err(Error::StakeShouldBeEmpty.into()));
  912. let parameters_fixture_stake_200 = parameters_fixture.with_required_stake(200);
  913. dummy_proposal =
  914. DummyProposalFixture::default().with_parameters(parameters_fixture_stake_200.params());
  915. dummy_proposal.create_proposal_and_assert(Err(Error::EmptyStake.into()));
  916. let parameters_fixture_stake_300 = parameters_fixture.with_required_stake(300);
  917. dummy_proposal = DummyProposalFixture::default()
  918. .with_parameters(parameters_fixture_stake_300.params())
  919. .with_stake(200);
  920. dummy_proposal.create_proposal_and_assert(Err(Error::StakeDiffersFromRequired.into()));
  921. });
  922. }
  923. #[test]
  924. fn finalize_expired_proposal_and_check_stake_removing_with_balance_checks_succeeds() {
  925. initial_test_ext().execute_with(|| {
  926. let account_id = 1;
  927. let stake_amount = 200;
  928. let parameters = ProposalParameters {
  929. voting_period: 3,
  930. approval_quorum_percentage: 50,
  931. approval_threshold_percentage: 60,
  932. slashing_quorum_percentage: 60,
  933. slashing_threshold_percentage: 60,
  934. grace_period: 5,
  935. required_stake: Some(stake_amount),
  936. };
  937. let dummy_proposal = DummyProposalFixture::default()
  938. .with_parameters(parameters)
  939. .with_account_id(account_id)
  940. .with_stake(stake_amount);
  941. let account_balance = 500;
  942. let _imbalance =
  943. <Test as stake::Trait>::Currency::deposit_creating(&account_id, account_balance);
  944. assert_eq!(
  945. <Test as stake::Trait>::Currency::total_balance(&account_id),
  946. account_balance
  947. );
  948. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  949. assert_eq!(
  950. <Test as stake::Trait>::Currency::total_balance(&account_id),
  951. account_balance - stake_amount
  952. );
  953. let mut proposal = <crate::Proposals<Test>>::get(proposal_id);
  954. let mut expected_proposal = Proposal {
  955. parameters,
  956. proposer_id: 1,
  957. created_at: 1,
  958. status: ProposalStatus::Active(Some(ActiveStake {
  959. stake_id: 0,
  960. source_account_id: 1,
  961. })),
  962. title: b"title".to_vec(),
  963. description: b"description".to_vec(),
  964. voting_results: VotingResults::default(),
  965. };
  966. assert_eq!(proposal, expected_proposal);
  967. run_to_block_and_finalize(5);
  968. proposal = <crate::Proposals<Test>>::get(proposal_id);
  969. expected_proposal.status = ProposalStatus::Finalized(FinalizationData {
  970. proposal_status: ProposalDecisionStatus::Expired,
  971. finalized_at: 4,
  972. encoded_unstaking_error_due_to_broken_runtime: None,
  973. stake_data_after_unstaking_error: None,
  974. });
  975. assert_eq!(proposal, expected_proposal);
  976. let rejection_fee = RejectionFee::get();
  977. assert_eq!(
  978. <Test as stake::Trait>::Currency::total_balance(&account_id),
  979. account_balance - rejection_fee
  980. );
  981. });
  982. }
  983. #[test]
  984. fn proposal_cancellation_with_slashes_with_balance_checks_succeeds() {
  985. initial_test_ext().execute_with(|| {
  986. let account_id = 1;
  987. let stake_amount = 200;
  988. let parameters = ProposalParameters {
  989. voting_period: 3,
  990. approval_quorum_percentage: 50,
  991. approval_threshold_percentage: 60,
  992. slashing_quorum_percentage: 60,
  993. slashing_threshold_percentage: 60,
  994. grace_period: 5,
  995. required_stake: Some(stake_amount),
  996. };
  997. let dummy_proposal = DummyProposalFixture::default()
  998. .with_parameters(parameters)
  999. .with_account_id(account_id.clone())
  1000. .with_stake(stake_amount);
  1001. let account_balance = 500;
  1002. let _imbalance =
  1003. <Test as stake::Trait>::Currency::deposit_creating(&account_id, account_balance);
  1004. assert_eq!(
  1005. <Test as stake::Trait>::Currency::total_balance(&account_id),
  1006. account_balance
  1007. );
  1008. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1009. assert_eq!(
  1010. <Test as stake::Trait>::Currency::total_balance(&account_id),
  1011. account_balance - stake_amount
  1012. );
  1013. let mut proposal = <crate::Proposals<Test>>::get(proposal_id);
  1014. let mut expected_proposal = Proposal {
  1015. parameters,
  1016. proposer_id: 1,
  1017. created_at: 1,
  1018. status: ProposalStatus::Active(Some(ActiveStake {
  1019. stake_id: 0,
  1020. source_account_id: 1,
  1021. })),
  1022. title: b"title".to_vec(),
  1023. description: b"description".to_vec(),
  1024. voting_results: VotingResults::default(),
  1025. };
  1026. assert_eq!(proposal, expected_proposal);
  1027. let cancel_proposal_fixture = CancelProposalFixture::new(proposal_id);
  1028. cancel_proposal_fixture.cancel_and_assert(Ok(()));
  1029. proposal = <crate::Proposals<Test>>::get(proposal_id);
  1030. expected_proposal.status = ProposalStatus::Finalized(FinalizationData {
  1031. proposal_status: ProposalDecisionStatus::Canceled,
  1032. finalized_at: 1,
  1033. encoded_unstaking_error_due_to_broken_runtime: None,
  1034. stake_data_after_unstaking_error: None,
  1035. });
  1036. assert_eq!(proposal, expected_proposal);
  1037. let cancellation_fee = CancellationFee::get();
  1038. assert_eq!(
  1039. <Test as stake::Trait>::Currency::total_balance(&account_id),
  1040. account_balance - cancellation_fee
  1041. );
  1042. });
  1043. }
  1044. #[test]
  1045. fn finalize_proposal_using_stake_mocks_succeeds() {
  1046. handle_mock(|| {
  1047. initial_test_ext().execute_with(|| {
  1048. let mock = {
  1049. let mut mock = crate::types::MockStakeHandler::<Test>::new();
  1050. mock.expect_create_stake().times(1).returning(|| Ok(1));
  1051. mock.expect_make_stake_imbalance()
  1052. .times(1)
  1053. .returning(|_, _| Ok(crate::types::NegativeImbalance::<Test>::new(200)));
  1054. mock.expect_stake().times(1).returning(|_, _| Ok(()));
  1055. mock.expect_remove_stake().times(1).returning(|_| Ok(()));
  1056. mock.expect_unstake().times(1).returning(|_| Ok(()));
  1057. mock.expect_slash().times(1).returning(|_, _| Ok(()));
  1058. Rc::new(mock)
  1059. };
  1060. set_stake_handler_impl(mock.clone());
  1061. let account_id = 1;
  1062. let stake_amount = 200;
  1063. let parameters_fixture =
  1064. ProposalParametersFixture::default().with_required_stake(stake_amount);
  1065. let dummy_proposal = DummyProposalFixture::default()
  1066. .with_parameters(parameters_fixture.params())
  1067. .with_account_id(account_id)
  1068. .with_stake(stake_amount);
  1069. let _proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1070. run_to_block_and_finalize(5);
  1071. });
  1072. });
  1073. }
  1074. #[test]
  1075. fn proposal_slashing_succeeds() {
  1076. initial_test_ext().execute_with(|| {
  1077. let dummy_proposal = DummyProposalFixture::default();
  1078. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1079. let mut vote_generator = VoteGenerator::new(proposal_id);
  1080. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  1081. vote_generator.vote_and_assert_ok(VoteKind::Slash);
  1082. vote_generator.vote_and_assert_ok(VoteKind::Slash);
  1083. vote_generator.vote_and_assert_ok(VoteKind::Slash);
  1084. assert!(<ActiveProposalIds<Test>>::exists(proposal_id));
  1085. run_to_block_and_finalize(2);
  1086. let proposal = <Proposals<Test>>::get(proposal_id);
  1087. assert_eq!(
  1088. proposal.voting_results,
  1089. VotingResults {
  1090. abstentions: 0,
  1091. approvals: 0,
  1092. rejections: 1,
  1093. slashes: 3,
  1094. }
  1095. );
  1096. assert_eq!(
  1097. proposal.status,
  1098. ProposalStatus::Finalized(FinalizationData {
  1099. proposal_status: ProposalDecisionStatus::Slashed,
  1100. encoded_unstaking_error_due_to_broken_runtime: None,
  1101. finalized_at: 1,
  1102. stake_data_after_unstaking_error: None,
  1103. }),
  1104. );
  1105. assert!(!<ActiveProposalIds<Test>>::exists(proposal_id));
  1106. });
  1107. }
  1108. #[test]
  1109. fn finalize_proposal_using_stake_mocks_failed() {
  1110. handle_mock(|| {
  1111. initial_test_ext().execute_with(|| {
  1112. let mock = {
  1113. let mut mock = crate::types::MockStakeHandler::<Test>::new();
  1114. mock.expect_create_stake().times(1).returning(|| Ok(1));
  1115. mock.expect_remove_stake()
  1116. .times(1)
  1117. .returning(|_| Err("Cannot remove stake"));
  1118. mock.expect_make_stake_imbalance()
  1119. .times(1)
  1120. .returning(|_, _| Ok(crate::types::NegativeImbalance::<Test>::new(200)));
  1121. mock.expect_stake().times(1).returning(|_, _| Ok(()));
  1122. mock.expect_unstake().times(1).returning(|_| Ok(()));
  1123. mock.expect_slash().times(1).returning(|_, _| Ok(()));
  1124. Rc::new(mock)
  1125. };
  1126. set_stake_handler_impl(mock.clone());
  1127. let account_id = 1;
  1128. let stake_amount = 200;
  1129. let parameters_fixture =
  1130. ProposalParametersFixture::default().with_required_stake(stake_amount);
  1131. let dummy_proposal = DummyProposalFixture::default()
  1132. .with_parameters(parameters_fixture.params())
  1133. .with_account_id(account_id)
  1134. .with_stake(stake_amount);
  1135. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1136. run_to_block_and_finalize(5);
  1137. let proposal = <Proposals<Test>>::get(proposal_id);
  1138. assert_eq!(
  1139. proposal,
  1140. Proposal {
  1141. parameters: parameters_fixture.params(),
  1142. proposer_id: 1,
  1143. created_at: 1,
  1144. status: ProposalStatus::finalized(
  1145. ProposalDecisionStatus::Expired,
  1146. Some("Cannot remove stake"),
  1147. Some(ActiveStake {
  1148. stake_id: 1,
  1149. source_account_id: 1
  1150. }),
  1151. 4,
  1152. ),
  1153. title: b"title".to_vec(),
  1154. description: b"description".to_vec(),
  1155. voting_results: VotingResults::default(),
  1156. }
  1157. );
  1158. });
  1159. });
  1160. }
  1161. #[test]
  1162. fn create_proposal_fails_with_invalid_threshold_parameters() {
  1163. initial_test_ext().execute_with(|| {
  1164. let mut parameters = ProposalParameters {
  1165. voting_period: 3,
  1166. approval_quorum_percentage: 50,
  1167. approval_threshold_percentage: 0,
  1168. slashing_quorum_percentage: 60,
  1169. slashing_threshold_percentage: 60,
  1170. grace_period: 5,
  1171. required_stake: None,
  1172. };
  1173. let mut dummy_proposal = DummyProposalFixture::default().with_parameters(parameters);
  1174. dummy_proposal
  1175. .create_proposal_and_assert(Err(Error::InvalidParameterApprovalThreshold.into()));
  1176. parameters.approval_threshold_percentage = 60;
  1177. parameters.slashing_threshold_percentage = 0;
  1178. dummy_proposal = DummyProposalFixture::default().with_parameters(parameters);
  1179. dummy_proposal
  1180. .create_proposal_and_assert(Err(Error::InvalidParameterSlashingThreshold.into()));
  1181. });
  1182. }
  1183. #[test]
  1184. fn proposal_reset_succeeds() {
  1185. initial_test_ext().execute_with(|| {
  1186. let dummy_proposal = DummyProposalFixture::default();
  1187. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1188. let mut vote_generator = VoteGenerator::new(proposal_id);
  1189. vote_generator.vote_and_assert_ok(VoteKind::Reject);
  1190. vote_generator.vote_and_assert_ok(VoteKind::Abstain);
  1191. vote_generator.vote_and_assert_ok(VoteKind::Slash);
  1192. assert!(<ActiveProposalIds<Test>>::exists(proposal_id));
  1193. assert_eq!(
  1194. <VoteExistsByProposalByVoter<Test>>::get(&proposal_id, &2),
  1195. VoteKind::Abstain
  1196. );
  1197. run_to_block_and_finalize(2);
  1198. let proposal = <Proposals<Test>>::get(proposal_id);
  1199. assert_eq!(
  1200. proposal.voting_results,
  1201. VotingResults {
  1202. abstentions: 1,
  1203. approvals: 0,
  1204. rejections: 1,
  1205. slashes: 1,
  1206. }
  1207. );
  1208. ProposalsEngine::reset_active_proposals();
  1209. let updated_proposal = <Proposals<Test>>::get(proposal_id);
  1210. assert_eq!(
  1211. updated_proposal.voting_results,
  1212. VotingResults {
  1213. abstentions: 0,
  1214. approvals: 0,
  1215. rejections: 0,
  1216. slashes: 0,
  1217. }
  1218. );
  1219. // whole double map prefix was removed (should return default value)
  1220. assert_eq!(
  1221. <VoteExistsByProposalByVoter<Test>>::get(&proposal_id, &2),
  1222. VoteKind::default()
  1223. );
  1224. });
  1225. }
  1226. #[test]
  1227. fn proposal_counters_are_valid() {
  1228. initial_test_ext().execute_with(|| {
  1229. let mut dummy_proposal = DummyProposalFixture::default();
  1230. let _ = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1231. dummy_proposal = DummyProposalFixture::default();
  1232. let _ = dummy_proposal.create_proposal_and_assert(Ok(2)).unwrap();
  1233. dummy_proposal = DummyProposalFixture::default();
  1234. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(3)).unwrap();
  1235. assert_eq!(ActiveProposalCount::get(), 3);
  1236. assert_eq!(ProposalCount::get(), 3);
  1237. let cancel_proposal_fixture = CancelProposalFixture::new(proposal_id);
  1238. cancel_proposal_fixture.cancel_and_assert(Ok(()));
  1239. assert_eq!(ActiveProposalCount::get(), 2);
  1240. assert_eq!(ProposalCount::get(), 3);
  1241. });
  1242. }
  1243. #[test]
  1244. fn proposal_stake_cache_is_valid() {
  1245. initial_test_ext().execute_with(|| {
  1246. increase_total_balance_issuance_using_account_id(1, 50000);
  1247. let stake = 250u32;
  1248. let parameters = ProposalParametersFixture::default().with_required_stake(stake.into());
  1249. let dummy_proposal = DummyProposalFixture::default()
  1250. .with_parameters(parameters.params())
  1251. .with_stake(stake as u64);
  1252. let proposal_id = dummy_proposal.create_proposal_and_assert(Ok(1)).unwrap();
  1253. let expected_stake_id = 0;
  1254. assert_eq!(
  1255. <StakesProposals<Test>>::get(&expected_stake_id),
  1256. proposal_id
  1257. );
  1258. });
  1259. }
  1260. #[test]
  1261. fn slash_balance_is_calculated_correctly() {
  1262. initial_test_ext().execute_with(|| {
  1263. let vetoed_slash_balance = ProposalsEngine::calculate_slash_balance(
  1264. &ProposalDecisionStatus::Vetoed,
  1265. &ProposalParametersFixture::default().params(),
  1266. );
  1267. assert_eq!(vetoed_slash_balance, 0);
  1268. let approved_slash_balance = ProposalsEngine::calculate_slash_balance(
  1269. &ProposalDecisionStatus::Approved(ApprovedProposalStatus::Executed),
  1270. &ProposalParametersFixture::default().params(),
  1271. );
  1272. assert_eq!(approved_slash_balance, 0);
  1273. let rejection_fee = <Test as crate::Trait>::RejectionFee::get();
  1274. let rejected_slash_balance = ProposalsEngine::calculate_slash_balance(
  1275. &ProposalDecisionStatus::Rejected,
  1276. &ProposalParametersFixture::default().params(),
  1277. );
  1278. assert_eq!(rejected_slash_balance, rejection_fee);
  1279. let expired_slash_balance = ProposalsEngine::calculate_slash_balance(
  1280. &ProposalDecisionStatus::Expired,
  1281. &ProposalParametersFixture::default().params(),
  1282. );
  1283. assert_eq!(expired_slash_balance, rejection_fee);
  1284. let cancellation_fee = <Test as crate::Trait>::CancellationFee::get();
  1285. let cancellation_slash_balance = ProposalsEngine::calculate_slash_balance(
  1286. &ProposalDecisionStatus::Canceled,
  1287. &ProposalParametersFixture::default().params(),
  1288. );
  1289. assert_eq!(cancellation_slash_balance, cancellation_fee);
  1290. let slash_balance_with_no_stake = ProposalsEngine::calculate_slash_balance(
  1291. &ProposalDecisionStatus::Slashed,
  1292. &ProposalParametersFixture::default().params(),
  1293. );
  1294. assert_eq!(slash_balance_with_no_stake, 0);
  1295. let stake = 256;
  1296. let slash_balance_with_stake = ProposalsEngine::calculate_slash_balance(
  1297. &ProposalDecisionStatus::Slashed,
  1298. &ProposalParametersFixture::default()
  1299. .with_required_stake(stake)
  1300. .params(),
  1301. );
  1302. assert_eq!(slash_balance_with_stake, stake);
  1303. });
  1304. }