Skip to content

Commit db415aa

Browse files
committed
Add testing for AccessManagerLight
1 parent c1248a5 commit db415aa

File tree

1 file changed

+250
-146
lines changed

1 file changed

+250
-146
lines changed
Lines changed: 250 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,250 @@
1-
// const { ethers } = require('hardhat');
2-
// const { expect } = require('chai');
3-
// const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
4-
5-
// const { impersonate } = require('../../helpers/account');
6-
// const time = require('../../helpers/time');
7-
8-
// async function fixture() {
9-
// const [admin, roleMember, other] = await ethers.getSigners();
10-
11-
// const authority = await ethers.deployContract('$AccessManager', [admin]);
12-
// const managed = await ethers.deployContract('$AccessManagedTarget', [authority]);
13-
14-
// const anotherAuthority = await ethers.deployContract('$AccessManager', [admin]);
15-
// const authorityObserveIsConsuming = await ethers.deployContract('$AuthorityObserveIsConsuming');
16-
17-
// await impersonate(authority.target);
18-
// const authorityAsSigner = await ethers.getSigner(authority.target);
19-
20-
// return {
21-
// roleMember,
22-
// other,
23-
// authorityAsSigner,
24-
// authority,
25-
// managed,
26-
// authorityObserveIsConsuming,
27-
// anotherAuthority,
28-
// };
29-
// }
30-
31-
// describe('AccessManaged', function () {
32-
// beforeEach(async function () {
33-
// Object.assign(this, await loadFixture(fixture));
34-
// });
35-
36-
// it('sets authority and emits AuthorityUpdated event during construction', async function () {
37-
// await expect(this.managed.deploymentTransaction())
38-
// .to.emit(this.managed, 'AuthorityUpdated')
39-
// .withArgs(this.authority);
40-
// });
41-
42-
// describe('restricted modifier', function () {
43-
// beforeEach(async function () {
44-
// this.selector = this.managed.fnRestricted.getFragment().selector;
45-
// this.role = 42n;
46-
// await this.authority.$_setTargetFunctionRole(this.managed, this.selector, this.role);
47-
// await this.authority.$_grantRole(this.role, this.roleMember, 0, 0);
48-
// });
49-
50-
// it('succeeds when role is granted without execution delay', async function () {
51-
// await this.managed.connect(this.roleMember)[this.selector]();
52-
// });
53-
54-
// it('reverts when role is not granted', async function () {
55-
// await expect(this.managed.connect(this.other)[this.selector]())
56-
// .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
57-
// .withArgs(this.other);
58-
// });
59-
60-
// it('panics in short calldata', async function () {
61-
// // We avoid adding the `restricted` modifier to the fallback function because other tests may depend on it
62-
// // being accessible without restrictions. We check for the internal `_checkCanCall` instead.
63-
// await expect(this.managed.$_checkCanCall(this.roleMember, '0x1234')).to.be.reverted;
64-
// });
65-
66-
// describe('when role is granted with execution delay', function () {
67-
// beforeEach(async function () {
68-
// const executionDelay = 911n;
69-
// await this.authority.$_grantRole(this.role, this.roleMember, 0, executionDelay);
70-
// });
71-
72-
// it('reverts if the operation is not scheduled', async function () {
73-
// const fn = this.managed.interface.getFunction(this.selector);
74-
// const calldata = this.managed.interface.encodeFunctionData(fn, []);
75-
// const opId = await this.authority.hashOperation(this.roleMember, this.managed, calldata);
76-
77-
// await expect(this.managed.connect(this.roleMember)[this.selector]())
78-
// .to.be.revertedWithCustomError(this.authority, 'AccessManagerNotScheduled')
79-
// .withArgs(opId);
80-
// });
81-
82-
// it('succeeds if the operation is scheduled', async function () {
83-
// // Arguments
84-
// const delay = time.duration.hours(12);
85-
// const fn = this.managed.interface.getFunction(this.selector);
86-
// const calldata = this.managed.interface.encodeFunctionData(fn, []);
87-
88-
// // Schedule
89-
// const scheduledAt = (await time.clock.timestamp()) + 1n;
90-
// const when = scheduledAt + delay;
91-
// await time.increaseTo.timestamp(scheduledAt, false);
92-
// await this.authority.connect(this.roleMember).schedule(this.managed, calldata, when);
93-
94-
// // Set execution date
95-
// await time.increaseTo.timestamp(when, false);
96-
97-
// // Shouldn't revert
98-
// await this.managed.connect(this.roleMember)[this.selector]();
99-
// });
100-
// });
101-
// });
102-
103-
// describe('setAuthority', function () {
104-
// it('reverts if the caller is not the authority', async function () {
105-
// await expect(this.managed.connect(this.other).setAuthority(this.other))
106-
// .to.be.revertedWithCustomError(this.managed, 'AccessManagedUnauthorized')
107-
// .withArgs(this.other);
108-
// });
109-
110-
// it('reverts if the new authority is not a valid authority', async function () {
111-
// await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.other))
112-
// .to.be.revertedWithCustomError(this.managed, 'AccessManagedInvalidAuthority')
113-
// .withArgs(this.other);
114-
// });
115-
116-
// it('sets authority and emits AuthorityUpdated event', async function () {
117-
// await expect(this.managed.connect(this.authorityAsSigner).setAuthority(this.anotherAuthority))
118-
// .to.emit(this.managed, 'AuthorityUpdated')
119-
// .withArgs(this.anotherAuthority);
120-
121-
// expect(await this.managed.authority()).to.equal(this.anotherAuthority);
122-
// });
123-
// });
124-
125-
// describe('isConsumingScheduledOp', function () {
126-
// beforeEach(async function () {
127-
// await this.managed.connect(this.authorityAsSigner).setAuthority(this.authorityObserveIsConsuming);
128-
// });
129-
130-
// it('returns bytes4(0) when not consuming operation', async function () {
131-
// expect(await this.managed.isConsumingScheduledOp()).to.equal('0x00000000');
132-
// });
133-
134-
// it('returns isConsumingScheduledOp selector when consuming operation', async function () {
135-
// const isConsumingScheduledOp = this.managed.interface.getFunction('isConsumingScheduledOp()');
136-
// const fnRestricted = this.managed.fnRestricted.getFragment();
137-
// await expect(this.managed.connect(this.other).fnRestricted())
138-
// .to.emit(this.authorityObserveIsConsuming, 'ConsumeScheduledOpCalled')
139-
// .withArgs(
140-
// this.other,
141-
// this.managed.interface.encodeFunctionData(fnRestricted, []),
142-
// isConsumingScheduledOp.selector,
143-
// );
144-
// });
145-
// });
146-
// });
1+
const { ethers } = require('hardhat');
2+
const { expect } = require('chai');
3+
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
4+
const { mapValues } = require('@openzeppelin/contracts/test/helpers/iterate');
5+
6+
// Mask helpers
7+
const toHexString = i => '0x' + i.toString(16).padStart(64, 0);
8+
const toMask = i => toHexString(1n << BigInt(i));
9+
const combine = (...masks) => toHexString(masks.reduce((acc, m) => acc | BigInt(m), 0n));
10+
11+
const Roles = { admin: 0x00, public: 0xff };
12+
const Masks = mapValues(Roles, toMask);
13+
14+
async function fixture() {
15+
const [admin, user, target, other] = await ethers.getSigners();
16+
17+
const authority = await ethers.deployContract('$AccessManagerLight', [admin]);
18+
19+
return {
20+
admin,
21+
user,
22+
target,
23+
other,
24+
authority,
25+
};
26+
}
27+
28+
describe('AccessManaged', function () {
29+
beforeEach(async function () {
30+
Object.assign(this, await loadFixture(fixture));
31+
});
32+
33+
describe('Permission Manager', function () {
34+
const selector = ethers.hexlify(ethers.randomBytes(4));
35+
const group = 17n;
36+
const adminGroup = 42n;
37+
const groups = [13n, 69n, 128n];
38+
39+
describe('canCall', function () {
40+
describe('simple case: one group', async function () {
41+
it('Requirements set and Permissions set', async function () {
42+
this.withRequirements = true;
43+
this.withPermission = true;
44+
});
45+
46+
it('Requirements set and Permissions not set', async function () {
47+
this.withRequirements = true;
48+
this.withPermission = false;
49+
});
50+
51+
it('Requirements not set and Permissions set', async function () {
52+
this.withRequirements = false;
53+
this.withPermission = true;
54+
});
55+
56+
it('Requirements not set and Permissions not set', async function () {
57+
this.withRequirements = false;
58+
this.withPermission = false;
59+
});
60+
61+
afterEach(async function () {
62+
if (this.withRequirements) {
63+
await this.authority.setRequirements(this.target, [selector], [group]);
64+
}
65+
if (this.withPermission) {
66+
await this.authority.addGroup(this.user, group);
67+
}
68+
await expect(this.authority.canCall(this.user, this.target, selector)).to.eventually.equal(
69+
this.withRequirements && this.withPermission,
70+
);
71+
});
72+
});
73+
74+
describe('complexe case: one of many groups', async function () {
75+
it('some intersection', async function () {
76+
this.userGroups = [32, 42, 94, 128]; // User has all these groups
77+
this.targetGroups = [17, 35, 42, 69, 91]; // Target accepts any of these groups
78+
});
79+
80+
it('no intersection', async function () {
81+
this.userGroups = [32, 50, 94, 128]; // User has all these groups
82+
this.targetGroups = [17, 35, 42, 69, 91]; // Target accepts any of these groups
83+
});
84+
85+
afterEach(async function () {
86+
// set permissions and requirements
87+
await Promise.all([
88+
this.authority.setRequirements(this.target, [selector], this.targetGroups),
89+
...this.userGroups.map(group => this.authority.addGroup(this.user, group)),
90+
]);
91+
92+
// check can call
93+
await expect(this.authority.canCall(this.user, this.target, selector)).to.eventually.equal(
94+
this.userGroups.some(g => this.targetGroups.includes(g)),
95+
);
96+
});
97+
});
98+
});
99+
100+
describe('addGroup', function () {
101+
it('authorized', async function () {
102+
await expect(this.authority.connect(this.admin).addGroup(this.user, group))
103+
.to.emit(this.authority, 'GroupAdded')
104+
.withArgs(this.user, group);
105+
});
106+
107+
it('restricted', async function () {
108+
await expect(this.authority.connect(this.other).addGroup(this.user, group))
109+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
110+
.withArgs(this.other, Masks.public, Masks.admin);
111+
});
112+
113+
it('with role admin', async function () {
114+
await this.authority.connect(this.admin).addGroup(this.other, adminGroup);
115+
116+
await expect(this.authority.connect(this.other).addGroup(this.user, group))
117+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
118+
.withArgs(this.other, combine(Masks.public, toMask(adminGroup)), Masks.admin);
119+
120+
await expect(this.authority.setGroupAdmins(group, [adminGroup]))
121+
.to.emit(this.authority, 'GroupAdmins')
122+
.withArgs(group, toMask(adminGroup));
123+
124+
await expect(this.authority.connect(this.other).addGroup(this.user, group))
125+
.to.emit(this.authority, 'GroupAdded')
126+
.withArgs(this.user, group);
127+
});
128+
129+
it('effect', async function () {
130+
await expect(this.authority.getGroups(this.user)).to.eventually.equal(Masks.public);
131+
132+
await expect(this.authority.connect(this.admin).addGroup(this.user, group))
133+
.to.emit(this.authority, 'GroupAdded')
134+
.withArgs(this.user, group);
135+
136+
await expect(this.authority.getGroups(this.user)).to.eventually.equal(combine(Masks.public, toMask(group)));
137+
});
138+
});
139+
140+
describe('remGroup', function () {
141+
beforeEach(async function () {
142+
await this.authority.connect(this.admin).addGroup(this.user, group);
143+
});
144+
145+
it('authorized', async function () {
146+
await expect(this.authority.connect(this.admin).remGroup(this.user, group))
147+
.to.emit(this.authority, 'GroupRemoved')
148+
.withArgs(this.user, group);
149+
});
150+
151+
it('restricted', async function () {
152+
await expect(this.authority.connect(this.other).remGroup(this.user, group))
153+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
154+
.withArgs(this.other, Masks.public, Masks.admin);
155+
});
156+
157+
it('with role admin', async function () {
158+
await this.authority.connect(this.admin).addGroup(this.other, adminGroup);
159+
160+
await expect(this.authority.connect(this.other).addGroup(this.user, group))
161+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
162+
.withArgs(this.other, combine(Masks.public, toMask(adminGroup)), Masks.admin);
163+
164+
await expect(this.authority.setGroupAdmins(group, [adminGroup]))
165+
.to.emit(this.authority, 'GroupAdmins')
166+
.withArgs(group, toMask(adminGroup));
167+
168+
await expect(this.authority.connect(this.other).remGroup(this.user, group))
169+
.to.emit(this.authority, 'GroupRemoved')
170+
.withArgs(this.user, group);
171+
});
172+
173+
it('effect', async function () {
174+
await expect(this.authority.getGroups(this.user)).to.eventually.equal(combine(Masks.public, toMask(group)));
175+
176+
await expect(this.authority.connect(this.admin).remGroup(this.user, group))
177+
.to.emit(this.authority, 'GroupRemoved')
178+
.withArgs(this.user, group);
179+
180+
await expect(this.authority.getGroups(this.user)).to.eventually.equal(Masks.public);
181+
});
182+
});
183+
184+
describe('setGroupAdmins', function () {
185+
it('authorized', async function () {
186+
await expect(this.authority.connect(this.admin).setGroupAdmins(group, groups))
187+
.to.emit(this.authority, 'GroupAdmins')
188+
.withArgs(group, combine(...groups.map(toMask)));
189+
});
190+
191+
it('restricted', async function () {
192+
await expect(this.authority.connect(this.other).setGroupAdmins(group, groups))
193+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
194+
.withArgs(this.other, Masks.public, Masks.admin);
195+
});
196+
197+
it('effect', async function () {
198+
// Set some previous value
199+
await this.authority.connect(this.admin).setGroupAdmins(group, [group]);
200+
201+
// Check previous value is set
202+
await expect(this.authority.getGroupAdmins(group)).to.eventually.equal(combine(Masks.admin, toMask(group)));
203+
204+
// Set some new values
205+
await expect(this.authority.connect(this.admin).setGroupAdmins(group, groups))
206+
.to.emit(this.authority, 'GroupAdmins')
207+
.withArgs(group, combine(...groups.map(toMask)));
208+
209+
// Check the new values are set, and the previous is removed
210+
await expect(this.authority.getGroupAdmins(group)).to.eventually.equal(
211+
combine(Masks.admin, ...groups.map(toMask)),
212+
);
213+
});
214+
});
215+
216+
describe('setRequirements', function () {
217+
it('authorized', async function () {
218+
await expect(this.authority.connect(this.admin).setRequirements(this.target, [selector], groups))
219+
.to.emit(this.authority, 'RequirementsSet')
220+
.withArgs(this.target, selector, combine(...groups.map(toMask)));
221+
});
222+
223+
it('restricted', async function () {
224+
await expect(this.authority.connect(this.other).setRequirements(this.target, [selector], groups))
225+
.to.revertedWithCustomError(this.authority, 'MissingPermissions')
226+
.withArgs(this.other, Masks.public, Masks.admin);
227+
});
228+
229+
it('effect', async function () {
230+
// Set some previous value
231+
await this.authority.connect(this.admin).setRequirements(this.target, [selector], [group]);
232+
233+
// Check previous value is set
234+
await expect(this.authority.getRequirements(this.target, selector)).to.eventually.equal(
235+
combine(Masks.admin, toMask(group)),
236+
);
237+
238+
// Set some new values
239+
await expect(this.authority.connect(this.admin).setRequirements(this.target, [selector], groups))
240+
.to.emit(this.authority, 'RequirementsSet')
241+
.withArgs(this.target, selector, combine(...groups.map(toMask)));
242+
243+
// Check the new values are set, and the previous is removed
244+
await expect(this.authority.getRequirements(this.target, selector)).to.eventually.equal(
245+
combine(Masks.admin, ...groups.map(toMask)),
246+
);
247+
});
248+
});
249+
});
250+
});

0 commit comments

Comments
 (0)