genlayer-test 0.4.1__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/METADATA +75 -14
  2. genlayer_test-2.0.0.dist-info/RECORD +76 -0
  3. gltest/__init__.py +7 -6
  4. gltest/{glchain/client.py → clients.py} +1 -1
  5. gltest/contracts/__init__.py +4 -0
  6. gltest/contracts/contract.py +197 -0
  7. gltest/{glchain/contract.py → contracts/contract_factory.py} +22 -137
  8. gltest/contracts/contract_functions.py +59 -0
  9. gltest/contracts/method_stats.py +163 -0
  10. gltest/contracts/stats_collector.py +259 -0
  11. gltest/contracts/utils.py +12 -0
  12. gltest/fixtures.py +2 -6
  13. gltest/helpers/take_snapshot.py +1 -1
  14. gltest_cli/config/constants.py +1 -0
  15. gltest_cli/config/plugin.py +53 -0
  16. gltest_cli/config/pytest_context.py +9 -0
  17. gltest_cli/config/types.py +41 -0
  18. gltest_cli/config/user.py +21 -8
  19. tests/examples/contracts/football_prediction_market.py +1 -1
  20. tests/examples/tests/test_football_prediction_market.py +2 -2
  21. tests/examples/tests/test_intelligent_oracle_factory.py +6 -6
  22. tests/examples/tests/test_llm_erc20.py +5 -5
  23. tests/examples/tests/test_llm_erc20_analyze.py +50 -0
  24. tests/examples/tests/test_log_indexer.py +23 -11
  25. tests/examples/tests/test_multi_file_contract.py +2 -2
  26. tests/examples/tests/test_multi_file_contract_legacy.py +2 -2
  27. tests/examples/tests/test_multi_read_erc20.py +14 -12
  28. tests/examples/tests/test_multi_tenant_storage.py +11 -7
  29. tests/examples/tests/test_read_erc20.py +1 -1
  30. tests/examples/tests/test_storage.py +4 -4
  31. tests/examples/tests/test_storage_legacy.py +5 -3
  32. tests/examples/tests/test_user_storage.py +20 -10
  33. tests/examples/tests/test_wizard_of_coin.py +1 -1
  34. tests/gltest_cli/config/test_config_integration.py +432 -0
  35. tests/gltest_cli/config/test_general_config.py +406 -0
  36. tests/gltest_cli/config/test_plugin.py +167 -0
  37. tests/gltest_cli/config/test_user.py +61 -1
  38. genlayer_test-0.4.1.dist-info/RECORD +0 -67
  39. gltest/glchain/__init__.py +0 -16
  40. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/WHEEL +0 -0
  41. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/entry_points.txt +0 -0
  42. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/licenses/LICENSE +0 -0
  43. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/top_level.txt +0 -0
  44. /gltest/{glchain/account.py → accounts.py} +0 -0
@@ -0,0 +1,406 @@
1
+ import pytest
2
+ from pathlib import Path
3
+ from gltest_cli.config.types import (
4
+ GeneralConfig,
5
+ UserConfig,
6
+ PluginConfig,
7
+ PathConfig,
8
+ NetworkConfigData,
9
+ )
10
+ from gltest_cli.config.constants import DEFAULT_ARTIFACTS_DIR, DEFAULT_CONTRACTS_DIR
11
+
12
+
13
+ def test_general_config_artifacts_methods():
14
+ """Test GeneralConfig artifacts directory methods."""
15
+ user_config = UserConfig(
16
+ networks={"localnet": NetworkConfigData()},
17
+ paths=PathConfig(contracts=Path("contracts"), artifacts=Path("user_artifacts")),
18
+ )
19
+
20
+ plugin_config = PluginConfig()
21
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
22
+
23
+ # Test get_artifacts_dir returns user config value when plugin config is not set
24
+ assert general_config.get_artifacts_dir() == Path("user_artifacts")
25
+
26
+ # Test set_artifacts_dir updates plugin config
27
+ general_config.set_artifacts_dir(Path("plugin_artifacts"))
28
+ assert general_config.get_artifacts_dir() == Path("plugin_artifacts")
29
+
30
+ # Plugin config should take precedence
31
+ assert general_config.plugin_config.artifacts_dir == Path("plugin_artifacts")
32
+
33
+
34
+ def test_general_config_artifacts_default():
35
+ """Test GeneralConfig artifacts directory with default values."""
36
+ user_config = UserConfig(
37
+ networks={"localnet": NetworkConfigData()},
38
+ paths=PathConfig(artifacts=DEFAULT_ARTIFACTS_DIR),
39
+ )
40
+
41
+ plugin_config = PluginConfig()
42
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
43
+
44
+ # Should return default artifacts directory
45
+ assert general_config.get_artifacts_dir() == DEFAULT_ARTIFACTS_DIR
46
+
47
+
48
+ def test_general_config_artifacts_plugin_precedence():
49
+ """Test that plugin config takes precedence over user config for artifacts."""
50
+ user_config = UserConfig(
51
+ networks={"localnet": NetworkConfigData()},
52
+ paths=PathConfig(artifacts=Path("user_artifacts")),
53
+ )
54
+
55
+ plugin_config = PluginConfig(artifacts_dir=Path("plugin_artifacts"))
56
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
57
+
58
+ # Plugin config should take precedence
59
+ assert general_config.get_artifacts_dir() == Path("plugin_artifacts")
60
+
61
+
62
+ def test_general_config_artifacts_none_values():
63
+ """Test GeneralConfig behavior when artifacts paths are None."""
64
+ user_config = UserConfig(
65
+ networks={"localnet": NetworkConfigData()}, paths=PathConfig(artifacts=None)
66
+ )
67
+
68
+ plugin_config = PluginConfig(artifacts_dir=None)
69
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
70
+
71
+ # Should return None when both are None
72
+ assert general_config.get_artifacts_dir() is None
73
+
74
+
75
+ def test_general_config_both_contracts_and_artifacts():
76
+ """Test that both contracts and artifacts directories work together."""
77
+ user_config = UserConfig(
78
+ networks={"localnet": NetworkConfigData()},
79
+ paths=PathConfig(
80
+ contracts=Path("src/contracts"), artifacts=Path("build/artifacts")
81
+ ),
82
+ )
83
+
84
+ plugin_config = PluginConfig(
85
+ contracts_dir=Path("custom/contracts"), artifacts_dir=Path("custom/artifacts")
86
+ )
87
+
88
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
89
+
90
+ # Both should return plugin values (precedence)
91
+ assert general_config.get_contracts_dir() == Path("custom/contracts")
92
+ assert general_config.get_artifacts_dir() == Path("custom/artifacts")
93
+
94
+
95
+ def test_general_config_mixed_precedence():
96
+ """Test mixed precedence where only one path is overridden in plugin."""
97
+ user_config = UserConfig(
98
+ networks={"localnet": NetworkConfigData()},
99
+ paths=PathConfig(
100
+ contracts=Path("user/contracts"), artifacts=Path("user/artifacts")
101
+ ),
102
+ )
103
+
104
+ # Only override artifacts in plugin config
105
+ plugin_config = PluginConfig(artifacts_dir=Path("plugin/artifacts"))
106
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
107
+
108
+ # Contracts should come from user config, artifacts from plugin config
109
+ assert general_config.get_contracts_dir() == Path("user/contracts")
110
+ assert general_config.get_artifacts_dir() == Path("plugin/artifacts")
111
+
112
+
113
+ def test_path_config_validation():
114
+ """Test PathConfig validation for artifacts."""
115
+ # Valid path configurations
116
+ valid_config = PathConfig(contracts=Path("contracts"), artifacts=Path("artifacts"))
117
+ assert valid_config.contracts == Path("contracts")
118
+ assert valid_config.artifacts == Path("artifacts")
119
+
120
+ # Test with string paths
121
+ string_config = PathConfig(contracts="contracts", artifacts="artifacts")
122
+ # PathConfig should handle string conversion in __post_init__
123
+ assert string_config.contracts == "contracts"
124
+ assert string_config.artifacts == "artifacts"
125
+
126
+
127
+ def test_path_config_invalid_types():
128
+ """Test PathConfig validation with invalid types."""
129
+ # Test invalid artifacts type
130
+ with pytest.raises(ValueError, match="artifacts must be a string or Path"):
131
+ PathConfig(artifacts=123)
132
+
133
+ # Test invalid contracts type (existing validation)
134
+ with pytest.raises(ValueError, match="contracts must be a string or Path"):
135
+ PathConfig(contracts=123)
136
+
137
+
138
+ def test_general_config_contracts_default():
139
+ """Test GeneralConfig contracts directory with default values."""
140
+ user_config = UserConfig(
141
+ networks={"localnet": NetworkConfigData()},
142
+ paths=PathConfig(contracts=DEFAULT_CONTRACTS_DIR),
143
+ )
144
+
145
+ plugin_config = PluginConfig()
146
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
147
+
148
+ # Should return default contracts directory
149
+ assert general_config.get_contracts_dir() == DEFAULT_CONTRACTS_DIR
150
+
151
+
152
+ def test_general_config_leader_only_default():
153
+ """Test GeneralConfig leader_only with default values."""
154
+ user_config = UserConfig(
155
+ networks={"localnet": NetworkConfigData()},
156
+ )
157
+
158
+ plugin_config = PluginConfig()
159
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
160
+
161
+ # Should return False by default
162
+ assert general_config.get_leader_only() is False
163
+
164
+
165
+ def test_general_config_leader_only_network_config():
166
+ """Test GeneralConfig leader_only from network configuration."""
167
+ user_config = UserConfig(
168
+ networks={"localnet": NetworkConfigData(leader_only=True)},
169
+ default_network="localnet",
170
+ )
171
+
172
+ plugin_config = PluginConfig()
173
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
174
+
175
+ # Should return True from network config
176
+ assert general_config.get_leader_only() is True
177
+
178
+
179
+ def test_general_config_leader_only_plugin_precedence():
180
+ """Test that plugin config takes precedence over network config for leader_only."""
181
+ user_config = UserConfig(
182
+ networks={"localnet": NetworkConfigData(leader_only=False)},
183
+ default_network="localnet",
184
+ )
185
+
186
+ plugin_config = PluginConfig(leader_only=True)
187
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
188
+
189
+ # Plugin config should take precedence
190
+ assert general_config.get_leader_only() is True
191
+
192
+
193
+ def test_general_config_leader_only_multiple_networks():
194
+ """Test leader_only with multiple networks."""
195
+ user_config = UserConfig(
196
+ networks={
197
+ "localnet": NetworkConfigData(leader_only=False),
198
+ "testnet": NetworkConfigData(leader_only=True),
199
+ },
200
+ default_network="testnet",
201
+ )
202
+
203
+ plugin_config = PluginConfig()
204
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
205
+
206
+ # Should use the default network's leader_only value
207
+ assert general_config.get_leader_only() is True
208
+
209
+ # Change network via plugin config
210
+ plugin_config.network_name = "localnet"
211
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
212
+ assert general_config.get_leader_only() is False
213
+
214
+
215
+ def test_general_config_leader_only_network_not_found():
216
+ """Test leader_only when selected network is not found."""
217
+ user_config = UserConfig(
218
+ networks={"localnet": NetworkConfigData(leader_only=True)},
219
+ default_network="localnet",
220
+ )
221
+
222
+ plugin_config = PluginConfig(network_name="nonexistent")
223
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
224
+
225
+ # Should return False when network is not found
226
+ assert general_config.get_leader_only() is False
227
+
228
+
229
+ def test_check_local_rpc_with_localhost():
230
+ """Test check_local_rpc with localhost URL."""
231
+ user_config = UserConfig(
232
+ networks={"localnet": NetworkConfigData(url="http://localhost:8545")},
233
+ default_network="localnet",
234
+ )
235
+
236
+ plugin_config = PluginConfig()
237
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
238
+
239
+ assert general_config.check_local_rpc() is True
240
+
241
+
242
+ def test_check_local_rpc_with_127_0_0_1():
243
+ """Test check_local_rpc with 127.0.0.1 URL."""
244
+ user_config = UserConfig(
245
+ networks={"localnet": NetworkConfigData(url="http://127.0.0.1:8545")},
246
+ default_network="localnet",
247
+ )
248
+
249
+ plugin_config = PluginConfig()
250
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
251
+
252
+ assert general_config.check_local_rpc() is True
253
+
254
+
255
+ def test_check_local_rpc_with_external_url():
256
+ """Test check_local_rpc with external URL."""
257
+ user_config = UserConfig(
258
+ networks={"testnet": NetworkConfigData(url="https://api.genlayer.com:8545")},
259
+ default_network="testnet",
260
+ )
261
+
262
+ plugin_config = PluginConfig()
263
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
264
+
265
+ assert general_config.check_local_rpc() is False
266
+
267
+
268
+ def test_check_local_rpc_with_plugin_override():
269
+ """Test check_local_rpc with plugin config RPC URL override."""
270
+ user_config = UserConfig(
271
+ networks={"localnet": NetworkConfigData(url="https://external.com")},
272
+ default_network="localnet",
273
+ )
274
+
275
+ plugin_config = PluginConfig(rpc_url="http://localhost:9000")
276
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
277
+
278
+ # Plugin config should take precedence
279
+ assert general_config.check_local_rpc() is True
280
+
281
+
282
+ def test_check_studio_based_rpc_with_localhost():
283
+ """Test check_studio_based_rpc with localhost URL."""
284
+ user_config = UserConfig(
285
+ networks={"localnet": NetworkConfigData(url="http://localhost:8545")},
286
+ default_network="localnet",
287
+ )
288
+
289
+ plugin_config = PluginConfig()
290
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
291
+
292
+ assert general_config.check_studio_based_rpc() is True
293
+
294
+
295
+ def test_check_studio_based_rpc_with_127_0_0_1():
296
+ """Test check_studio_based_rpc with 127.0.0.1 URL."""
297
+ user_config = UserConfig(
298
+ networks={"localnet": NetworkConfigData(url="http://127.0.0.1:8545")},
299
+ default_network="localnet",
300
+ )
301
+
302
+ plugin_config = PluginConfig()
303
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
304
+
305
+ assert general_config.check_studio_based_rpc() is True
306
+
307
+
308
+ def test_check_studio_based_rpc_with_genlayer_subdomain():
309
+ """Test check_studio_based_rpc with .genlayer.com subdomains."""
310
+ test_cases = [
311
+ "https://api.genlayer.com:8545",
312
+ "https://test.genlayer.com",
313
+ "http://staging.api.genlayer.com:9000",
314
+ "https://dev.test.genlayer.com",
315
+ ]
316
+
317
+ for url in test_cases:
318
+ user_config = UserConfig(
319
+ networks={"testnet": NetworkConfigData(url=url)},
320
+ default_network="testnet",
321
+ )
322
+
323
+ plugin_config = PluginConfig()
324
+ general_config = GeneralConfig(
325
+ user_config=user_config, plugin_config=plugin_config
326
+ )
327
+
328
+ assert general_config.check_studio_based_rpc() is True, f"Failed for URL: {url}"
329
+
330
+
331
+ def test_check_studio_based_rpc_with_genlayerlabs_subdomain():
332
+ """Test check_studio_based_rpc with .genlayerlabs.com subdomains."""
333
+ test_cases = [
334
+ "https://api.genlayerlabs.com:8545",
335
+ "https://test.genlayerlabs.com",
336
+ "http://staging.api.genlayerlabs.com:9000",
337
+ "https://dev.test.genlayerlabs.com",
338
+ ]
339
+
340
+ for url in test_cases:
341
+ user_config = UserConfig(
342
+ networks={"testnet": NetworkConfigData(url=url)},
343
+ default_network="testnet",
344
+ )
345
+
346
+ plugin_config = PluginConfig()
347
+ general_config = GeneralConfig(
348
+ user_config=user_config, plugin_config=plugin_config
349
+ )
350
+
351
+ assert general_config.check_studio_based_rpc() is True, f"Failed for URL: {url}"
352
+
353
+
354
+ def test_check_studio_based_rpc_with_non_genlayer_domain():
355
+ """Test check_studio_based_rpc with non-GenLayer domains."""
356
+ test_cases = [
357
+ "https://api.example.com:8545",
358
+ "https://test.otherdomain.com",
359
+ "http://staging.api.random.org:9000",
360
+ "https://genlayer.example.com", # Not a subdomain of .genlayer.com
361
+ "https://genlayerlabs.example.com", # Not a subdomain of .genlayerlabs.com
362
+ ]
363
+
364
+ for url in test_cases:
365
+ user_config = UserConfig(
366
+ networks={"testnet": NetworkConfigData(url=url)},
367
+ default_network="testnet",
368
+ )
369
+
370
+ plugin_config = PluginConfig()
371
+ general_config = GeneralConfig(
372
+ user_config=user_config, plugin_config=plugin_config
373
+ )
374
+
375
+ assert (
376
+ general_config.check_studio_based_rpc() is False
377
+ ), f"Failed for URL: {url}"
378
+
379
+
380
+ def test_check_studio_based_rpc_with_plugin_override():
381
+ """Test check_studio_based_rpc with plugin config RPC URL override."""
382
+ # User config has external URL, but plugin overrides with GenLayer domain
383
+ user_config = UserConfig(
384
+ networks={"localnet": NetworkConfigData(url="https://external.com")},
385
+ default_network="localnet",
386
+ )
387
+
388
+ plugin_config = PluginConfig(rpc_url="https://api.genlayer.com:9000")
389
+ general_config = GeneralConfig(user_config=user_config, plugin_config=plugin_config)
390
+
391
+ # Plugin config should take precedence
392
+ assert general_config.check_studio_based_rpc() is True
393
+
394
+ # Test opposite case: user has GenLayer domain, plugin overrides with external
395
+ user_config2 = UserConfig(
396
+ networks={"localnet": NetworkConfigData(url="https://api.genlayer.com")},
397
+ default_network="localnet",
398
+ )
399
+
400
+ plugin_config2 = PluginConfig(rpc_url="https://external.com:9000")
401
+ general_config2 = GeneralConfig(
402
+ user_config=user_config2, plugin_config=plugin_config2
403
+ )
404
+
405
+ # Plugin config should take precedence
406
+ assert general_config2.check_studio_based_rpc() is False
@@ -8,6 +8,8 @@ def test_help_message(pytester):
8
8
  "gltest:",
9
9
  " --contracts-dir=CONTRACTS_DIR",
10
10
  " Path to directory containing contract files",
11
+ " --artifacts-dir=ARTIFACTS_DIR",
12
+ " Path to directory for storing contract artifacts",
11
13
  " --default-wait-interval=DEFAULT_WAIT_INTERVAL",
12
14
  " Default interval (ms) between transaction receipt checks",
13
15
  " --default-wait-retries=DEFAULT_WAIT_RETRIES",
@@ -15,6 +17,8 @@ def test_help_message(pytester):
15
17
  " --rpc-url=RPC_URL RPC endpoint URL for the GenLayer network",
16
18
  " --network=NETWORK Target network (defaults to 'localnet' if no config",
17
19
  " file)",
20
+ " --test-with-mocks Test with mocks",
21
+ " --leader-only Run contracts in leader-only mode",
18
22
  ]
19
23
  )
20
24
 
@@ -125,3 +129,166 @@ def test_network_testnet(pytester):
125
129
  ]
126
130
  )
127
131
  assert result.ret == 0
132
+
133
+
134
+ def test_artifacts_dir(pytester):
135
+ """Test that artifacts directory CLI parameter works correctly."""
136
+ pytester.makepyfile(
137
+ """
138
+ from gltest_cli.config.general import get_general_config
139
+ from pathlib import Path
140
+
141
+ def test_artifacts_dir():
142
+ general_config = get_general_config()
143
+ assert general_config.get_artifacts_dir() == Path("custom/artifacts")
144
+ """
145
+ )
146
+
147
+ result = pytester.runpytest("--artifacts-dir=custom/artifacts", "-v")
148
+
149
+ result.stdout.fnmatch_lines(
150
+ [
151
+ "*::test_artifacts_dir PASSED*",
152
+ ]
153
+ )
154
+ assert result.ret == 0
155
+
156
+
157
+ def test_contracts_and_artifacts_dirs(pytester):
158
+ """Test that both contracts and artifacts directories can be set via CLI."""
159
+ pytester.makepyfile(
160
+ """
161
+ from gltest_cli.config.general import get_general_config
162
+ from pathlib import Path
163
+
164
+ def test_both_dirs():
165
+ general_config = get_general_config()
166
+ assert general_config.get_contracts_dir() == Path("src/contracts")
167
+ assert general_config.get_artifacts_dir() == Path("build/artifacts")
168
+ """
169
+ )
170
+
171
+ result = pytester.runpytest(
172
+ "--contracts-dir=src/contracts", "--artifacts-dir=build/artifacts", "-v"
173
+ )
174
+
175
+ result.stdout.fnmatch_lines(
176
+ [
177
+ "*::test_both_dirs PASSED*",
178
+ ]
179
+ )
180
+ assert result.ret == 0
181
+
182
+
183
+ def test_test_with_mocks_true(pytester):
184
+ pytester.makepyfile(
185
+ """
186
+ from gltest_cli.config.general import get_general_config
187
+
188
+ def test_test_with_mocks():
189
+ general_config = get_general_config()
190
+ assert general_config.get_test_with_mocks() == True
191
+ """
192
+ )
193
+
194
+ result = pytester.runpytest("--test-with-mocks", "-v")
195
+
196
+ result.stdout.fnmatch_lines(
197
+ [
198
+ "*::test_test_with_mocks PASSED*",
199
+ ]
200
+ )
201
+ assert result.ret == 0
202
+
203
+
204
+ def test_test_with_mocks_false(pytester):
205
+ pytester.makepyfile(
206
+ """
207
+ from gltest_cli.config.general import get_general_config
208
+
209
+ def test_test_with_mocks():
210
+ general_config = get_general_config()
211
+ assert general_config.get_test_with_mocks() == False
212
+ "*::test_test_with_mocks PASSED*",
213
+
214
+ """
215
+ )
216
+
217
+ result = pytester.runpytest("-v")
218
+
219
+ result.stdout.fnmatch_lines(
220
+ [
221
+ "*::test_test_with_mocks PASSED*",
222
+ ]
223
+ )
224
+ assert result.ret == 0
225
+
226
+
227
+ def test_artifacts_dir_default_fallback(pytester):
228
+ """Test that artifacts directory falls back to config file default when CLI not provided."""
229
+ pytester.makepyfile(
230
+ """
231
+ from gltest_cli.config.general import get_general_config
232
+ from pathlib import Path
233
+
234
+ def test_artifacts_default():
235
+ general_config = get_general_config()
236
+ # Should use the default from config
237
+ artifacts_dir = general_config.get_artifacts_dir()
238
+ assert isinstance(artifacts_dir, Path)
239
+ # Default should be 'artifacts'
240
+ assert str(artifacts_dir) == "artifacts"
241
+
242
+ """
243
+ )
244
+
245
+ result = pytester.runpytest("-v")
246
+
247
+ result.stdout.fnmatch_lines(
248
+ [
249
+ "*::test_artifacts_default PASSED*",
250
+ ]
251
+ )
252
+ assert result.ret == 0
253
+
254
+
255
+ def test_leader_only_true(pytester):
256
+ pytester.makepyfile(
257
+ """
258
+ from gltest_cli.config.general import get_general_config
259
+
260
+ def test_leader_only():
261
+ general_config = get_general_config()
262
+ assert general_config.get_leader_only() == True
263
+ """
264
+ )
265
+
266
+ result = pytester.runpytest("--leader-only", "-v")
267
+
268
+ result.stdout.fnmatch_lines(
269
+ [
270
+ "*::test_leader_only PASSED*",
271
+ ]
272
+ )
273
+ assert result.ret == 0
274
+
275
+
276
+ def test_leader_only_false(pytester):
277
+ pytester.makepyfile(
278
+ """
279
+ from gltest_cli.config.general import get_general_config
280
+
281
+ def test_leader_only():
282
+ general_config = get_general_config()
283
+ assert general_config.get_leader_only() == False
284
+ """
285
+ )
286
+
287
+ result = pytester.runpytest("-v")
288
+
289
+ result.stdout.fnmatch_lines(
290
+ [
291
+ "*::test_leader_only PASSED*",
292
+ ]
293
+ )
294
+ assert result.ret == 0
@@ -11,6 +11,7 @@ from gltest_cli.config.user import (
11
11
  DEFAULT_NETWORK,
12
12
  DEFAULT_ENVIRONMENT,
13
13
  DEFAULT_CONTRACTS_DIR,
14
+ DEFAULT_ARTIFACTS_DIR,
14
15
  )
15
16
  from gltest_cli.config.constants import DEFAULT_RPC_URL
16
17
  from gltest_cli.config.types import UserConfig, NetworkConfigData, PathConfig
@@ -33,7 +34,7 @@ VALID_CONFIG = {
33
34
  "from": "0x123",
34
35
  },
35
36
  },
36
- "paths": {"contracts": "contracts"},
37
+ "paths": {"contracts": "contracts", "artifacts": "artifacts"},
37
38
  "environment": ".env",
38
39
  }
39
40
 
@@ -73,6 +74,7 @@ def test_get_default_user_config():
73
74
  # Check paths
74
75
  assert isinstance(config.paths, PathConfig)
75
76
  assert config.paths.contracts == DEFAULT_CONTRACTS_DIR
77
+ assert config.paths.artifacts == DEFAULT_ARTIFACTS_DIR
76
78
 
77
79
  # Check environment
78
80
  assert config.environment == DEFAULT_ENVIRONMENT
@@ -135,6 +137,11 @@ def test_validate_raw_user_config_invalid():
135
137
  {"networks": {"default": "localnet", "localnet": {"from": 123}}}
136
138
  )
137
139
 
140
+ with pytest.raises(ValueError, match="leader_only must be a boolean"):
141
+ validate_raw_user_config(
142
+ {"networks": {"default": "localnet", "localnet": {"leader_only": "true"}}}
143
+ )
144
+
138
145
  with pytest.raises(ValueError, match="paths must be a dictionary"):
139
146
  validate_raw_user_config({"paths": "not_a_dict"})
140
147
 
@@ -180,6 +187,11 @@ def test_validate_raw_user_config_invalid():
180
187
  {"networks": {"default": "localnet", "testnet": {"from": 123}}}
181
188
  )
182
189
 
190
+ with pytest.raises(ValueError, match="leader_only must be a boolean"):
191
+ validate_raw_user_config(
192
+ {"networks": {"default": "localnet", "testnet": {"leader_only": "true"}}}
193
+ )
194
+
183
195
  # Test required fields for non-default networks
184
196
  with pytest.raises(ValueError, match="network testnet must have an id"):
185
197
  validate_raw_user_config(
@@ -255,6 +267,7 @@ def test_load_user_config(mock_load_dotenv, mock_file):
255
267
  # Check paths
256
268
  assert isinstance(config.paths, PathConfig)
257
269
  assert config.paths.contracts == Path("contracts")
270
+ assert config.paths.artifacts == Path("artifacts")
258
271
 
259
272
  # Check environment
260
273
  assert config.environment == ".env"
@@ -349,3 +362,50 @@ def test_user_config_exists(mock_cwd):
349
362
  # Test with no files
350
363
  mock_path.iterdir.return_value = []
351
364
  assert user_config_exists() is False
365
+
366
+
367
+ # Tests for artifacts directory functionality
368
+ def test_artifacts_path_in_config():
369
+ """Test that artifacts path is properly handled in configuration."""
370
+ config_with_artifacts = {
371
+ "networks": {"default": "localnet"},
372
+ "paths": {"contracts": "contracts", "artifacts": "build/artifacts"},
373
+ }
374
+
375
+ config = transform_raw_to_user_config_with_defaults(config_with_artifacts)
376
+ assert config.paths.artifacts == Path("build/artifacts")
377
+
378
+
379
+ def test_artifacts_path_defaults():
380
+ """Test that artifacts path defaults to DEFAULT_ARTIFACTS_DIR when not specified."""
381
+ config_without_artifacts = {
382
+ "networks": {"default": "localnet"},
383
+ "paths": {"contracts": "contracts"},
384
+ }
385
+
386
+ config = transform_raw_to_user_config_with_defaults(config_without_artifacts)
387
+ assert config.paths.artifacts == DEFAULT_ARTIFACTS_DIR
388
+
389
+
390
+ def test_artifacts_path_validation():
391
+ """Test validation of artifacts path configuration."""
392
+ # Valid config with artifacts
393
+ valid_config = {"paths": {"artifacts": "custom/artifacts"}}
394
+ validate_raw_user_config(valid_config) # Should not raise
395
+
396
+ # Test that artifacts is included in valid path keys
397
+ from gltest_cli.config.user import VALID_PATHS_KEYS
398
+
399
+ assert "artifacts" in VALID_PATHS_KEYS
400
+
401
+
402
+ def test_artifacts_path_only_config():
403
+ """Test configuration with only artifacts path specified."""
404
+ config_artifacts_only = {
405
+ "networks": {"default": "localnet"},
406
+ "paths": {"artifacts": "my_artifacts"},
407
+ }
408
+
409
+ config = transform_raw_to_user_config_with_defaults(config_artifacts_only)
410
+ assert config.paths.contracts == DEFAULT_CONTRACTS_DIR
411
+ assert config.paths.artifacts == Path("my_artifacts")