-
Notifications
You must be signed in to change notification settings - Fork 11.7k
/
PublicRole.behavior.js
133 lines (110 loc) · 5.47 KB
/
PublicRole.behavior.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const { shouldFail, constants, expectEvent } = require('openzeppelin-test-helpers');
const { ZERO_ADDRESS } = constants;
function capitalize (str) {
return str.replace(/\b\w/g, l => l.toUpperCase());
}
// Tests that a role complies with the standard role interface, that is:
// * an onlyRole modifier
// * an isRole function
// * an addRole function, accessible to role havers
// * a renounceRole function
// * roleAdded and roleRemoved events
// Both the modifier and an additional internal remove function are tested through a mock contract that exposes them.
// This mock contract should be stored in this.contract.
//
// @param authorized an account that has the role
// @param otherAuthorized another account that also has the role
// @param other an account that doesn't have the role, passed inside an array for ergonomics
// @param rolename a string with the name of the role
// @param manager undefined for regular roles, or a manager account for managed roles. In these, only the manager
// account can create and remove new role bearers.
function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolename, manager) {
rolename = capitalize(rolename);
describe('should behave like public role', function () {
beforeEach('check preconditions', async function () {
(await this.contract[`is${rolename}`](authorized)).should.equal(true);
(await this.contract[`is${rolename}`](otherAuthorized)).should.equal(true);
(await this.contract[`is${rolename}`](other)).should.equal(false);
});
if (manager === undefined) { // Managed roles are only assigned by the manager, and none are set at construction
it('emits events during construction', async function () {
await expectEvent.inConstruction(this.contract, `${rolename}Added`, {
account: authorized,
});
});
}
it('reverts when querying roles for the null account', async function () {
await shouldFail.reverting(this.contract[`is${rolename}`](ZERO_ADDRESS));
});
describe('access control', function () {
context('from authorized account', function () {
const from = authorized;
it('allows access', async function () {
await this.contract[`only${rolename}Mock`]({ from });
});
});
context('from unauthorized account', function () {
const from = other;
it('reverts', async function () {
await shouldFail.reverting(this.contract[`only${rolename}Mock`]({ from }));
});
});
});
describe('add', function () {
const from = manager === undefined ? authorized : manager;
context(`from ${manager ? 'the manager' : 'a role-haver'} account`, function () {
it('adds role to a new account', async function () {
await this.contract[`add${rolename}`](other, { from });
(await this.contract[`is${rolename}`](other)).should.equal(true);
});
it(`emits a ${rolename}Added event`, async function () {
const { logs } = await this.contract[`add${rolename}`](other, { from });
expectEvent.inLogs(logs, `${rolename}Added`, { account: other });
});
it('reverts when adding role to an already assigned account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](authorized, { from }));
});
it('reverts when adding role to the null account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](ZERO_ADDRESS, { from }));
});
});
});
describe('remove', function () {
// Non-managed roles have no restrictions on the mocked '_remove' function (exposed via 'remove').
const from = manager || other;
context(`from ${manager ? 'the manager' : 'any'} account`, function () {
it('removes role from an already assigned account', async function () {
await this.contract[`remove${rolename}`](authorized, { from });
(await this.contract[`is${rolename}`](authorized)).should.equal(false);
(await this.contract[`is${rolename}`](otherAuthorized)).should.equal(true);
});
it(`emits a ${rolename}Removed event`, async function () {
const { logs } = await this.contract[`remove${rolename}`](authorized, { from });
expectEvent.inLogs(logs, `${rolename}Removed`, { account: authorized });
});
it('reverts when removing from an unassigned account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](other, { from }));
});
it('reverts when removing role from the null account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](ZERO_ADDRESS, { from }));
});
});
});
describe('renouncing roles', function () {
it('renounces an assigned role', async function () {
await this.contract[`renounce${rolename}`]({ from: authorized });
(await this.contract[`is${rolename}`](authorized)).should.equal(false);
});
it(`emits a ${rolename}Removed event`, async function () {
const { logs } = await this.contract[`renounce${rolename}`]({ from: authorized });
expectEvent.inLogs(logs, `${rolename}Removed`, { account: authorized });
});
it('reverts when renouncing unassigned role', async function () {
await shouldFail.reverting(this.contract[`renounce${rolename}`]({ from: other }));
});
});
});
}
module.exports = {
shouldBehaveLikePublicRole,
};