From 3b8779456ed844f381261f874ecad813978a69c2 Mon Sep 17 00:00:00 2001 From: Haythem Sellami Date: Mon, 17 Jun 2019 12:45:08 +0100 Subject: [PATCH] contributor app tests --- apps/contributor/contracts/Contributor.sol | 5 +- apps/contributor/contracts/test/Spoof.sol | 16 ++ apps/contributor/package-lock.json | 63 +++++++- apps/contributor/package.json | 1 + apps/contributor/test/contributor.js | 170 +++++++++++++++++++++ 5 files changed, 247 insertions(+), 8 deletions(-) create mode 100644 apps/contributor/contracts/test/Spoof.sol create mode 100644 apps/contributor/test/contributor.js diff --git a/apps/contributor/contracts/Contributor.sol b/apps/contributor/contracts/Contributor.sol index 0b595c1..9dcab19 100644 --- a/apps/contributor/contracts/Contributor.sol +++ b/apps/contributor/contracts/Contributor.sol @@ -64,10 +64,13 @@ contract Contributor is AragonApp { } function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) { + require(newAccount != address(0), "invalid new account address"); + require(getContributorAddressById(id) == oldAccount, "contributor does not exist"); + contributorIds[oldAccount] = 0; contributorIds[newAccount] = id; contributors[id].account = newAccount; - ContributorAccountUpdated(id, oldAccount, newAccount); + emit ContributorAccountUpdated(id, oldAccount, newAccount); } function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) { diff --git a/apps/contributor/contracts/test/Spoof.sol b/apps/contributor/contracts/test/Spoof.sol new file mode 100644 index 0000000..34fb889 --- /dev/null +++ b/apps/contributor/contracts/test/Spoof.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.24; + +import "@aragon/os/contracts/acl/ACL.sol"; +import "@aragon/os/contracts/kernel/Kernel.sol"; +import "@aragon/os/contracts/factory/DAOFactory.sol"; + +// You might think this file is a bit odd, but let me explain. +// We only use for now those imported contracts in our tests, which +// means Truffle will not compile them for us, because they are from +// an external dependency. + + +// solium-disable-next-line no-empty-blocks +contract Spoof { + // ... +} diff --git a/apps/contributor/package-lock.json b/apps/contributor/package-lock.json index 676000d..86c6282 100644 --- a/apps/contributor/package-lock.json +++ b/apps/contributor/package-lock.json @@ -7329,11 +7329,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7346,15 +7348,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -7457,7 +7462,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -7467,6 +7473,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7479,17 +7486,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7506,6 +7516,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -7578,7 +7589,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -7588,6 +7600,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -7693,6 +7706,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -24843,6 +24857,24 @@ } } }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "dev": true, + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + } + } + }, "eth-gas-reporter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.0.tgz", @@ -25831,6 +25863,23 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dev": true, + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "dev": true + } + } + }, "immediate": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", diff --git a/apps/contributor/package.json b/apps/contributor/package.json index 9a50b1c..1c99b9e 100644 --- a/apps/contributor/package.json +++ b/apps/contributor/package.json @@ -8,6 +8,7 @@ }, "devDependencies": { "@aragon/test-helpers": "^1.1.0", + "eth-ens-namehash": "^2.0.8", "eth-gas-reporter": "^0.2.0", "ganache-cli": "^6.4.3", "solidity-coverage": "^0.5.11" diff --git a/apps/contributor/test/contributor.js b/apps/contributor/test/contributor.js new file mode 100644 index 0000000..589c5cb --- /dev/null +++ b/apps/contributor/test/contributor.js @@ -0,0 +1,170 @@ +const namehash = require('eth-ens-namehash').hash; + +// eslint-disable-next-line no-undef +const Contributor = artifacts.require("Contributor.sol"); + +// eslint-disable-next-line no-undef +const getContract = name => artifacts.require(name); +const { assertRevert } = require('@aragon/test-helpers/assertThrow'); + +const ZERO_ADDR = '0x0000000000000000000000000000000000000000'; + +contract('Contributor app', (accounts) => { + let kernelBase, aclBase, daoFactory, r, dao, acl, contributor; + + const root = accounts[0]; + const member1 = accounts[1]; + + // eslint-disable-next-line no-undef + before(async () => { + kernelBase = await getContract('Kernel').new(true); // petrify immediately + aclBase = await getContract('ACL').new(); + daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR); + r = await daoFactory.newDAO(root); + dao = getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao); + acl = getContract('ACL').at(await dao.acl()); + + //create dao mamnager permission for coin owner + await acl.createPermission( + root, + dao.address, + await dao.APP_MANAGER_ROLE(), + root, + { from: root } + ); + + //get new app instance from DAO + const receipt = await dao.newAppInstance( + '0x1234', + (await Contributor.new()).address, + 0x0, + false, + { from: root } + ); + contributor = Contributor.at( + receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy + ); + + //apps id + let appsId = []; + appsId[0] = namehash("kredits-contribution"); + appsId[1] = namehash("kredits-contributor"); + appsId[2] = namehash("kredits-proposal"); + appsId[3] = namehash("kredits-token"); + + //init contributor (app) + await contributor.initialize(root, appsId); + + //create manage contributors role + await acl.createPermission( + root, + contributor.address, + await contributor.MANAGE_CONTRIBUTORS_ROLE(), + root, + { from: root } + ); + + }); + + describe("Owner default space permissions", async () => { + it('check owner is token issuer', async () => { + let manageContributorPermission = await acl.hasPermission(root, contributor.address, await contributor.MANAGE_CONTRIBUTORS_ROLE()); + // eslint-disable-next-line no-undef + assert.equal(manageContributorPermission, true); + }); + }); + + describe("Add contributor", async () => { + let account = root; + let hashDigest = '0x0'; + let hashFunction = 0; + let hashSize = 0; + + it("should revert when add contributor from an address that does not have permission", async () => { + return assertRevert(async () => { + await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1}); + 'sender does not have permission'; + }); + }); + + it('add contributor', async () => { + let contributorCount = await contributor.coreContributorsCount(); + await contributor.addContributor(account, hashDigest, hashFunction, hashSize); + // eslint-disable-next-line no-undef + assert.equal(await contributor.addressExists(account), true); + let contributorCountAfter = await contributor.coreContributorsCount(); + // eslint-disable-next-line no-undef + assert.equal(await contributorCountAfter.toNumber(), parseInt(contributorCount)+1); + }); + + it("should revert when add contributor with an address that already exist", async () => { + return assertRevert(async () => { + await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1}); + 'address already exist'; + }); + }); + }); + + describe("Update contributor", async () => { + let id; + let oldAccount; + let newAccount; + let hashDigest; + let hashFunction; + let hashSize; + + // eslint-disable-next-line no-undef + before(async () => { + id = await contributor.coreContributorsCount(); + oldAccount = root; + newAccount = member1; + hashDigest = '0x1000000000000000000000000000000000000000000000000000000000000000'; + hashFunction = 1; + hashSize = 1; + }); + + it('update contributor account', async () => { + await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount); + let contributorId = await contributor.getContributorIdByAddress(oldAccount); + // eslint-disable-next-line no-undef + assert.equal(contributorId.toNumber(), 0); + }); + + it("should revert when update contributor account from address that does not have permission", async () => { + return assertRevert(async () => { + await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount, {from: member1}); + 'sender does not have permission'; + }); + }); + + it("should revert when update contributor account that does not exist", async () => { + return assertRevert(async () => { + await contributor.updateContributorAccount(id.toNumber(), accounts[3], newAccount); + 'contributor does not exist'; + }); + }); + + it("should revert when update contributor account with address(0)", async () => { + return assertRevert(async () => { + await contributor.updateContributorAccount(id.toNumber(), oldAccount, ZERO_ADDR); + 'contributor does not exist'; + }); + }); + + it('update contributor profile hash', async () => { + await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize); + let contributorProfile = await contributor.contributors(id.toNumber()); + assert.equal(hashDigest, contributorProfile[1]); // eslint-disable-line no-undef + assert.equal(hashFunction, contributorProfile[2].toNumber()); // eslint-disable-line no-undef + assert.equal(hashSize, contributorProfile[3].toNumber()); // eslint-disable-line no-undef + }); + + it("should revert when update contributor profile hash from address that does not have permission", async () => { + return assertRevert(async () => { + await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize, {from: member1}); + 'sender does not have permission'; + }); + }); + + }); +});