From e1ec9c51f8703350a40324c9f61eede82110a010 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 13 Nov 2018 16:28:46 +0100 Subject: [PATCH 1/3] submitProposal logic --- .../components/governance/LiProposal.vue | 4 +- app/src/renderer/connectors/lcdClientMock.js | 77 +++++++++++++++++-- .../components/governance/LiProposal.spec.js | 6 +- .../governance/PageProposal.spec.js | 4 +- test/unit/specs/lcdClientMock.spec.js | 4 +- 5 files changed, 78 insertions(+), 17 deletions(-) diff --git a/app/src/renderer/components/governance/LiProposal.vue b/app/src/renderer/components/governance/LiProposal.vue index 3390a890e3..9950dc6d86 100644 --- a/app/src/renderer/components/governance/LiProposal.vue +++ b/app/src/renderer/components/governance/LiProposal.vue @@ -30,13 +30,13 @@ export default { message: `This proposal has been rejected and voting is closed`, color: `red` } - if (this.proposal.proposal_status === `Pending`) + if (this.proposal.proposal_status === `DepositPeriod`) return { button: `deposit`, message: `Deposits are open for this proposal`, color: `yellow` } - if (this.proposal.proposal_status === `Active`) + if (this.proposal.proposal_status === `VotingPeriod`) return { button: `vote`, message: `Voting for this proposal is open`, diff --git a/app/src/renderer/connectors/lcdClientMock.js b/app/src/renderer/connectors/lcdClientMock.js index f228a008ad..f1973bd1d3 100644 --- a/app/src/renderer/connectors/lcdClientMock.js +++ b/app/src/renderer/connectors/lcdClientMock.js @@ -351,7 +351,7 @@ let state = { { proposal_id: `2`, proposal_type: `Text`, - title: `Active proposal`, + title: `VotingPeriod proposal`, description: `custom text proposal description`, initial_deposit: [ { @@ -367,7 +367,7 @@ let state = { ], submit_block: `10`, voting_start_block: `10`, - proposal_status: `Active`, + proposal_status: `VotingPeriod`, tally_result: { yes: `0`, no: `0`, @@ -394,7 +394,7 @@ let state = { ], submit_block: `10`, voting_start_block: `-1`, - proposal_status: `Pending`, + proposal_status: `DepositPeriod`, tally_result: { yes: `0`, no: `0`, @@ -903,6 +903,67 @@ module.exports = { async getProposals() { return state.proposals || [] }, + async submitProposal({ + base_req, + title, + description, + proposal_type, + proposer, + initial_deposit + }) { + let results = [] + // get new proposal id + let proposal_id = `0` + if (state.proposals && state.proposals.length > 0) { + proposal_id = String(parseInt(state.proposals[-1].proposal_id) + 1) + } + + if ( + proposal_type !== `Text` && + proposal_type !== `ParameterChange` && + proposal_type !== `SoftwareUpgrade` + ) { + results.push(txResult(2, `${proposal_type} is not a valid proposal type`)) + return results + } + + let tally_result = { + yes: `0`, + no: `0`, + no_with_veto: `0`, + abstain: `0` + } + let submit_time = Date.now() + let deposit_end_time = moment(submit_time) + .add(86400000, `ms`) + .toDate() + + let proposal = { + proposal_id, + title, + description, + proposal_type, + proposal_status: `DepositPeriod`, + tally_result, + submit_time, + deposit_end_time, + voting_start_time: undefined, + voting_end_time: undefined + } + + // we add the proposal to the state to make it available for the submitProposalDeposit function + state.proposals.push(proposal) + let res = this.submitProposalDeposit({ + base_req, + depositer: proposer, + deposit: initial_deposit + }) + // remove proposal from state if it fails + if (res[0].check_tx.code !== 0) { + state.proposals.pop() + } + return res + }, async getProposal(proposalId) { return state.proposals.find( proposal => proposal.proposal_id === String(proposalId) @@ -947,8 +1008,8 @@ module.exports = { results.push(txResult(3, `Nonexistent proposal`)) return results } else if ( - proposal.proposal_status != `Pending` && - proposal.proposal_status != `Active` + proposal.proposal_status != `DepositPeriod` && + proposal.proposal_status != `VotingPeriod` ) { results.push(txResult(3, `Proposal #${proposal_id} already finished`)) return results @@ -1021,14 +1082,14 @@ module.exports = { incrementSequence(fromAccount) // check if the propoposal is now active - if (proposal.proposal_status === `Pending`) { + if (proposal.proposal_status === `DepositPeriod`) { // TODO: get min deposit denom from gov params instead of stake params let depositCoinAmt = proposal.total_deposit.find(coin => { return coin.denom === `steak` }).amount // TODO: get min deposit amount from gov params if (parseInt(depositCoinAmt) >= 10) { - proposal.proposal_status = `Active` + proposal.proposal_status = `VotingPeriod` // TODO: get voting time from gov params proposal.voting_start_block = Date.now() proposal.voting_end_block = moment(proposal.voting_start_block) @@ -1075,7 +1136,7 @@ module.exports = { if (!proposal) { results.push(txResult(3, `Nonexistent proposal`)) return results - } else if (proposal.proposal_status != `Active`) { + } else if (proposal.proposal_status != `VotingPeriod`) { results.push(txResult(3, `Proposal #${proposal_id} is inactive`)) return results } else if ( diff --git a/test/unit/specs/components/governance/LiProposal.spec.js b/test/unit/specs/components/governance/LiProposal.spec.js index 07464138f6..c53b27ce0e 100644 --- a/test/unit/specs/components/governance/LiProposal.spec.js +++ b/test/unit/specs/components/governance/LiProposal.spec.js @@ -53,7 +53,7 @@ describe(`LiProposal`, () => { }) it(`should return status info for active proposals`, () => { - proposal.proposal_status = `Active` + proposal.proposal_status = `VotingPeriod` let { wrapper } = mount(LiProposal, { propsData: { proposal @@ -67,8 +67,8 @@ describe(`LiProposal`, () => { }) }) - it(`should return status info for pending proposals`, () => { - proposal.proposal_status = `Pending` + it(`should return status info for 'DepositPeriod' proposals`, () => { + proposal.proposal_status = `DepositPeriod` let { wrapper } = mount(LiProposal, { propsData: { proposal diff --git a/test/unit/specs/components/governance/PageProposal.spec.js b/test/unit/specs/components/governance/PageProposal.spec.js index bf91f41a92..cdbb16ac13 100644 --- a/test/unit/specs/components/governance/PageProposal.spec.js +++ b/test/unit/specs/components/governance/PageProposal.spec.js @@ -65,7 +65,7 @@ describe(`PageProposal`, () => { }) describe(`Modal onVote`, () => { - it(`enables voting if the proposal is Active`, () => { + it(`enables voting if the proposal is on the 'VotingPeriod'`, () => { let status = { button: `vote` } wrapper.setProps({ status }) @@ -75,7 +75,7 @@ describe(`PageProposal`, () => { expect(voteBtn.html()).not.toContain(`disabled="disabled"`) }) - it(`disables voting if the proposal is Pending deposits`, () => { + it(`disables voting if the proposal is on the 'DepositPeriod'`, () => { let status = { button: `deposit` } wrapper.setProps({ status }) expect(wrapper.find(`#vote-btn`).exists()).toEqual(false) diff --git a/test/unit/specs/lcdClientMock.spec.js b/test/unit/specs/lcdClientMock.spec.js index 6622b0af5d..f905531052 100644 --- a/test/unit/specs/lcdClientMock.spec.js +++ b/test/unit/specs/lcdClientMock.spec.js @@ -1078,8 +1078,8 @@ describe(`LCD Client Mock`, () => { }) expect(totalDepositAfter).toEqual(deposit.amount[0]) - // should have updated the status of the proposal from `Pending` to `Active` - expect(proposalAfter.proposal_status).toEqual(`Active`) + // should have updated the status of the proposal from `DepositPeriod` to `VotingPeriod` + expect(proposalAfter.proposal_status).toEqual(`VotingPeriod`) // should have added the deposit from the depositer let userDepositAfter = await client.getProposalDeposit( From d8f33c818032aab4b911e6ffe44cb662125f7c1b Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 13 Nov 2018 19:08:37 +0100 Subject: [PATCH 2/3] finished all success test cases --- app/src/renderer/connectors/lcdClientMock.js | 34 +- .../__snapshots__/TabProposals.spec.js.snap | 2 +- .../__snapshots__/TableProposals.spec.js.snap | 2 +- test/unit/specs/lcdClientMock.spec.js | 302 ++++++++++++------ 4 files changed, 233 insertions(+), 107 deletions(-) diff --git a/app/src/renderer/connectors/lcdClientMock.js b/app/src/renderer/connectors/lcdClientMock.js index f1973bd1d3..5c106c17f4 100644 --- a/app/src/renderer/connectors/lcdClientMock.js +++ b/app/src/renderer/connectors/lcdClientMock.js @@ -913,9 +913,12 @@ module.exports = { }) { let results = [] // get new proposal id - let proposal_id = `0` - if (state.proposals && state.proposals.length > 0) { - proposal_id = String(parseInt(state.proposals[-1].proposal_id) + 1) + let proposal_id = `1` + let proposalsLen = state.proposals.length + if (state.proposals && proposalsLen > 0) { + proposal_id = String( + parseInt(state.proposals[proposalsLen - 1].proposal_id) + 1 + ) } if ( @@ -948,26 +951,26 @@ module.exports = { submit_time, deposit_end_time, voting_start_time: undefined, - voting_end_time: undefined + voting_end_time: undefined, + total_deposit: [] } // we add the proposal to the state to make it available for the submitProposalDeposit function state.proposals.push(proposal) - let res = this.submitProposalDeposit({ + results = await this.submitProposalDeposit({ base_req, + proposal_id, depositer: proposer, - deposit: initial_deposit + amount: initial_deposit }) // remove proposal from state if it fails - if (res[0].check_tx.code !== 0) { + if (results[0].check_tx.code !== 0) { state.proposals.pop() } - return res + return results }, async getProposal(proposalId) { - return state.proposals.find( - proposal => proposal.proposal_id === String(proposalId) - ) + return state.proposals.find(proposal => proposal.proposal_id === proposalId) }, async getProposalDeposits(proposalId) { return state.deposits[proposalId] || [] @@ -1055,12 +1058,15 @@ module.exports = { // ============= USER'S DEPOSITS ============= // check if there's an existing deposit by the depositer - let prevDeposit = state.deposits[proposal_id].find( - deposit => deposit.depositer === depositer - ) + let prevDeposit = + state.deposits[proposal_id] && + state.deposits[proposal_id].find( + deposit => deposit.depositer === depositer + ) if (!prevDeposit) { // if no previous deposit by the depositer, we add it to the existing deposits + if (!state.deposits[proposal_id]) state.deposits[proposal_id] = [] state.deposits[proposal_id].push(submittedDeposit) break // break since no need to iterate over other coins } else { diff --git a/test/unit/specs/components/governance/__snapshots__/TabProposals.spec.js.snap b/test/unit/specs/components/governance/__snapshots__/TabProposals.spec.js.snap index 5e0e48da9c..b33b679f4f 100644 --- a/test/unit/specs/components/governance/__snapshots__/TabProposals.spec.js.snap +++ b/test/unit/specs/components/governance/__snapshots__/TabProposals.spec.js.snap @@ -56,7 +56,7 @@ exports[`TabProposals has the expected html structure 1`] = `

- Active proposal + VotingPeriod proposal

custom text proposal description diff --git a/test/unit/specs/components/governance/__snapshots__/TableProposals.spec.js.snap b/test/unit/specs/components/governance/__snapshots__/TableProposals.spec.js.snap index a7acc580d9..02a2e66c2c 100644 --- a/test/unit/specs/components/governance/__snapshots__/TableProposals.spec.js.snap +++ b/test/unit/specs/components/governance/__snapshots__/TableProposals.spec.js.snap @@ -56,7 +56,7 @@ exports[`TableProposals has the expected html structure 1`] = `

- Active proposal + VotingPeriod proposal

custom text proposal description diff --git a/test/unit/specs/lcdClientMock.spec.js b/test/unit/specs/lcdClientMock.spec.js index f905531052..e6bcb8b87a 100644 --- a/test/unit/specs/lcdClientMock.spec.js +++ b/test/unit/specs/lcdClientMock.spec.js @@ -865,6 +865,119 @@ describe(`LCD Client Mock`, () => { expect(proposalRes).toBeDefined() expect(proposalRes).toEqual(proposals[0]) }) + + describe(`fails to submit a proposal`, () => { + it(`if the type is invalid`, async () => { + let lenBefore = client.state.proposals.length + let res = await client.submitProposal({ + base_req: { + name: `default`, + sequence: 1 + }, + title: `A proposal`, + description: `A description`, + proposal_type: `Other`, + proposer: lcdClientMock.addresses[0], + initial_deposit: [ + { + denom: `stake`, + amount: `1` + } + ] + }) + expect(res).toHaveLength(1) + expect(res[0].check_tx.code).toBe(2) + expect(res[0].check_tx.log).toBe(`Other is not a valid proposal type`) + expect(client.state.proposals).toHaveLength(lenBefore) + }) + + it(`if the deposit is invalid for whatever reason`, async () => { + let lenBefore = client.state.proposals.length + let res = await client.submitProposal({ + base_req: { + name: `default`, + sequence: 1 + }, + title: `A proposal`, + description: `A description`, + proposal_type: `Text`, + proposer: lcdClientMock.addresses[0], + initial_deposit: [ + { + denom: `stake`, + amount: `-1` + } + ] + }) + expect(res).toHaveLength(1) + expect(res[0].check_tx.code).toBe(1) + expect(res[0].check_tx.log).toBe( + `Amount of stakes cannot be negative` + ) + expect(client.state.proposals).toHaveLength(lenBefore) + }) + }) + + describe(`successfuly submits a proposal`, () => { + it(`when there are existing proposals`, async () => { + let lenBefore = client.state.proposals.length + let tailProposal = client.state.proposals[lenBefore - 1] + let proposal = { + title: `A proposal`, + description: `A description`, + proposal_type: `Text` + } + await client.submitProposal({ + base_req: { + name: `default`, + sequence: 1 + }, + proposer: lcdClientMock.addresses[0], + initial_deposit: [ + { + denom: `steak`, // TODO: use stake + amount: `5` + } + ], + ...proposal + }) + expect(client.state.proposals).toHaveLength(lenBefore + 1) + + let newProposalId = String(parseInt(tailProposal.proposal_id) + 1) + let res = await client.getProposal(newProposalId) + expect(res).toBeDefined() + expect(res).toMatchObject(proposal) + expect(res.proposal_id).toEqual(newProposalId) + }) + + it(`when there are no previous proposals`, async () => { + client.state.proposals = [] + let proposal = { + title: `A ParameterChange proposal`, + description: `A description`, + proposal_type: `ParameterChange` + } + await client.submitProposal({ + base_req: { + name: `default`, + sequence: 1 + }, + proposer: lcdClientMock.addresses[0], + initial_deposit: [ + { + denom: `steak`, // TODO: use stake + amount: `9` + } + ], + ...proposal + }) + expect(client.state.proposals).toHaveLength(1) + let res = await client.getProposal(`1`) + expect(res).toBeDefined() + expect(res).toMatchObject(proposal) + expect(res.proposal_id).toEqual(`1`) + }) + }) }) describe(`Deposits`, () => { @@ -1164,110 +1277,117 @@ describe(`LCD Client Mock`, () => { expect(voteRes).toEqual(votes[`1`][0]) }) - it(`votes successfully on an active proposal`, async () => { - let option = `no_with_veto` - let proposalBefore = await client.getProposal(`2`) - let optionBefore = proposalBefore.tally_result[option] - - await client.submitProposalVote({ - base_req: { - name: `default`, - sequence: 1 - }, - proposal_id: `2`, - option: option, - voter: lcdClientMock.addresses[0] - }) - - let res = await client.getProposalVote(`2`, lcdClientMock.addresses[0]) - expect(res).toBeDefined() - expect(res.option).toEqual(option) - - // check if the tally was updated - let proposalAfter = await client.getProposal(`2`) - let optionAfter = proposalAfter.tally_result[option] - expect(optionAfter).toEqual(String(parseInt(optionBefore) + 1)) - }) + describe(`fails to vote on a proposal`, () => { + it(`if account is nonexistent`, async () => { + client.state.keys.push({ + name: `nonexistent_account`, + password: `1234567890`, + address: lcdClientMock.addresses[1] + }) - it(`errors when voting with nonexistent account`, async () => { - client.state.keys.push({ - name: `nonexistent_account`, - password: `1234567890`, - address: lcdClientMock.addresses[1] + let res = await client.submitProposalVote({ + base_req: { + name: `nonexistent_account`, + sequence: 1 + }, + proposal_id: `2`, + option: `abstain`, + voter: lcdClientMock.addresses[0] + }) + expect(res.length).toBe(1) + expect(res[0].check_tx.log).toBe(`Nonexistent account`) + expect(res[0].check_tx.code).toBe(1) }) - let res = await client.submitProposalVote({ - base_req: { - name: `nonexistent_account`, - sequence: 1 - }, - proposal_id: `2`, - option: `abstain`, - voter: lcdClientMock.addresses[0] + it(`if sequence is invalid`, async () => { + let res = await client.submitProposalVote({ + base_req: { + name: `default`, + sequence: 0 + }, + proposal_id: `2`, + option: `yes`, + voter: lcdClientMock.addresses[0] + }) + expect(res.length).toBe(1) + expect(res[0].check_tx.code).toBe(2) + expect(res[0].check_tx.log).toBe(`Expected sequence "1", got "0"`) }) - expect(res.length).toBe(1) - expect(res[0].check_tx.log).toBe(`Nonexistent account`) - expect(res[0].check_tx.code).toBe(1) - }) - it(`fails to vote if sequence is invalid`, async () => { - let res = await client.submitProposalVote({ - base_req: { - name: `default`, - sequence: 0 - }, - proposal_id: `2`, - option: `yes`, - voter: lcdClientMock.addresses[0] + it(`if proposal is invalid`, async () => { + let res = await client.submitProposalVote({ + base_req: { + name: `default`, + sequence: 1 + }, + proposal_id: `17`, + option: `yes`, + voter: lcdClientMock.addresses[0] + }) + expect(res.length).toBe(1) + expect(res[0].check_tx.code).toBe(3) + expect(res[0].check_tx.log).toBe(`Nonexistent proposal`) }) - expect(res.length).toBe(1) - expect(res[0].check_tx.code).toBe(2) - expect(res[0].check_tx.log).toBe(`Expected sequence "1", got "0"`) - }) - it(`fails to vote on an invalid proposal`, async () => { - let res = await client.submitProposalVote({ - base_req: { - name: `default`, - sequence: 1 - }, - proposal_id: `17`, - option: `yes`, - voter: lcdClientMock.addresses[0] + it(`if proposal is inactive`, async () => { + let res = await client.submitProposalVote({ + base_req: { + name: `default`, + sequence: 1 + }, + proposal_id: `1`, + option: `yes`, + voter: lcdClientMock.addresses[0] + }) + expect(res.length).toBe(1) + expect(res[0].check_tx.code).toBe(3) + expect(res[0].check_tx.log).toBe(`Proposal #1 is inactive`) }) - expect(res.length).toBe(1) - expect(res[0].check_tx.code).toBe(3) - expect(res[0].check_tx.log).toBe(`Nonexistent proposal`) - }) - it(`fails to vote on an inactive proposal`, async () => { - let res = await client.submitProposalVote({ - base_req: { - name: `default`, - sequence: 1 - }, - proposal_id: `1`, - option: `yes`, - voter: lcdClientMock.addresses[0] + it(`if the selected option is invalid`, async () => { + let res = await client.submitProposalVote({ + base_req: { + name: `default`, + sequence: 1 + }, + proposal_id: `2`, + option: `other`, + voter: lcdClientMock.addresses[0] + }) + expect(res.length).toBe(1) + expect(res[0].check_tx.code).toBe(3) + expect(res[0].check_tx.log).toBe(`Invalid option 'other'`) }) - expect(res.length).toBe(1) - expect(res[0].check_tx.code).toBe(3) - expect(res[0].check_tx.log).toBe(`Proposal #1 is inactive`) }) - it(`fails to vote if the selected option is invalid`, async () => { - let res = await client.submitProposalVote({ - base_req: { - name: `default`, - sequence: 1 - }, - proposal_id: `2`, - option: `other`, - voter: lcdClientMock.addresses[0] + describe(`votes successfully on a proposal`, () => { + it(`if proposal is in 'VotingPeriod'`, async () => { + let option = `no_with_veto` + let proposalBefore = await client.getProposal(`2`) + let optionBefore = proposalBefore.tally_result[option] + + await client.submitProposalVote({ + base_req: { + name: `default`, + sequence: 1 + }, + proposal_id: `2`, + option: option, + voter: lcdClientMock.addresses[0] + }) + + let res = await client.getProposalVote( + `2`, + lcdClientMock.addresses[0] + ) + expect(res).toBeDefined() + expect(res.option).toEqual(option) + + // check if the tally was updated + let proposalAfter = await client.getProposal(`2`) + let optionAfter = proposalAfter.tally_result[option] + expect(optionAfter).toEqual(String(parseInt(optionBefore) + 1)) }) - expect(res.length).toBe(1) - expect(res[0].check_tx.code).toBe(3) - expect(res[0].check_tx.log).toBe(`Invalid option 'other'`) }) }) From fab2b4c07c98a33230e5a00dedca2804b1e20c49 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 13 Nov 2018 19:09:56 +0100 Subject: [PATCH 3/3] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 585453182c..681dbd3827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * [\1502](https://github.com/cosmos/voyager/issues/1502) A page for each proposal. @jbibla * [\1548](https://github.com/cosmos/voyager/issues/1548) Add mocked deposit for testing @fedekunze * [\1116](https://github.com/cosmos/voyager/issues/1116) Elaborate a bit about the release process. @NodeGuy +* [\1568](https://github.com/cosmos/voyager/issues/1568) Add mocked submit deposit for testing @fedekunze ### Changed