const { accounts, contract } = require('@openzeppelin/test-environment');

const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');

const { expect } = require('chai');

const PausableMock = contract.fromArtifact('PausableMockUpgradeSafe');

describe('Pausable', function () {
  const [ pauser ] = accounts;

  beforeEach(async function () {
    this.pausable = await PausableMock.new();
  });

  context('when unpaused', function () {
    beforeEach(async function () {
      expect(await this.pausable.paused()).to.equal(false);
    });

    it('can perform normal process in non-pause', async function () {
      expect(await this.pausable.count()).to.be.bignumber.equal('0');

      await this.pausable.normalProcess();
      expect(await this.pausable.count()).to.be.bignumber.equal('1');
    });

    it('cannot take drastic measure in non-pause', async function () {
      await expectRevert(this.pausable.drasticMeasure(),
        'Pausable: not paused'
      );
      expect(await this.pausable.drasticMeasureTaken()).to.equal(false);
    });

    context('when paused', function () {
      beforeEach(async function () {
        ({ logs: this.logs } = await this.pausable.pause({ from: pauser }));
      });

      it('emits a Paused event', function () {
        expectEvent.inLogs(this.logs, 'Paused', { account: pauser });
      });

      it('cannot perform normal process in pause', async function () {
        await expectRevert(this.pausable.normalProcess(), 'Pausable: paused');
      });

      it('can take a drastic measure in a pause', async function () {
        await this.pausable.drasticMeasure();
        expect(await this.pausable.drasticMeasureTaken()).to.equal(true);
      });

      it('reverts when re-pausing', async function () {
        await expectRevert(this.pausable.pause(), 'Pausable: paused');
      });

      describe('unpausing', function () {
        it('is unpausable by the pauser', async function () {
          await this.pausable.unpause();
          expect(await this.pausable.paused()).to.equal(false);
        });

        context('when unpaused', function () {
          beforeEach(async function () {
            ({ logs: this.logs } = await this.pausable.unpause({ from: pauser }));
          });

          it('emits an Unpaused event', function () {
            expectEvent.inLogs(this.logs, 'Unpaused', { account: pauser });
          });

          it('should resume allowing normal process', async function () {
            expect(await this.pausable.count()).to.be.bignumber.equal('0');
            await this.pausable.normalProcess();
            expect(await this.pausable.count()).to.be.bignumber.equal('1');
          });

          it('should prevent drastic measure', async function () {
            await expectRevert(this.pausable.drasticMeasure(),
              'Pausable: not paused'
            );
          });

          it('reverts when re-unpausing', async function () {
            await expectRevert(this.pausable.unpause(), 'Pausable: not paused');
          });
        });
      });
    });
  });
});
