Browse Source

Cleanup the structure of GovernorTimelockControl.test.js (#4302)

Hadrien Croubois 2 years ago
parent
commit
7cc2cbfeb5
1 changed files with 262 additions and 292 deletions
  1. 262 292
      test/governance/extensions/GovernorTimelockControl.test.js

+ 262 - 292
test/governance/extensions/GovernorTimelockControl.test.js

@@ -136,323 +136,204 @@ contract('GovernorTimelockControl', function (accounts) {
             await this.helper.propose();
             await this.helper.waitForSnapshot();
             await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 });
-            await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 });
-            await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 });
             await this.helper.waitForDeadline();
-            const txQueue = await this.helper.queue();
-            await this.helper.waitForEta();
-            const txExecute = await this.helper.execute();
+            await this.helper.queue();
+            await expectRevertCustomError(this.helper.queue(), 'GovernorUnexpectedProposalState', [
+              this.proposal.id,
+              Enums.ProposalState.Queued,
+              proposalStatesToBitMap([Enums.ProposalState.Succeeded]),
+            ]);
+          });
+        });
 
-            expectEvent(txQueue, 'ProposalQueued', { proposalId: this.proposal.id });
-            await expectEvent.inTransaction(txQueue.tx, this.timelock, 'CallScheduled', {
-              id: this.proposal.timelockid,
-            });
+        describe('on execute', function () {
+          it('if not queued', async function () {
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline(+1);
 
-            expectEvent(txExecute, 'ProposalExecuted', { proposalId: this.proposal.id });
-            await expectEvent.inTransaction(txExecute.tx, this.timelock, 'CallExecuted', {
-              id: this.proposal.timelockid,
-            });
-            await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled');
+            expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
+
+            await expectRevertCustomError(this.helper.execute(), 'TimelockUnexpectedOperationState', [
+              this.proposal.timelockid,
+              Enums.OperationState.Ready,
+            ]);
           });
 
-          describe('should revert', function () {
-            describe('on queue', function () {
-              it('if already queued', async function () {
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await expectRevertCustomError(this.helper.queue(), 'GovernorUnexpectedProposalState', [
-                  this.proposal.id,
-                  Enums.ProposalState.Queued,
-                  proposalStatesToBitMap([Enums.ProposalState.Succeeded]),
-                ]);
-              });
-            });
+          it('if too early', async function () {
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline();
+            await this.helper.queue();
 
-            describe('on execute', function () {
-              it('if not queued', async function () {
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline(+1);
-
-                expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded);
-
-                await expectRevertCustomError(this.helper.execute(), 'TimelockUnexpectedOperationState', [
-                  this.proposal.timelockid,
-                  Enums.OperationState.Ready,
-                ]);
-              });
-
-              it('if too early', async function () {
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-
-                expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
-
-                await expectRevertCustomError(this.helper.execute(), 'TimelockUnexpectedOperationState', [
-                  this.proposal.timelockid,
-                  Enums.OperationState.Ready,
-                ]);
-              });
-
-              it('if already executed', async function () {
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await this.helper.waitForEta();
-                await this.helper.execute();
-                await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
-                  this.proposal.id,
-                  Enums.ProposalState.Executed,
-                  proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
-                ]);
-              });
-
-              it('if already executed by another proposer', async function () {
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await this.helper.waitForEta();
-
-                await this.timelock.executeBatch(
-                  ...this.proposal.shortProposal.slice(0, 3),
-                  '0x0',
-                  this.proposal.shortProposal[3],
-                );
-
-                await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
-                  this.proposal.id,
-                  Enums.ProposalState.Executed,
-                  proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
-                ]);
-              });
-            });
+            expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
+
+            await expectRevertCustomError(this.helper.execute(), 'TimelockUnexpectedOperationState', [
+              this.proposal.timelockid,
+              Enums.OperationState.Ready,
+            ]);
           });
 
-          describe('cancel', function () {
-            it('cancel before queue prevents scheduling', async function () {
-              await this.helper.propose();
-              await this.helper.waitForSnapshot();
-              await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-              await this.helper.waitForDeadline();
-
-              expectEvent(await this.helper.cancel('internal'), 'ProposalCanceled', { proposalId: this.proposal.id });
-
-              expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
-              await expectRevertCustomError(this.helper.queue(), 'GovernorUnexpectedProposalState', [
-                this.proposal.id,
-                Enums.ProposalState.Canceled,
-                proposalStatesToBitMap([Enums.ProposalState.Succeeded]),
-              ]);
-            });
+          it('if already executed', async function () {
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline();
+            await this.helper.queue();
+            await this.helper.waitForEta();
+            await this.helper.execute();
+            await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
+              this.proposal.id,
+              Enums.ProposalState.Executed,
+              proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
+            ]);
+          });
 
-            it('cancel after queue prevents executing', async function () {
-              await this.helper.propose();
-              await this.helper.waitForSnapshot();
-              await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-              await this.helper.waitForDeadline();
-              await this.helper.queue();
-
-              expectEvent(await this.helper.cancel('internal'), 'ProposalCanceled', { proposalId: this.proposal.id });
-
-              expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
-              await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
-                this.proposal.id,
-                Enums.ProposalState.Canceled,
-                proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
-              ]);
-            });
+          it('if already executed by another proposer', async function () {
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline();
+            await this.helper.queue();
+            await this.helper.waitForEta();
+
+            await this.timelock.executeBatch(
+              ...this.proposal.shortProposal.slice(0, 3),
+              '0x0',
+              this.proposal.shortProposal[3],
+            );
+
+            await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
+              this.proposal.id,
+              Enums.ProposalState.Executed,
+              proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
+            ]);
+          });
+        });
+      });
+
+      describe('cancel', function () {
+        it('cancel before queue prevents scheduling', async function () {
+          await this.helper.propose();
+          await this.helper.waitForSnapshot();
+          await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+          await this.helper.waitForDeadline();
+
+          expectEvent(await this.helper.cancel('internal'), 'ProposalCanceled', { proposalId: this.proposal.id });
+
+          expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
+          await expectRevertCustomError(this.helper.queue(), 'GovernorUnexpectedProposalState', [
+            this.proposal.id,
+            Enums.ProposalState.Canceled,
+            proposalStatesToBitMap([Enums.ProposalState.Succeeded]),
+          ]);
+        });
 
-            it('cancel on timelock is reflected on governor', async function () {
-              await this.helper.propose();
-              await this.helper.waitForSnapshot();
-              await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-              await this.helper.waitForDeadline();
-              await this.helper.queue();
+        it('cancel after queue prevents executing', async function () {
+          await this.helper.propose();
+          await this.helper.waitForSnapshot();
+          await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+          await this.helper.waitForDeadline();
+          await this.helper.queue();
+
+          expectEvent(await this.helper.cancel('internal'), 'ProposalCanceled', { proposalId: this.proposal.id });
+
+          expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
+          await expectRevertCustomError(this.helper.execute(), 'GovernorUnexpectedProposalState', [
+            this.proposal.id,
+            Enums.ProposalState.Canceled,
+            proposalStatesToBitMap([Enums.ProposalState.Succeeded, Enums.ProposalState.Queued]),
+          ]);
+        });
 
-              expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
+        it('cancel on timelock is reflected on governor', async function () {
+          await this.helper.propose();
+          await this.helper.waitForSnapshot();
+          await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+          await this.helper.waitForDeadline();
+          await this.helper.queue();
 
-              expectEvent(await this.timelock.cancel(this.proposal.timelockid, { from: owner }), 'Cancelled', {
-                id: this.proposal.timelockid,
-              });
+          expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued);
 
-              expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
-            });
+          expectEvent(await this.timelock.cancel(this.proposal.timelockid, { from: owner }), 'Cancelled', {
+            id: this.proposal.timelockid,
           });
 
-          describe('onlyGovernance', function () {
-            describe('relay', function () {
-              beforeEach(async function () {
-                await this.token.$_mint(this.mock.address, 1);
-              });
-
-              it('is protected', async function () {
-                await expectRevertCustomError(
-                  this.mock.relay(this.token.address, 0, this.token.contract.methods.transfer(other, 1).encodeABI(), {
-                    from: owner,
-                  }),
-                  'GovernorOnlyExecutor',
-                  [owner],
-                );
-              });
-
-              it('can be executed through governance', async function () {
-                this.helper.setProposal(
-                  [
-                    {
-                      target: this.mock.address,
-                      data: this.mock.contract.methods
-                        .relay(this.token.address, 0, this.token.contract.methods.transfer(other, 1).encodeABI())
-                        .encodeABI(),
-                    },
-                  ],
-                  '<proposal description>',
-                );
-
-                expect(await this.token.balanceOf(this.mock.address), 1);
-                expect(await this.token.balanceOf(other), 0);
-
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await this.helper.waitForEta();
-                const txExecute = await this.helper.execute();
-
-                expect(await this.token.balanceOf(this.mock.address), 0);
-                expect(await this.token.balanceOf(other), 1);
-
-                await expectEvent.inTransaction(txExecute.tx, this.token, 'Transfer', {
-                  from: this.mock.address,
-                  to: other,
-                  value: '1',
-                });
-              });
-
-              it('is payable and can transfer eth to EOA', async function () {
-                const t2g = web3.utils.toBN(128); // timelock to governor
-                const g2o = web3.utils.toBN(100); // governor to eoa (other)
-
-                this.helper.setProposal(
-                  [
-                    {
-                      target: this.mock.address,
-                      value: t2g,
-                      data: this.mock.contract.methods.relay(other, g2o, '0x').encodeABI(),
-                    },
-                  ],
-                  '<proposal description>',
-                );
-
-                expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
-                const timelockBalance = await web3.eth.getBalance(this.timelock.address).then(web3.utils.toBN);
-                const otherBalance = await web3.eth.getBalance(other).then(web3.utils.toBN);
-
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await this.helper.waitForEta();
-                await this.helper.execute();
-
-                expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(
-                  timelockBalance.sub(t2g),
-                );
-                expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(t2g.sub(g2o));
-                expect(await web3.eth.getBalance(other)).to.be.bignumber.equal(otherBalance.add(g2o));
-              });
-
-              it('protected against other proposers', async function () {
-                const target = this.mock.address;
-                const value = web3.utils.toWei('0');
-                const data = this.mock.contract.methods.relay(constants.ZERO_ADDRESS, 0, '0x').encodeABI();
-                const predecessor = constants.ZERO_BYTES32;
-                const salt = constants.ZERO_BYTES32;
-                const delay = 3600;
-
-                await this.timelock.schedule(target, value, data, predecessor, salt, delay, { from: owner });
-
-                await time.increase(3600);
-
-                await expectRevertCustomError(
-                  this.timelock.execute(target, value, data, predecessor, salt, { from: owner }),
-                  'QueueEmpty', // Bubbled up from Governor
-                  [],
-                );
-              });
-            });
+          expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled);
+        });
+      });
+
+      describe('onlyGovernance', function () {
+        describe('relay', function () {
+          beforeEach(async function () {
+            await this.token.$_mint(this.mock.address, 1);
+          });
+
+          it('is protected', async function () {
+            await expectRevertCustomError(
+              this.mock.relay(this.token.address, 0, this.token.contract.methods.transfer(other, 1).encodeABI(), {
+                from: owner,
+              }),
+              'GovernorOnlyExecutor',
+              [owner],
+            );
+          });
+
+          it('can be executed through governance', async function () {
+            this.helper.setProposal(
+              [
+                {
+                  target: this.mock.address,
+                  data: this.mock.contract.methods
+                    .relay(this.token.address, 0, this.token.contract.methods.transfer(other, 1).encodeABI())
+                    .encodeABI(),
+                },
+              ],
+              '<proposal description>',
+            );
 
-            describe('updateTimelock', function () {
-              beforeEach(async function () {
-                this.newTimelock = await Timelock.new(
-                  3600,
-                  [this.mock.address],
-                  [this.mock.address],
-                  constants.ZERO_ADDRESS,
-                );
-              });
-
-              it('is protected', async function () {
-                await expectRevertCustomError(
-                  this.mock.updateTimelock(this.newTimelock.address, { from: owner }),
-                  'GovernorOnlyExecutor',
-                  [owner],
-                );
-              });
-
-              it('can be executed through governance to', async function () {
-                this.helper.setProposal(
-                  [
-                    {
-                      target: this.mock.address,
-                      data: this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI(),
-                    },
-                  ],
-                  '<proposal description>',
-                );
-
-                await this.helper.propose();
-                await this.helper.waitForSnapshot();
-                await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
-                await this.helper.waitForDeadline();
-                await this.helper.queue();
-                await this.helper.waitForEta();
-                const txExecute = await this.helper.execute();
-
-                expectEvent(txExecute, 'TimelockChange', {
-                  oldTimelock: this.timelock.address,
-                  newTimelock: this.newTimelock.address,
-                });
-
-                expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address);
-              });
+            expect(await this.token.balanceOf(this.mock.address), 1);
+            expect(await this.token.balanceOf(other), 0);
+
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline();
+            await this.helper.queue();
+            await this.helper.waitForEta();
+            const txExecute = await this.helper.execute();
+
+            expect(await this.token.balanceOf(this.mock.address), 0);
+            expect(await this.token.balanceOf(other), 1);
+
+            await expectEvent.inTransaction(txExecute.tx, this.token, 'Transfer', {
+              from: this.mock.address,
+              to: other,
+              value: '1',
             });
           });
 
-          it('clear queue of pending governor calls', async function () {
+          it('is payable and can transfer eth to EOA', async function () {
+            const t2g = web3.utils.toBN(128); // timelock to governor
+            const g2o = web3.utils.toBN(100); // governor to eoa (other)
+
             this.helper.setProposal(
               [
                 {
                   target: this.mock.address,
-                  data: this.mock.contract.methods.nonGovernanceFunction().encodeABI(),
+                  value: t2g,
+                  data: this.mock.contract.methods.relay(other, g2o, '0x').encodeABI(),
                 },
               ],
               '<proposal description>',
             );
 
+            expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
+            const timelockBalance = await web3.eth.getBalance(this.timelock.address).then(web3.utils.toBN);
+            const otherBalance = await web3.eth.getBalance(other).then(web3.utils.toBN);
+
             await this.helper.propose();
             await this.helper.waitForSnapshot();
             await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
@@ -461,11 +342,100 @@ contract('GovernorTimelockControl', function (accounts) {
             await this.helper.waitForEta();
             await this.helper.execute();
 
-            // This path clears _governanceCall as part of the afterExecute call,
-            // but we have not way to check that the cleanup actually happened other
-            // then coverage reports.
+            expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(timelockBalance.sub(t2g));
+            expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(t2g.sub(g2o));
+            expect(await web3.eth.getBalance(other)).to.be.bignumber.equal(otherBalance.add(g2o));
+          });
+
+          it('protected against other proposers', async function () {
+            const target = this.mock.address;
+            const value = web3.utils.toWei('0');
+            const data = this.mock.contract.methods.relay(constants.ZERO_ADDRESS, 0, '0x').encodeABI();
+            const predecessor = constants.ZERO_BYTES32;
+            const salt = constants.ZERO_BYTES32;
+            const delay = 3600;
+
+            await this.timelock.schedule(target, value, data, predecessor, salt, delay, { from: owner });
+
+            await time.increase(3600);
+
+            await expectRevertCustomError(
+              this.timelock.execute(target, value, data, predecessor, salt, { from: owner }),
+              'QueueEmpty', // Bubbled up from Governor
+              [],
+            );
           });
         });
+
+        describe('updateTimelock', function () {
+          beforeEach(async function () {
+            this.newTimelock = await Timelock.new(
+              3600,
+              [this.mock.address],
+              [this.mock.address],
+              constants.ZERO_ADDRESS,
+            );
+          });
+
+          it('is protected', async function () {
+            await expectRevertCustomError(
+              this.mock.updateTimelock(this.newTimelock.address, { from: owner }),
+              'GovernorOnlyExecutor',
+              [owner],
+            );
+          });
+
+          it('can be executed through governance to', async function () {
+            this.helper.setProposal(
+              [
+                {
+                  target: this.mock.address,
+                  data: this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI(),
+                },
+              ],
+              '<proposal description>',
+            );
+
+            await this.helper.propose();
+            await this.helper.waitForSnapshot();
+            await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+            await this.helper.waitForDeadline();
+            await this.helper.queue();
+            await this.helper.waitForEta();
+            const txExecute = await this.helper.execute();
+
+            expectEvent(txExecute, 'TimelockChange', {
+              oldTimelock: this.timelock.address,
+              newTimelock: this.newTimelock.address,
+            });
+
+            expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address);
+          });
+        });
+      });
+
+      it('clear queue of pending governor calls', async function () {
+        this.helper.setProposal(
+          [
+            {
+              target: this.mock.address,
+              data: this.mock.contract.methods.nonGovernanceFunction().encodeABI(),
+            },
+          ],
+          '<proposal description>',
+        );
+
+        await this.helper.propose();
+        await this.helper.waitForSnapshot();
+        await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 });
+        await this.helper.waitForDeadline();
+        await this.helper.queue();
+        await this.helper.waitForEta();
+        await this.helper.execute();
+
+        // This path clears _governanceCall as part of the afterExecute call,
+        // but we have not way to check that the cleanup actually happened other
+        // then coverage reports.
       });
     });
   }