Skip to content

Commit 498c563

Browse files
committed
Extension tests
1 parent d06b829 commit 498c563

2 files changed

Lines changed: 453 additions & 0 deletions

File tree

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+
pragma solidity ^0.8.17;
4+
5+
import {Test} from "forge-std/Test.sol";
6+
import {Addresslist} from "../../../../../src/common/plugin/extensions/governance/Addresslist.sol";
7+
import {AddresslistMock} from "../../../../mocks/commons/plugin/extensions/governance/AddresslistMock.sol";
8+
9+
/// @notice Direct tests for `Addresslist` in
10+
/// `src/common/plugin/extensions/governance/Addresslist.sol`.
11+
///
12+
/// Ports `osx-commons/contracts/test/plugin/extensions/governance/addresslist.ts`.
13+
/// Each TS step that advanced the chain via `evm_mine` is reproduced with
14+
/// `vm.roll(block.number + 1)` so the checkpoint reads target distinct blocks.
15+
/// Closes the gaps from `TESTS.md` §11: large-array behaviour, exact-block
16+
/// precision, off-by-one window around the boundary block.
17+
contract AddresslistTest is Test {
18+
AddresslistMock internal addresslist;
19+
address internal alice;
20+
address internal bob;
21+
address internal carol;
22+
23+
function setUp() public {
24+
alice = makeAddr("alice");
25+
bob = makeAddr("bob");
26+
carol = makeAddr("carol");
27+
addresslist = new AddresslistMock();
28+
// Start at a block far from genesis so we can read `block.number - 1`
29+
// without falling off the chain (some Foundry versions choke at 0).
30+
vm.roll(100);
31+
}
32+
33+
// -------------------------------------------------------------------------
34+
// addresslistLength
35+
// -------------------------------------------------------------------------
36+
37+
function test_addresslistLength_growsWithAdditions() public {
38+
assertEq(addresslist.addresslistLength(), 0);
39+
40+
_add(alice);
41+
assertEq(addresslist.addresslistLength(), 1);
42+
43+
_add2(bob, carol);
44+
assertEq(addresslist.addresslistLength(), 3);
45+
}
46+
47+
function test_addresslistLength_shrinksWithRemovals() public {
48+
_add3(alice, bob, carol);
49+
assertEq(addresslist.addresslistLength(), 3);
50+
51+
_remove(alice);
52+
assertEq(addresslist.addresslistLength(), 2);
53+
54+
_remove2(bob, carol);
55+
assertEq(addresslist.addresslistLength(), 0);
56+
}
57+
58+
// -------------------------------------------------------------------------
59+
// addresslistLengthAtBlock
60+
// -------------------------------------------------------------------------
61+
62+
function test_addresslistLengthAtBlock_reflectsAddHistory() public {
63+
// tx1 at block B1 adds alice → length 1
64+
uint256 b1 = block.number;
65+
_add(alice);
66+
vm.roll(block.number + 1);
67+
68+
// tx2 at block B2 adds bob+carol → length 3
69+
uint256 b2 = block.number;
70+
_add2(bob, carol);
71+
vm.roll(block.number + 1);
72+
73+
assertEq(addresslist.addresslistLengthAtBlock(b1 - 1), 0);
74+
assertEq(addresslist.addresslistLengthAtBlock(b1), 1);
75+
assertEq(addresslist.addresslistLengthAtBlock(b2), 3);
76+
}
77+
78+
function test_addresslistLengthAtBlock_reflectsRemovalHistory() public {
79+
uint256 b1 = block.number;
80+
_add3(alice, bob, carol);
81+
vm.roll(block.number + 1);
82+
83+
uint256 b2 = block.number;
84+
_remove(alice);
85+
vm.roll(block.number + 1);
86+
87+
uint256 b3 = block.number;
88+
_remove2(bob, carol);
89+
vm.roll(block.number + 1);
90+
91+
assertLt(b1, b2);
92+
assertLt(b2, b3);
93+
94+
assertEq(addresslist.addresslistLengthAtBlock(b1), 3);
95+
assertEq(addresslist.addresslistLengthAtBlock(b2), 2);
96+
assertEq(addresslist.addresslistLengthAtBlock(b3), 0);
97+
}
98+
99+
// -------------------------------------------------------------------------
100+
// isListed
101+
// -------------------------------------------------------------------------
102+
103+
function test_isListed_returnsTrueIfListed() public {
104+
_add(alice);
105+
vm.roll(block.number + 1);
106+
assertTrue(addresslist.isListed(alice));
107+
}
108+
109+
function test_isListed_returnsFalseIfNotListed() public view {
110+
assertFalse(addresslist.isListed(alice));
111+
}
112+
113+
// -------------------------------------------------------------------------
114+
// isListedAtBlock
115+
// -------------------------------------------------------------------------
116+
117+
function test_isListedAtBlock_returnsTrueAtSpecificBlock() public {
118+
uint256 b1 = block.number;
119+
_add(alice);
120+
vm.roll(block.number + 1);
121+
122+
uint256 b2 = block.number;
123+
_remove(alice);
124+
vm.roll(block.number + 1);
125+
126+
assertLt(b1, b2);
127+
assertTrue(addresslist.isListedAtBlock(alice, b1));
128+
assertFalse(addresslist.isListedAtBlock(alice, b2));
129+
}
130+
131+
function test_isListedAtBlock_returnsFalseAtPriorBlock() public {
132+
uint256 b1 = block.number;
133+
_add(alice);
134+
vm.roll(block.number + 1);
135+
136+
// GAP: precision check — the very block before the add must report false.
137+
assertFalse(addresslist.isListedAtBlock(alice, b1 - 1));
138+
assertTrue(addresslist.isListedAtBlock(alice, b1));
139+
}
140+
141+
// -------------------------------------------------------------------------
142+
// addAddresses
143+
// -------------------------------------------------------------------------
144+
145+
function test_addAddresses_addsAllProvidedEntries() public {
146+
assertFalse(addresslist.isListed(alice));
147+
assertFalse(addresslist.isListed(bob));
148+
assertEq(addresslist.addresslistLength(), 0);
149+
150+
_add2(alice, bob);
151+
vm.roll(block.number + 1);
152+
153+
assertTrue(addresslist.isListed(alice));
154+
assertTrue(addresslist.isListed(bob));
155+
assertEq(addresslist.addresslistLength(), 2);
156+
}
157+
158+
function test_addAddresses_revertsIfMemberAlreadyListed() public {
159+
_add2(alice, carol);
160+
vm.roll(block.number + 1);
161+
assertEq(addresslist.addresslistLength(), 2);
162+
163+
// Try to add bob and carol; carol is already listed so the call reverts
164+
// on the second iteration.
165+
address[] memory batch = new address[](2);
166+
batch[0] = bob;
167+
batch[1] = carol;
168+
vm.expectRevert(abi.encodeWithSelector(Addresslist.InvalidAddresslistUpdate.selector, carol));
169+
addresslist.addAddresses(batch);
170+
171+
vm.roll(block.number + 1);
172+
assertTrue(addresslist.isListed(alice));
173+
assertTrue(addresslist.isListed(carol));
174+
assertFalse(addresslist.isListed(bob));
175+
assertEq(addresslist.addresslistLength(), 2);
176+
}
177+
178+
function test_addAddresses_revertsIfDuplicatesInBatch() public {
179+
address[] memory batch = new address[](2);
180+
batch[0] = alice;
181+
batch[1] = alice;
182+
vm.expectRevert(abi.encodeWithSelector(Addresslist.InvalidAddresslistUpdate.selector, alice));
183+
addresslist.addAddresses(batch);
184+
}
185+
186+
// -------------------------------------------------------------------------
187+
// removeAddresses
188+
// -------------------------------------------------------------------------
189+
190+
function test_removeAddresses_removesAllProvidedEntries() public {
191+
_add2(alice, bob);
192+
vm.roll(block.number + 1);
193+
194+
assertTrue(addresslist.isListed(alice));
195+
assertTrue(addresslist.isListed(bob));
196+
assertEq(addresslist.addresslistLength(), 2);
197+
198+
_remove2(alice, bob);
199+
vm.roll(block.number + 1);
200+
201+
assertFalse(addresslist.isListed(alice));
202+
assertFalse(addresslist.isListed(bob));
203+
assertEq(addresslist.addresslistLength(), 0);
204+
}
205+
206+
function test_removeAddresses_revertsIfMemberNotListed() public {
207+
_add2(alice, bob);
208+
vm.roll(block.number + 1);
209+
assertEq(addresslist.addresslistLength(), 2);
210+
211+
// Try to remove bob and carol; carol is not listed.
212+
address[] memory batch = new address[](2);
213+
batch[0] = bob;
214+
batch[1] = carol;
215+
vm.expectRevert(abi.encodeWithSelector(Addresslist.InvalidAddresslistUpdate.selector, carol));
216+
addresslist.removeAddresses(batch);
217+
218+
vm.roll(block.number + 1);
219+
assertTrue(addresslist.isListed(alice));
220+
assertTrue(addresslist.isListed(bob));
221+
assertEq(addresslist.addresslistLength(), 2);
222+
}
223+
224+
function test_removeAddresses_revertsIfDuplicatesInBatch() public {
225+
_add2(alice, bob);
226+
vm.roll(block.number + 1);
227+
228+
address[] memory batch = new address[](2);
229+
batch[0] = alice;
230+
batch[1] = alice;
231+
vm.expectRevert(abi.encodeWithSelector(Addresslist.InvalidAddresslistUpdate.selector, alice));
232+
addresslist.removeAddresses(batch);
233+
}
234+
235+
// -------------------------------------------------------------------------
236+
// GAP — large-array behaviour (closes flaw log F18)
237+
// -------------------------------------------------------------------------
238+
239+
/// 256 distinct addresses round-trip through add and remove cleanly. Locks
240+
/// in the `_uncheckedAdd` / `_uncheckedSub` checkpoint writes against
241+
/// silent overflow on large inputs.
242+
function test_addAndRemove_largeArray() public {
243+
uint256 N = 256;
244+
address[] memory many = new address[](N);
245+
for (uint256 i = 0; i < N; i++) {
246+
many[i] = address(uint160(0x1000 + i));
247+
}
248+
249+
addresslist.addAddresses(many);
250+
vm.roll(block.number + 1);
251+
assertEq(addresslist.addresslistLength(), N);
252+
253+
for (uint256 i = 0; i < N; i++) {
254+
assertTrue(addresslist.isListed(many[i]));
255+
}
256+
257+
addresslist.removeAddresses(many);
258+
vm.roll(block.number + 1);
259+
assertEq(addresslist.addresslistLength(), 0);
260+
}
261+
262+
// -------------------------------------------------------------------------
263+
// Internal helpers — terse wrappers over single/double/triple add+remove.
264+
// -------------------------------------------------------------------------
265+
266+
function _add(address a) internal {
267+
address[] memory arr = new address[](1);
268+
arr[0] = a;
269+
addresslist.addAddresses(arr);
270+
}
271+
272+
function _add2(address a, address b) internal {
273+
address[] memory arr = new address[](2);
274+
arr[0] = a;
275+
arr[1] = b;
276+
addresslist.addAddresses(arr);
277+
}
278+
279+
function _add3(address a, address b, address c) internal {
280+
address[] memory arr = new address[](3);
281+
arr[0] = a;
282+
arr[1] = b;
283+
arr[2] = c;
284+
addresslist.addAddresses(arr);
285+
}
286+
287+
function _remove(address a) internal {
288+
address[] memory arr = new address[](1);
289+
arr[0] = a;
290+
addresslist.removeAddresses(arr);
291+
}
292+
293+
function _remove2(address a, address b) internal {
294+
address[] memory arr = new address[](2);
295+
arr[0] = a;
296+
arr[1] = b;
297+
addresslist.removeAddresses(arr);
298+
}
299+
}

0 commit comments

Comments
 (0)