Skip to content

Commit d0eb106

Browse files
committed
fix(bal): Fix explicit empty checks for account changes; add unit tests
1 parent c6055af commit d0eb106

File tree

2 files changed

+212
-4
lines changed

2 files changed

+212
-4
lines changed

src/ethereum_test_types/block_access_list/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,11 @@ def _compare_account_expectations(
297297
expected_value = getattr(expected, field_name)
298298
actual_value = getattr(actual, field_name)
299299

300-
# explicit check for None
301-
if expected_value is None:
302-
if actual_value is not None and actual_value != []:
300+
# empty list explicitly set (no changes expected)
301+
if not expected_value:
302+
if actual_value:
303303
raise AssertionError(
304-
f"Expected {field_name} to be `None` but found: {actual_value}"
304+
f"Expected {field_name} to be empty but found: {actual_value}"
305305
)
306306
continue
307307

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""Unit tests for BlockAccessListExpectation validation."""
2+
3+
import pytest
4+
5+
from ethereum_test_base_types import Address
6+
from ethereum_test_types.block_access_list import (
7+
BalAccountChange,
8+
BalAccountExpectation,
9+
BalBalanceChange,
10+
BalNonceChange,
11+
BalStorageChange,
12+
BalStorageSlot,
13+
BlockAccessList,
14+
BlockAccessListExpectation,
15+
)
16+
17+
18+
def test_address_exclusion_validation_passes():
19+
"""Test that address exclusion works when address is not in BAL."""
20+
alice = Address("0x000000000000000000000000000000000000000a")
21+
bob = Address("0x000000000000000000000000000000000000000b")
22+
23+
actual_bal = BlockAccessList(
24+
[
25+
BalAccountChange(
26+
address=alice,
27+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
28+
),
29+
]
30+
)
31+
32+
expectation = BlockAccessListExpectation(
33+
account_expectations={
34+
alice: BalAccountExpectation(nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)]),
35+
bob: None, # expect Bob is not in BAL (correctly)
36+
}
37+
)
38+
39+
expectation.verify_against(actual_bal)
40+
41+
42+
def test_address_exclusion_validation_raises_when_address_is_present():
43+
"""Test that validation fails when excluded address is in BAL."""
44+
alice = Address("0x000000000000000000000000000000000000000a")
45+
bob = Address("0x000000000000000000000000000000000000000b")
46+
47+
actual_bal = BlockAccessList(
48+
[
49+
BalAccountChange(
50+
address=alice,
51+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
52+
),
53+
BalAccountChange(
54+
address=bob,
55+
balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)],
56+
),
57+
]
58+
)
59+
60+
expectation = BlockAccessListExpectation(
61+
# explicitly expect Bob to NOT be in BAL (wrongly)
62+
account_expectations={bob: None},
63+
)
64+
65+
with pytest.raises(Exception, match="should not be in BAL but was found"):
66+
expectation.verify_against(actual_bal)
67+
68+
69+
def test_empty_list_validation():
70+
"""Test that empty list validates correctly."""
71+
alice = Address("0x000000000000000000000000000000000000000a")
72+
73+
actual_bal = BlockAccessList(
74+
[
75+
BalAccountChange(
76+
address=alice,
77+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
78+
balance_changes=[], # no balance changes
79+
),
80+
]
81+
)
82+
83+
expectation = BlockAccessListExpectation(
84+
account_expectations={
85+
alice: BalAccountExpectation(
86+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
87+
balance_changes=[], # explicitly expect no balance changes
88+
),
89+
}
90+
)
91+
92+
expectation.verify_against(actual_bal)
93+
94+
95+
def test_empty_list_validation_fails():
96+
"""Test that validation fails when expecting empty but field has values."""
97+
alice = Address("0x000000000000000000000000000000000000000a")
98+
99+
actual_bal = BlockAccessList(
100+
[
101+
BalAccountChange(
102+
address=alice,
103+
balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)],
104+
),
105+
]
106+
)
107+
108+
expectation = BlockAccessListExpectation(
109+
account_expectations={
110+
# expect no balance changes (wrongly)
111+
alice: BalAccountExpectation(balance_changes=[]),
112+
}
113+
)
114+
115+
with pytest.raises(Exception, match="Expected balance_changes to be empty"):
116+
expectation.verify_against(actual_bal)
117+
118+
119+
def test_partial_validation():
120+
"""Test that unset fields are not validated."""
121+
alice = Address("0x000000000000000000000000000000000000000a")
122+
123+
# Actual BAL has multiple types of changes
124+
actual_bal = BlockAccessList(
125+
[
126+
BalAccountChange(
127+
address=alice,
128+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
129+
balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)],
130+
storage_reads=[0x01, 0x02],
131+
),
132+
]
133+
)
134+
135+
# Only validate nonce changes, ignore balance and storage
136+
expectation = BlockAccessListExpectation(
137+
account_expectations={
138+
alice: BalAccountExpectation(
139+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
140+
# balance_changes and storage_reads not set and won't be validated
141+
),
142+
}
143+
)
144+
145+
expectation.verify_against(actual_bal)
146+
147+
148+
def test_storage_changes_validation():
149+
"""Test storage changes validation."""
150+
contract = Address("0x000000000000000000000000000000000000000c")
151+
152+
# Actual BAL with storage changes
153+
actual_bal = BlockAccessList(
154+
[
155+
BalAccountChange(
156+
address=contract,
157+
storage_changes=[
158+
BalStorageSlot(
159+
slot=0x01,
160+
slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)],
161+
)
162+
],
163+
),
164+
]
165+
)
166+
167+
# Expect the same storage changes
168+
expectation = BlockAccessListExpectation(
169+
account_expectations={
170+
contract: BalAccountExpectation(
171+
storage_changes=[
172+
BalStorageSlot(
173+
slot=0x01,
174+
slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)],
175+
)
176+
],
177+
),
178+
}
179+
)
180+
181+
expectation.verify_against(actual_bal)
182+
183+
184+
def test_missing_expected_address():
185+
"""Test that validation fails when expected address is missing."""
186+
alice = Address("0x000000000000000000000000000000000000000a")
187+
bob = Address("0x000000000000000000000000000000000000000b")
188+
189+
actual_bal = BlockAccessList(
190+
[
191+
BalAccountChange(
192+
address=alice,
193+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
194+
),
195+
]
196+
)
197+
198+
expectation = BlockAccessListExpectation(
199+
account_expectations={
200+
# wrongly expect Bob to be present
201+
bob: BalAccountExpectation(
202+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
203+
),
204+
}
205+
)
206+
207+
with pytest.raises(Exception, match="Expected address .* not found in actual BAL"):
208+
expectation.verify_against(actual_bal)

0 commit comments

Comments
 (0)