diff --git a/contracts/staking/test/unit_tests/authorizable.ts b/contracts/staking/test/unit_tests/authorizable.ts new file mode 100644 index 0000000000..3c99acec50 --- /dev/null +++ b/contracts/staking/test/unit_tests/authorizable.ts @@ -0,0 +1,88 @@ +import { blockchainTests, constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils'; +import { + AuthorizableAuthorizedAddressAddedEventArgs, + AuthorizableAuthorizedAddressRemovedEventArgs, + OwnableRevertErrors, +} from '@0x/contracts-utils'; + +import { artifacts, TestStakingContract, TestStakingEvents } from '../../src'; + +blockchainTests.resets('Staking Authorization Tests', env => { + let testContract: TestStakingContract; + + let owner: string; + let nonOwner: string; + + before(async () => { + [owner, nonOwner] = await env.getAccountAddressesAsync(); + + testContract = await TestStakingContract.deployFrom0xArtifactAsync( + artifacts.TestStaking, + env.provider, + { + ...env.txDefaults, + from: owner, + }, + artifacts, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + ); + }); + + it("shouldn't have any authorized addresses initially", async () => { + const authorities = await testContract.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([]); + }); + + describe('addAuthorizedAddress', () => { + it('should allow owner to add authorized address', async () => { + const receipt = await testContract + .addAuthorizedAddress(nonOwner) + .awaitTransactionSuccessAsync({ from: owner }); + + const args = filterLogsToArguments( + receipt.logs, + TestStakingEvents.AuthorizedAddressAdded, + ); + expect(args).to.be.deep.eq([{ target: nonOwner, caller: owner }]); + + const authorities = await testContract.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([nonOwner]); + }); + + it('should throw if non-owner adds authorized address', async () => { + const tx = testContract.addAuthorizedAddress(owner).awaitTransactionSuccessAsync({ from: nonOwner }); + const expectedError = new OwnableRevertErrors.OnlyOwnerError(nonOwner, owner); + return expect(tx).to.revertWith(expectedError); + }); + }); + + describe('removeAuthorizedAddress', () => { + before(async () => { + await testContract.addAuthorizedAddress(owner).awaitTransactionSuccessAsync({ from: owner }); + const authorities = await testContract.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([owner]); + }); + + it('should allow owner to remove authorized address', async () => { + const receipt = await testContract + .removeAuthorizedAddress(owner) + .awaitTransactionSuccessAsync({ from: owner }); + + const args = filterLogsToArguments( + receipt.logs, + TestStakingEvents.AuthorizedAddressRemoved, + ); + expect(args).to.be.deep.eq([{ target: owner, caller: owner }]); + + const authorities = await testContract.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([]); + }); + + it('should throw if non-owner removes authorized address', async () => { + const tx = testContract.removeAuthorizedAddress(owner).awaitTransactionSuccessAsync({ from: nonOwner }); + const expectedError = new OwnableRevertErrors.OnlyOwnerError(nonOwner, owner); + return expect(tx).to.revertWith(expectedError); + }); + }); +}); diff --git a/contracts/utils/contracts/test/TestAuthorizable.sol b/contracts/utils/contracts/test/TestAuthorizable.sol new file mode 100644 index 0000000000..366a4b4274 --- /dev/null +++ b/contracts/utils/contracts/test/TestAuthorizable.sol @@ -0,0 +1,33 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; + +import "../src/Authorizable.sol"; + + +// solhint-disable no-empty-blocks +contract TestAuthorizable is + Authorizable +{ + function onlyAuthorizedFn() + external + view + onlyAuthorized + {} +} diff --git a/contracts/utils/package.json b/contracts/utils/package.json index 94678d37ab..0a8f0c6271 100644 --- a/contracts/utils/package.json +++ b/contracts/utils/package.json @@ -38,7 +38,7 @@ "config": { "publicInterfaceContracts": "Authorizable,IAuthorizable,IOwnable,LibAddress,LibAddressArray,LibAddressArrayRichErrors,LibAuthorizableRichErrors,LibBytes,LibBytesRichErrors,LibEIP1271,LibEIP712,LibFractions,LibOwnableRichErrors,LibReentrancyGuardRichErrors,LibRichErrors,LibSafeMath,LibSafeMathRichErrors,Ownable,ReentrancyGuard,Refundable", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(Authorizable|DeploymentConstants|IAuthorizable|IOwnable|LibAddress|LibAddressArray|LibAddressArrayRichErrors|LibAuthorizableRichErrors|LibBytes|LibBytesRichErrors|LibEIP1271|LibEIP712|LibFractions|LibOwnableRichErrors|LibReentrancyGuardRichErrors|LibRichErrors|LibSafeMath|LibSafeMathRichErrors|Ownable|ReentrancyGuard|Refundable|TestLibAddress|TestLibAddressArray|TestLibBytes|TestLibEIP712|TestLibRichErrors|TestLibSafeMath|TestLogDecoding|TestLogDecodingDownstream|TestOwnable|TestReentrancyGuard|TestRefundable|TestRefundableReceiver).json" + "abis": "./test/generated-artifacts/@(Authorizable|DeploymentConstants|IAuthorizable|IOwnable|LibAddress|LibAddressArray|LibAddressArrayRichErrors|LibAuthorizableRichErrors|LibBytes|LibBytesRichErrors|LibEIP1271|LibEIP712|LibFractions|LibOwnableRichErrors|LibReentrancyGuardRichErrors|LibRichErrors|LibSafeMath|LibSafeMathRichErrors|Ownable|ReentrancyGuard|Refundable|TestAuthorizable|TestLibAddress|TestLibAddressArray|TestLibBytes|TestLibEIP712|TestLibRichErrors|TestLibSafeMath|TestLogDecoding|TestLogDecodingDownstream|TestOwnable|TestReentrancyGuard|TestRefundable|TestRefundableReceiver).json" }, "repository": { "type": "git", diff --git a/contracts/utils/test/artifacts.ts b/contracts/utils/test/artifacts.ts index 740accd100..242bbc40ef 100644 --- a/contracts/utils/test/artifacts.ts +++ b/contracts/utils/test/artifacts.ts @@ -26,6 +26,7 @@ import * as LibSafeMathRichErrors from '../test/generated-artifacts/LibSafeMathR import * as Ownable from '../test/generated-artifacts/Ownable.json'; import * as ReentrancyGuard from '../test/generated-artifacts/ReentrancyGuard.json'; import * as Refundable from '../test/generated-artifacts/Refundable.json'; +import * as TestAuthorizable from '../test/generated-artifacts/TestAuthorizable.json'; import * as TestLibAddress from '../test/generated-artifacts/TestLibAddress.json'; import * as TestLibAddressArray from '../test/generated-artifacts/TestLibAddressArray.json'; import * as TestLibBytes from '../test/generated-artifacts/TestLibBytes.json'; @@ -60,6 +61,7 @@ export const artifacts = { Refundable: Refundable as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact, IOwnable: IOwnable as ContractArtifact, + TestAuthorizable: TestAuthorizable as ContractArtifact, TestLibAddress: TestLibAddress as ContractArtifact, TestLibAddressArray: TestLibAddressArray as ContractArtifact, TestLibBytes: TestLibBytes as ContractArtifact, diff --git a/contracts/utils/test/authorizable.ts b/contracts/utils/test/authorizable.ts index 26a71b337e..60850ada5b 100644 --- a/contracts/utils/test/authorizable.ts +++ b/contracts/utils/test/authorizable.ts @@ -1,52 +1,30 @@ -import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; +import { blockchainTests, constants, expect } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; import * as _ from 'lodash'; import AuthorizableRevertErrors = require('../src/authorizable_revert_errors'); import OwnableRevertErrors = require('../src/ownable_revert_errors'); import { artifacts } from './artifacts'; -import { AuthorizableContract } from './wrappers'; +import { TestAuthorizableContract } from './wrappers'; -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Authorizable', () => { +blockchainTests.resets('Authorizable', env => { let owner: string; let notOwner: string; let address: string; - let authorizable: AuthorizableContract; + let authorizable: TestAuthorizableContract; before(async () => { - await blockchainLifecycle.startAsync(); - }); - - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const accounts = await env.getAccountAddressesAsync(); [owner, address, notOwner] = _.slice(accounts, 0, 3); - authorizable = await AuthorizableContract.deployFrom0xArtifactAsync( - artifacts.Authorizable, - provider, - txDefaults, - {}, + authorizable = await TestAuthorizableContract.deployFrom0xArtifactAsync( + artifacts.TestAuthorizable, + env.provider, + env.txDefaults, + artifacts, ); }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('addAuthorizedAddress', () => { it('should revert if not called by owner', async () => { const expectedError = new OwnableRevertErrors.OnlyOwnerError(notOwner, owner); @@ -74,6 +52,26 @@ describe('Authorizable', () => { }); }); + describe('onlyAuthorized', () => { + before(async () => { + await authorizable.addAuthorizedAddress(owner).awaitTransactionSuccessAsync({ from: owner }); + }); + + after(async () => { + await authorizable.removeAuthorizedAddress(owner).awaitTransactionSuccessAsync({ from: owner }); + }); + + it('should revert if sender is not authorized', async () => { + const tx = authorizable.onlyAuthorizedFn().callAsync({ from: notOwner }); + const expectedError = new AuthorizableRevertErrors.SenderNotAuthorizedError(notOwner); + return expect(tx).to.revertWith(expectedError); + }); + + it('should succeed if sender is authorized', async () => { + await authorizable.onlyAuthorizedFn().callAsync({ from: owner }); + }); + }); + describe('removeAuthorizedAddress', () => { it('should revert if not called by owner', async () => { await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); @@ -151,16 +149,20 @@ describe('Authorizable', () => { }); describe('getAuthorizedAddresses', () => { - it('should return all authorized addresses', async () => { - const initial = await authorizable.getAuthorizedAddresses().callAsync(); - expect(initial).to.have.length(0); + it('should return correct authorized addresses', async () => { + // Initial Authorities + let authorities = await authorizable.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([]); + + // Authorities after addition await authorizable.addAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); - const afterAdd = await authorizable.getAuthorizedAddresses().callAsync(); - expect(afterAdd).to.have.length(1); - expect(afterAdd).to.include(address); + authorities = await authorizable.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([address]); + + // Authorities after removal await authorizable.removeAuthorizedAddress(address).awaitTransactionSuccessAsync({ from: owner }); - const afterRemove = await authorizable.getAuthorizedAddresses().callAsync(); - expect(afterRemove).to.have.length(0); + authorities = await authorizable.getAuthorizedAddresses().callAsync(); + expect(authorities).to.be.deep.eq([]); }); }); }); diff --git a/contracts/utils/test/wrappers.ts b/contracts/utils/test/wrappers.ts index fa6efefa66..72acfbdd5c 100644 --- a/contracts/utils/test/wrappers.ts +++ b/contracts/utils/test/wrappers.ts @@ -24,6 +24,7 @@ export * from '../test/generated-wrappers/lib_safe_math_rich_errors'; export * from '../test/generated-wrappers/ownable'; export * from '../test/generated-wrappers/reentrancy_guard'; export * from '../test/generated-wrappers/refundable'; +export * from '../test/generated-wrappers/test_authorizable'; export * from '../test/generated-wrappers/test_lib_address'; export * from '../test/generated-wrappers/test_lib_address_array'; export * from '../test/generated-wrappers/test_lib_bytes'; diff --git a/contracts/utils/tsconfig.json b/contracts/utils/tsconfig.json index fd68dbba9e..36dbaf8e67 100644 --- a/contracts/utils/tsconfig.json +++ b/contracts/utils/tsconfig.json @@ -44,6 +44,7 @@ "test/generated-artifacts/Ownable.json", "test/generated-artifacts/ReentrancyGuard.json", "test/generated-artifacts/Refundable.json", + "test/generated-artifacts/TestAuthorizable.json", "test/generated-artifacts/TestLibAddress.json", "test/generated-artifacts/TestLibAddressArray.json", "test/generated-artifacts/TestLibBytes.json",