Skip to content

Commit b030265

Browse files
committed
reworked with pytest hook generate_tests
1 parent 50654cc commit b030265

File tree

2 files changed

+66
-73
lines changed

2 files changed

+66
-73
lines changed

src/pytest_plugins/execute/eth_config/eth_config.py

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,6 @@ def pytest_configure(config: pytest.Config) -> None:
7878
network_configs_path = config.getoption("network_config_file")
7979
clients = config.getoption("clients")
8080
rpc_endpoint = config.getoption("rpc_endpoint")
81-
82-
# set flags for defining whether to run majority eth_config test or not, and how
83-
config.option.majority_eth_config_test_enabled = False
8481
config.option.majority_clients = [] # List[str]
8582

8683
# load provided networks file
@@ -106,12 +103,11 @@ def pytest_configure(config: pytest.Config) -> None:
106103
for c in clients:
107104
if c not in EXECUTION_CLIENTS:
108105
pytest.exit(f"Unsupported client was passed: {c}")
109-
print(f"Activating majority mode\nProvided client list: {clients}")
106+
print(f"Provided client list: {clients}")
110107

111108
# store majority mode configuration
112109
if ".ethpandaops.io" in rpc_endpoint:
113110
print("Ethpandaops RPC detected, toggling majority test on")
114-
config.option.majority_eth_config_test_enabled = True
115111
config.option.majority_clients = clients # List[str]
116112
else:
117113
print("No ethpandaops RPC detected, majority test will not be toggled on")
@@ -130,6 +126,7 @@ def pytest_configure(config: pytest.Config) -> None:
130126
try:
131127
print("Will now briefly check whether eth_config is supported by target rpc..")
132128
eth_rpc.config()
129+
print("Connection check ok (successfully got eth_config response)")
133130
except Exception as e:
134131
pytest.exit(f"RPC endpoint {rpc_endpoint} does not support `eth_config`: {e}")
135132

@@ -140,19 +137,19 @@ def rpc_endpoint(request) -> str:
140137
return request.config.getoption("rpc_endpoint")
141138

142139

143-
@pytest.fixture(autouse=True, scope="session")
144-
def eth_rpc(rpc_endpoint: str, request) -> Dict[str, List[EthRPC]]:
145-
"""Generate list of RPC URLs which define which clients are tested."""
146-
# generate all cl+el client combinations
147-
config = request.config
148-
el_clients: List[str] = config.getoption("majority_clients") # besu, erigon, ..
140+
# @pytest.fixture(autouse=True, scope="session")
141+
# def eth_rpc(rpc_endpoint: str) -> EthRPC:
142+
# """Initialize ethereum RPC client for the execution client under test."""
143+
# return EthRPC(rpc_endpoint)
144+
149145

150-
assert len(el_clients) > 0
151-
if "geth" in el_clients and "fusaka-devnet-3" in rpc_endpoint:
152-
pytest.exit("fusaka-devnet-3 geth does not support eth_config!")
146+
def all_rpc_endpoints(config) -> Dict[str, List[EthRPC]]:
147+
"""Derive a mapping of exec clients to the RPC URLs they are reachable at."""
148+
rpc_endpoint = config.getoption("rpc_endpoint")
149+
el_clients: List[str] = config.getoption("majority_clients") # besu, erigon, ..
150+
if len(el_clients) == 0:
151+
return {}
153152

154-
# generate client-specific URLs from provided rpc_endpoint (it does not matter which client the original rpc_endpoint specifies) # noqa: E501
155-
# we want all combinations of consensus and execution clients (sometimes an exec client is only reachable via a subset of consensus client combinations) # noqa: E501
156153
pattern = r"(.*?@rpc\.)([^-]+)-([^-]+)(-.*)"
157154
url_dict: Dict[str, List[EthRPC]] = {
158155
exec_client: [
@@ -174,3 +171,37 @@ def eth_rpc(rpc_endpoint: str, request) -> Dict[str, List[EthRPC]]:
174171
# ...
175172
# }
176173
return url_dict
174+
175+
176+
def pytest_generate_tests(metafunc: pytest.Metafunc):
177+
"""Generate tests for all clients under test."""
178+
# all_rpc_endpoints is a dictionary with the name of the exec client as key
179+
# and the possible URLs to contact it (different cl combinations) as value list
180+
all_rpc_endpoints_dict = all_rpc_endpoints(metafunc.config)
181+
182+
if metafunc.definition.name == "test_eth_config_majority":
183+
if len(all_rpc_endpoints_dict) < 2:
184+
# The test function is not run because we only have a single client, so no majority comparison # noqa: E501
185+
print("Skipping eth_config majority because less than 2 exec clients were passed")
186+
metafunc.parametrize(
187+
["all_rpc_endpoints"],
188+
[],
189+
)
190+
else:
191+
metafunc.parametrize(
192+
["all_rpc_endpoints"],
193+
[[all_rpc_endpoints_dict]], # interpret it as a single argument dict
194+
scope="function",
195+
)
196+
else:
197+
metafunc.parametrize(
198+
["eth_rpc"],
199+
[
200+
pytest.param(
201+
rpc_endpoint,
202+
id=endpoint_name,
203+
)
204+
for endpoint_name, rpc_endpoint in all_rpc_endpoints_dict.items()
205+
],
206+
scope="function",
207+
)

src/pytest_plugins/execute/eth_config/execute_eth_config.py

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,26 @@
1212
from .types import NetworkConfig
1313

1414

15-
@pytest.fixture(scope="session")
16-
def eth_config_response(request) -> EthConfigResponse | None:
17-
"""Get the `eth_config` response from the user-provided RPC endpoint."""
18-
config = request.config
19-
eth_rpc_url = config.getoption("rpc_endpoint")
20-
eth_rpc_target = EthRPC(eth_rpc_url)
21-
return eth_rpc_target.config()
15+
@pytest.fixture(scope="function")
16+
def eth_config_response(eth_rpc: List[EthRPC]) -> EthConfigResponse | None:
17+
"""Get the `eth_config` response from the client to be verified by all tests."""
18+
assert len(eth_rpc) > 0
19+
return eth_rpc[0].config() # just pick the first of possible URLs for this exec client
2220

2321

24-
@pytest.fixture(scope="session")
22+
@pytest.fixture(scope="function")
2523
def network(request) -> NetworkConfig:
2624
"""Get the network that will be used to verify all tests."""
2725
return request.config.network
2826

2927

30-
@pytest.fixture(scope="session")
28+
@pytest.fixture(scope="function")
3129
def current_time() -> int:
3230
"""Get the `eth_config` response from the client to be verified by all tests."""
3331
return int(time.time())
3432

3533

36-
@pytest.fixture(scope="session")
34+
@pytest.fixture(scope="function")
3735
def expected_eth_config(network: NetworkConfig, current_time: int) -> EthConfigResponse:
3836
"""Calculate the current fork value to verify against the client's response."""
3937
print(f"Network provided: {network}, Type: {type(network)}")
@@ -43,13 +41,8 @@ def expected_eth_config(network: NetworkConfig, current_time: int) -> EthConfigR
4341
def test_eth_config_current(
4442
eth_config_response: EthConfigResponse | None,
4543
expected_eth_config: EthConfigResponse,
46-
request,
4744
) -> None:
4845
"""Validate `current` field of the `eth_config` RPC endpoint."""
49-
config = request.config
50-
if config.getoption("network_config_file") is None:
51-
pytest.skip("Skipping test because no 'network_config_file' was specified")
52-
5346
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
5447
assert eth_config_response.current is not None, (
5548
"Client did not return a valid `current` fork config."
@@ -65,13 +58,8 @@ def test_eth_config_current(
6558
def test_eth_config_current_fork_id(
6659
eth_config_response: EthConfigResponse | None,
6760
expected_eth_config: EthConfigResponse,
68-
request,
6961
) -> None:
7062
"""Validate `forkId` field within the `current` configuration object."""
71-
config = request.config
72-
if config.getoption("network_config_file") is None:
73-
pytest.skip("Skipping test because no 'network_config_file' was specified")
74-
7563
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
7664
assert eth_config_response.current is not None, (
7765
"Client did not return a valid `current` fork config."
@@ -89,13 +77,8 @@ def test_eth_config_current_fork_id(
8977
def test_eth_config_next(
9078
eth_config_response: EthConfigResponse | None,
9179
expected_eth_config: EthConfigResponse,
92-
request,
9380
) -> None:
9481
"""Validate `next` field of the `eth_config` RPC endpoint."""
95-
config = request.config
96-
if config.getoption("network_config_file") is None:
97-
pytest.skip("Skipping test because no 'network_config_file' was specified")
98-
9982
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
10083
expected_next = expected_eth_config.next
10184
if expected_next is None:
@@ -116,13 +99,8 @@ def test_eth_config_next(
11699
def test_eth_config_next_fork_id(
117100
eth_config_response: EthConfigResponse | None,
118101
expected_eth_config: EthConfigResponse,
119-
request,
120102
) -> None:
121103
"""Validate `forkId` field within the `next` configuration object."""
122-
config = request.config
123-
if config.getoption("network_config_file") is None:
124-
pytest.skip("Skipping test because no 'network_config_file' was specified")
125-
126104
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
127105
expected_next = expected_eth_config.next
128106
if expected_next is None:
@@ -151,13 +129,8 @@ def test_eth_config_next_fork_id(
151129
def test_eth_config_last(
152130
eth_config_response: EthConfigResponse | None,
153131
expected_eth_config: EthConfigResponse,
154-
request,
155132
) -> None:
156133
"""Validate `last` field of the `eth_config` RPC endpoint."""
157-
config = request.config
158-
if config.getoption("network_config_file") is None:
159-
pytest.skip("Skipping test because no 'network_config_file' was specified")
160-
161134
expected_last = expected_eth_config.last
162135
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
163136
if expected_last is None:
@@ -178,13 +151,8 @@ def test_eth_config_last(
178151
def test_eth_config_last_fork_id(
179152
eth_config_response: EthConfigResponse | None,
180153
expected_eth_config: EthConfigResponse,
181-
request,
182154
) -> None:
183155
"""Validate `forkId` field within the `last` configuration object."""
184-
config = request.config
185-
if config.getoption("network_config_file") is None:
186-
pytest.skip("Skipping test because no 'network_config_file' was specified")
187-
188156
assert eth_config_response is not None, "Client did not return a valid `eth_config` response."
189157
expected_last = expected_eth_config.last
190158
if expected_last is None:
@@ -211,24 +179,15 @@ def test_eth_config_last_fork_id(
211179

212180

213181
def test_eth_config_majority(
214-
eth_rpc: Dict[str, List[EthRPC]],
215-
request,
182+
all_rpc_endpoints: Dict[str, List[EthRPC]],
216183
) -> None:
217184
"""Queries devnet exec clients for their eth_config and fails if not all have the same response.""" # noqa: E501
218-
# decide whether to run this test
219-
config = request.config
220-
run_this_test_bool = config.getoption(name="majority_eth_config_test_enabled")
221-
if not run_this_test_bool:
222-
pytest.skip("Skipping eth_config majority test")
223-
224-
assert eth_rpc is not None
225-
226185
responses = dict() # Dict[exec_client_name : response] # noqa: C408
227186
client_to_url_used_dict = dict() # noqa: C408
228-
for exec_client in eth_rpc.keys():
187+
for exec_client in all_rpc_endpoints.keys():
229188
# try only as many consensus+exec client combinations until you receive a response
230189
# if all combinations for a given exec client fail we panic
231-
for eth_rpc_target in eth_rpc[exec_client]:
190+
for eth_rpc_target in all_rpc_endpoints[exec_client]:
232191
response = eth_rpc_target.config(timeout=10)
233192
if response is None:
234193
# safely split url to not leak rpc_endpoint in logs
@@ -246,11 +205,14 @@ def test_eth_config_majority(
246205

247206
break # no need to gather more responses for this client
248207

249-
assert len(responses.keys()) == len(eth_rpc.keys()), "Failed to get an eth_config response "
250-
f" from each specified execution client. Full list of execution clients is {eth_rpc.keys()} "
251-
f"but we were only able to gather eth_config responses from: {responses.keys()}\nWill try "
252-
"again with a different consensus-execution client combination for this execution client"
253-
208+
assert len(responses.keys()) == len(all_rpc_endpoints.keys()), (
209+
"Failed to get an eth_config response "
210+
f" from each specified execution client. Full list of execution clients is "
211+
f"{all_rpc_endpoints.keys()} but we were only able to gather eth_config responses "
212+
f"from: {responses.keys()}\n"
213+
"Will try again with a different consensus-execution client combination for "
214+
"this execution client"
215+
)
254216
# determine hashes of client responses
255217
client_to_hash_dict = dict() # Dict[exec_client : response hash] # noqa: C408
256218
for client in responses.keys():

0 commit comments

Comments
 (0)