genlayer-test 0.4.1__tar.gz → 2.0.0__tar.gz
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.
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/PKG-INFO +75 -14
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/README.md +73 -12
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/PKG-INFO +75 -14
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/SOURCES.txt +13 -4
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/requires.txt +1 -1
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/__init__.py +7 -6
- genlayer_test-0.4.1/gltest/glchain/client.py → genlayer_test-2.0.0/gltest/clients.py +1 -1
- genlayer_test-2.0.0/gltest/contracts/__init__.py +4 -0
- genlayer_test-2.0.0/gltest/contracts/contract.py +197 -0
- genlayer_test-0.4.1/gltest/glchain/contract.py → genlayer_test-2.0.0/gltest/contracts/contract_factory.py +22 -137
- genlayer_test-2.0.0/gltest/contracts/contract_functions.py +59 -0
- genlayer_test-2.0.0/gltest/contracts/method_stats.py +163 -0
- genlayer_test-2.0.0/gltest/contracts/stats_collector.py +259 -0
- genlayer_test-2.0.0/gltest/contracts/utils.py +12 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/fixtures.py +2 -6
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/helpers/take_snapshot.py +1 -1
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/constants.py +1 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/plugin.py +53 -0
- genlayer_test-2.0.0/gltest_cli/config/pytest_context.py +9 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/types.py +41 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/user.py +21 -8
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/pyproject.toml +2 -2
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/football_prediction_market.py +1 -1
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_football_prediction_market.py +2 -2
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_intelligent_oracle_factory.py +6 -6
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_llm_erc20.py +5 -5
- genlayer_test-2.0.0/tests/examples/tests/test_llm_erc20_analyze.py +50 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_log_indexer.py +23 -11
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_multi_file_contract.py +2 -2
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_multi_file_contract_legacy.py +2 -2
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_multi_read_erc20.py +14 -12
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_multi_tenant_storage.py +11 -7
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_read_erc20.py +1 -1
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_storage.py +4 -4
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_storage_legacy.py +5 -3
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_user_storage.py +20 -10
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/tests/test_wizard_of_coin.py +1 -1
- genlayer_test-2.0.0/tests/gltest_cli/config/test_config_integration.py +432 -0
- genlayer_test-2.0.0/tests/gltest_cli/config/test_general_config.py +406 -0
- genlayer_test-2.0.0/tests/gltest_cli/config/test_plugin.py +294 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest_cli/config/test_user.py +61 -1
- genlayer_test-0.4.1/gltest/glchain/__init__.py +0 -16
- genlayer_test-0.4.1/tests/gltest_cli/config/test_plugin.py +0 -127
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/LICENSE +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/dependency_links.txt +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/entry_points.txt +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/genlayer_test.egg-info/top_level.txt +0 -0
- /genlayer_test-0.4.1/gltest/glchain/account.py → /genlayer_test-2.0.0/gltest/accounts.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/artifacts/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/artifacts/contract.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/assertions.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/exceptions.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/helpers/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/helpers/fixture_snapshot.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/logging.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest/types.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/config/general.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/logging.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/gltest_cli/main.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/setup.cfg +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/conftest.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/intelligent_oracle.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/intelligent_oracle_factory.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/llm_erc20.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/log_indexer.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/multi_file_contract/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/multi_file_contract/other.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/multi_read_erc20.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/multi_tenant_storage.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/read_erc20.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/storage.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/user_storage.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/examples/contracts/wizard_of_coin.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/artifact/__init__.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_1.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_2.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/artifact/contracts/not_ic_contract.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/artifact/test_contract_definition.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest/assertions/test_assertions.py +0 -0
- {genlayer_test-0.4.1 → genlayer_test-2.0.0}/tests/gltest_cli/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: genlayer-test
|
3
|
-
Version: 0.
|
3
|
+
Version: 2.0.0
|
4
4
|
Summary: GenLayer Testing Suite
|
5
5
|
Author: GenLayer
|
6
6
|
License-Expression: MIT
|
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
16
16
|
Requires-Dist: pytest
|
17
17
|
Requires-Dist: setuptools>=77.0
|
18
|
-
Requires-Dist: genlayer-py==0.
|
18
|
+
Requires-Dist: genlayer-py==0.7.2
|
19
19
|
Requires-Dist: colorama>=0.4.6
|
20
20
|
Requires-Dist: pyyaml
|
21
21
|
Requires-Dist: python-dotenv
|
@@ -59,8 +59,8 @@ contract = factory.deploy(account=other_account)
|
|
59
59
|
assert contract.account == other_account
|
60
60
|
|
61
61
|
# Interact with the contract
|
62
|
-
result = contract.get_value() # Read method
|
63
|
-
tx_receipt = contract.set_value(args=["new_value"]) # Write method
|
62
|
+
result = contract.get_value().call() # Read method
|
63
|
+
tx_receipt = contract.set_value(args=["new_value"]).transact() # Write method
|
64
64
|
|
65
65
|
assert tx_execution_succeeded(tx_receipt)
|
66
66
|
```
|
@@ -114,6 +114,7 @@ networks:
|
|
114
114
|
|
115
115
|
localnet: # Local development network configuration
|
116
116
|
url: "http://127.0.0.1:4000/api"
|
117
|
+
leader_only: false # Set to true to run all contracts in leader-only mode by default
|
117
118
|
|
118
119
|
testnet_asimov: # Test network configuration
|
119
120
|
id: 4221
|
@@ -125,6 +126,7 @@ networks:
|
|
125
126
|
|
126
127
|
paths:
|
127
128
|
contracts: "contracts" # Path to your contracts directory
|
129
|
+
artifacts: "artifacts" # Path to your artifacts directory
|
128
130
|
|
129
131
|
environment: .env # Path to your environment file containing private keys and other secrets
|
130
132
|
```
|
@@ -137,12 +139,14 @@ Key configuration sections:
|
|
137
139
|
- `url`: The RPC endpoint for the network
|
138
140
|
- `id`: Chain ID
|
139
141
|
- `accounts`: List of account private keys (using environment variables)
|
142
|
+
- `leader_only`: Leader only mode
|
140
143
|
- Special case for `localnet`:
|
141
144
|
- If a network is named `localnet`, missing fields will be filled with default values
|
142
145
|
- For all other network names, `id`, `url`, and `accounts` are required fields
|
143
146
|
|
144
147
|
2. **Paths**: Define important directory paths
|
145
148
|
- `contracts`: Location of your contract files
|
149
|
+
- `artifacts`: Location of your artifacts files (analysis results will be stored here)
|
146
150
|
|
147
151
|
3. **Environment**: Path to your `.env` file containing sensitive information like private keys
|
148
152
|
|
@@ -231,6 +235,20 @@ def test_with_mocked_llm(setup_validators):
|
|
231
235
|
|
232
236
|
Note: This feature is only available when running tests on localnet.
|
233
237
|
|
238
|
+
11. Run tests with leader-only mode enabled
|
239
|
+
```bash
|
240
|
+
$ gltest --leader-only
|
241
|
+
```
|
242
|
+
The `--leader-only` flag configures all contract deployments and write operations to run only on the leader node. This is useful for:
|
243
|
+
- Faster test execution by avoiding consensus
|
244
|
+
- Testing specific leader-only scenarios
|
245
|
+
- Development and debugging purposes
|
246
|
+
- Reducing computational overhead in test environments
|
247
|
+
|
248
|
+
When this flag is enabled, all contracts deployed and all write transactions will automatically use leader-only mode, regardless of individual method parameters.
|
249
|
+
|
250
|
+
**Note:** Leader-only mode is only available for studio-based networks (localhost, 127.0.0.1, *.genlayer.com, *.genlayerlabs.com). When enabled on other networks, it will have no effect and a warning will be logged.
|
251
|
+
|
234
252
|
## 🚀 Key Features
|
235
253
|
|
236
254
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -251,8 +269,9 @@ Before diving into the examples, let's understand the basic project structure:
|
|
251
269
|
genlayer-example/
|
252
270
|
├── contracts/ # Contract definitions
|
253
271
|
│ └── storage.py # Example storage contract
|
254
|
-
|
255
|
-
|
272
|
+
├── test/ # Test files
|
273
|
+
│ └── test_contract.py # Contract test cases
|
274
|
+
└── gltest.config.yaml # Configuration file
|
256
275
|
```
|
257
276
|
|
258
277
|
### Storage Contract Example
|
@@ -311,7 +330,6 @@ def test_deployment():
|
|
311
330
|
args=["initial_value"], # Constructor arguments
|
312
331
|
account=get_default_account(), # Account to deploy from
|
313
332
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
314
|
-
leader_only=False, # Optional: whether to run only on leader
|
315
333
|
)
|
316
334
|
|
317
335
|
# Contract is now deployed and ready to use
|
@@ -320,7 +338,7 @@ def test_deployment():
|
|
320
338
|
|
321
339
|
### Read Methods
|
322
340
|
|
323
|
-
Reading from the contract
|
341
|
+
Reading from the contract requires calling `.call()` on the method:
|
324
342
|
|
325
343
|
```python
|
326
344
|
from gltest import get_contract_factory
|
@@ -332,7 +350,7 @@ def test_read_methods():
|
|
332
350
|
contract = factory.deploy()
|
333
351
|
|
334
352
|
# Call a read-only method
|
335
|
-
result = contract.
|
353
|
+
result = contract.get_storage(args=[]).call()
|
336
354
|
|
337
355
|
# Assert the result matches the initial value
|
338
356
|
assert result == "initial_value"
|
@@ -340,7 +358,7 @@ def test_read_methods():
|
|
340
358
|
|
341
359
|
### Write Methods
|
342
360
|
|
343
|
-
Writing to the contract requires transaction
|
361
|
+
Writing to the contract requires calling `.transact()` on the method. Method arguments are passed to the write method, while transaction parameters are passed to `.transact()`:
|
344
362
|
|
345
363
|
```python
|
346
364
|
from gltest import get_contract_factory
|
@@ -354,9 +372,9 @@ def test_write_methods():
|
|
354
372
|
# Call a write method with arguments
|
355
373
|
tx_receipt = contract.update_storage(
|
356
374
|
args=["new_value"], # Method arguments
|
375
|
+
).transact(
|
357
376
|
value=0, # Optional: amount of native currency to send
|
358
377
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
359
|
-
leader_only=False, # Optional: whether to run only on leader
|
360
378
|
wait_interval=1, # Optional: seconds between status checks
|
361
379
|
wait_retries=10, # Optional: max number of retries
|
362
380
|
)
|
@@ -365,7 +383,7 @@ def test_write_methods():
|
|
365
383
|
assert tx_execution_succeeded(tx_receipt)
|
366
384
|
|
367
385
|
# Verify the value was updated
|
368
|
-
assert contract.get_storage() == "new_value"
|
386
|
+
assert contract.get_storage().call() == "new_value"
|
369
387
|
```
|
370
388
|
|
371
389
|
### Assertions
|
@@ -503,6 +521,50 @@ Fixtures help maintain clean, DRY test code by:
|
|
503
521
|
- Ensuring consistent test environments
|
504
522
|
- Managing resource cleanup automatically
|
505
523
|
- Providing appropriate scoping for performance
|
524
|
+
### Statistical Analysis with `.analyze()`
|
525
|
+
|
526
|
+
The GenLayer Testing Suite provides a powerful `.analyze()` method for write operations that performs statistical analysis through multiple simulation runs. This is particularly useful for testing LLM-based contracts where outputs may vary:
|
527
|
+
|
528
|
+
```python
|
529
|
+
from gltest import get_contract_factory
|
530
|
+
|
531
|
+
def test_analyze_method():
|
532
|
+
factory = get_contract_factory("LlmContract")
|
533
|
+
contract = factory.deploy()
|
534
|
+
|
535
|
+
# Analyze a write method's behavior across multiple runs
|
536
|
+
analysis = contract.process_with_llm(args=["input_data"]).analyze(
|
537
|
+
provider="openai", # LLM provider
|
538
|
+
model="gpt-4o", # Model to use
|
539
|
+
runs=100, # Number of simulation runs (default: 100)
|
540
|
+
config=None, # Optional: provider-specific config
|
541
|
+
plugin=None, # Optional: plugin name
|
542
|
+
plugin_config=None, # Optional: plugin configuration
|
543
|
+
)
|
544
|
+
|
545
|
+
# Access analysis results
|
546
|
+
print(f"Method: {analysis.method}")
|
547
|
+
print(f"Success rate: {analysis.success_rate:.2f}%")
|
548
|
+
print(f"Reliability score: {analysis.reliability_score:.2f}%")
|
549
|
+
print(f"Unique states: {analysis.unique_states}")
|
550
|
+
print(f"Execution time: {analysis.execution_time:.1f}s")
|
551
|
+
|
552
|
+
# The analysis returns a MethodStatsSummary object with:
|
553
|
+
# - method: The contract method name
|
554
|
+
# - args: Arguments passed to the method
|
555
|
+
# - total_runs: Total number of simulation runs
|
556
|
+
# - successful_runs: Number of successful executions
|
557
|
+
# - failed_runs: Number of failed executions
|
558
|
+
# - unique_states: Number of unique contract states observed
|
559
|
+
# - reliability_score: Percentage of runs with the most common state
|
560
|
+
# - execution_time: Total time for all simulations
|
561
|
+
```
|
562
|
+
|
563
|
+
The `.analyze()` method helps you:
|
564
|
+
- Test non-deterministic contract methods
|
565
|
+
- Measure consistency of LLM-based operations
|
566
|
+
- Identify edge cases and failure patterns
|
567
|
+
- Benchmark performance across multiple runs
|
506
568
|
|
507
569
|
## 📝 Best Practices
|
508
570
|
|
@@ -546,6 +608,7 @@ Fixtures help maintain clean, DRY test code by:
|
|
546
608
|
```python
|
547
609
|
tx_receipt = contract.set_value(
|
548
610
|
args=["new_value"],
|
611
|
+
).transact(
|
549
612
|
wait_interval=2, # Increase wait interval between status checks
|
550
613
|
wait_retries=20, # Increase number of retry attempts
|
551
614
|
)
|
@@ -558,13 +621,11 @@ Fixtures help maintain clean, DRY test code by:
|
|
558
621
|
# Try with increased consensus parameters
|
559
622
|
contract = factory.deploy(
|
560
623
|
consensus_max_rotations=5, # Increase number of consensus rotations
|
561
|
-
leader_only=True, # Try leader-only mode for faster execution
|
562
624
|
)
|
563
625
|
|
564
626
|
# For critical operations, use more conservative settings
|
565
627
|
contract = factory.deploy(
|
566
628
|
consensus_max_rotations=10, # More rotations for better reliability
|
567
|
-
leader_only=False, # Full consensus for better security
|
568
629
|
wait_interval=3, # Longer wait between checks
|
569
630
|
wait_retries=30 # More retries for consensus
|
570
631
|
)
|
@@ -36,8 +36,8 @@ contract = factory.deploy(account=other_account)
|
|
36
36
|
assert contract.account == other_account
|
37
37
|
|
38
38
|
# Interact with the contract
|
39
|
-
result = contract.get_value() # Read method
|
40
|
-
tx_receipt = contract.set_value(args=["new_value"]) # Write method
|
39
|
+
result = contract.get_value().call() # Read method
|
40
|
+
tx_receipt = contract.set_value(args=["new_value"]).transact() # Write method
|
41
41
|
|
42
42
|
assert tx_execution_succeeded(tx_receipt)
|
43
43
|
```
|
@@ -91,6 +91,7 @@ networks:
|
|
91
91
|
|
92
92
|
localnet: # Local development network configuration
|
93
93
|
url: "http://127.0.0.1:4000/api"
|
94
|
+
leader_only: false # Set to true to run all contracts in leader-only mode by default
|
94
95
|
|
95
96
|
testnet_asimov: # Test network configuration
|
96
97
|
id: 4221
|
@@ -102,6 +103,7 @@ networks:
|
|
102
103
|
|
103
104
|
paths:
|
104
105
|
contracts: "contracts" # Path to your contracts directory
|
106
|
+
artifacts: "artifacts" # Path to your artifacts directory
|
105
107
|
|
106
108
|
environment: .env # Path to your environment file containing private keys and other secrets
|
107
109
|
```
|
@@ -114,12 +116,14 @@ Key configuration sections:
|
|
114
116
|
- `url`: The RPC endpoint for the network
|
115
117
|
- `id`: Chain ID
|
116
118
|
- `accounts`: List of account private keys (using environment variables)
|
119
|
+
- `leader_only`: Leader only mode
|
117
120
|
- Special case for `localnet`:
|
118
121
|
- If a network is named `localnet`, missing fields will be filled with default values
|
119
122
|
- For all other network names, `id`, `url`, and `accounts` are required fields
|
120
123
|
|
121
124
|
2. **Paths**: Define important directory paths
|
122
125
|
- `contracts`: Location of your contract files
|
126
|
+
- `artifacts`: Location of your artifacts files (analysis results will be stored here)
|
123
127
|
|
124
128
|
3. **Environment**: Path to your `.env` file containing sensitive information like private keys
|
125
129
|
|
@@ -208,6 +212,20 @@ def test_with_mocked_llm(setup_validators):
|
|
208
212
|
|
209
213
|
Note: This feature is only available when running tests on localnet.
|
210
214
|
|
215
|
+
11. Run tests with leader-only mode enabled
|
216
|
+
```bash
|
217
|
+
$ gltest --leader-only
|
218
|
+
```
|
219
|
+
The `--leader-only` flag configures all contract deployments and write operations to run only on the leader node. This is useful for:
|
220
|
+
- Faster test execution by avoiding consensus
|
221
|
+
- Testing specific leader-only scenarios
|
222
|
+
- Development and debugging purposes
|
223
|
+
- Reducing computational overhead in test environments
|
224
|
+
|
225
|
+
When this flag is enabled, all contracts deployed and all write transactions will automatically use leader-only mode, regardless of individual method parameters.
|
226
|
+
|
227
|
+
**Note:** Leader-only mode is only available for studio-based networks (localhost, 127.0.0.1, *.genlayer.com, *.genlayerlabs.com). When enabled on other networks, it will have no effect and a warning will be logged.
|
228
|
+
|
211
229
|
## 🚀 Key Features
|
212
230
|
|
213
231
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -228,8 +246,9 @@ Before diving into the examples, let's understand the basic project structure:
|
|
228
246
|
genlayer-example/
|
229
247
|
├── contracts/ # Contract definitions
|
230
248
|
│ └── storage.py # Example storage contract
|
231
|
-
|
232
|
-
|
249
|
+
├── test/ # Test files
|
250
|
+
│ └── test_contract.py # Contract test cases
|
251
|
+
└── gltest.config.yaml # Configuration file
|
233
252
|
```
|
234
253
|
|
235
254
|
### Storage Contract Example
|
@@ -288,7 +307,6 @@ def test_deployment():
|
|
288
307
|
args=["initial_value"], # Constructor arguments
|
289
308
|
account=get_default_account(), # Account to deploy from
|
290
309
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
291
|
-
leader_only=False, # Optional: whether to run only on leader
|
292
310
|
)
|
293
311
|
|
294
312
|
# Contract is now deployed and ready to use
|
@@ -297,7 +315,7 @@ def test_deployment():
|
|
297
315
|
|
298
316
|
### Read Methods
|
299
317
|
|
300
|
-
Reading from the contract
|
318
|
+
Reading from the contract requires calling `.call()` on the method:
|
301
319
|
|
302
320
|
```python
|
303
321
|
from gltest import get_contract_factory
|
@@ -309,7 +327,7 @@ def test_read_methods():
|
|
309
327
|
contract = factory.deploy()
|
310
328
|
|
311
329
|
# Call a read-only method
|
312
|
-
result = contract.
|
330
|
+
result = contract.get_storage(args=[]).call()
|
313
331
|
|
314
332
|
# Assert the result matches the initial value
|
315
333
|
assert result == "initial_value"
|
@@ -317,7 +335,7 @@ def test_read_methods():
|
|
317
335
|
|
318
336
|
### Write Methods
|
319
337
|
|
320
|
-
Writing to the contract requires transaction
|
338
|
+
Writing to the contract requires calling `.transact()` on the method. Method arguments are passed to the write method, while transaction parameters are passed to `.transact()`:
|
321
339
|
|
322
340
|
```python
|
323
341
|
from gltest import get_contract_factory
|
@@ -331,9 +349,9 @@ def test_write_methods():
|
|
331
349
|
# Call a write method with arguments
|
332
350
|
tx_receipt = contract.update_storage(
|
333
351
|
args=["new_value"], # Method arguments
|
352
|
+
).transact(
|
334
353
|
value=0, # Optional: amount of native currency to send
|
335
354
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
336
|
-
leader_only=False, # Optional: whether to run only on leader
|
337
355
|
wait_interval=1, # Optional: seconds between status checks
|
338
356
|
wait_retries=10, # Optional: max number of retries
|
339
357
|
)
|
@@ -342,7 +360,7 @@ def test_write_methods():
|
|
342
360
|
assert tx_execution_succeeded(tx_receipt)
|
343
361
|
|
344
362
|
# Verify the value was updated
|
345
|
-
assert contract.get_storage() == "new_value"
|
363
|
+
assert contract.get_storage().call() == "new_value"
|
346
364
|
```
|
347
365
|
|
348
366
|
### Assertions
|
@@ -480,6 +498,50 @@ Fixtures help maintain clean, DRY test code by:
|
|
480
498
|
- Ensuring consistent test environments
|
481
499
|
- Managing resource cleanup automatically
|
482
500
|
- Providing appropriate scoping for performance
|
501
|
+
### Statistical Analysis with `.analyze()`
|
502
|
+
|
503
|
+
The GenLayer Testing Suite provides a powerful `.analyze()` method for write operations that performs statistical analysis through multiple simulation runs. This is particularly useful for testing LLM-based contracts where outputs may vary:
|
504
|
+
|
505
|
+
```python
|
506
|
+
from gltest import get_contract_factory
|
507
|
+
|
508
|
+
def test_analyze_method():
|
509
|
+
factory = get_contract_factory("LlmContract")
|
510
|
+
contract = factory.deploy()
|
511
|
+
|
512
|
+
# Analyze a write method's behavior across multiple runs
|
513
|
+
analysis = contract.process_with_llm(args=["input_data"]).analyze(
|
514
|
+
provider="openai", # LLM provider
|
515
|
+
model="gpt-4o", # Model to use
|
516
|
+
runs=100, # Number of simulation runs (default: 100)
|
517
|
+
config=None, # Optional: provider-specific config
|
518
|
+
plugin=None, # Optional: plugin name
|
519
|
+
plugin_config=None, # Optional: plugin configuration
|
520
|
+
)
|
521
|
+
|
522
|
+
# Access analysis results
|
523
|
+
print(f"Method: {analysis.method}")
|
524
|
+
print(f"Success rate: {analysis.success_rate:.2f}%")
|
525
|
+
print(f"Reliability score: {analysis.reliability_score:.2f}%")
|
526
|
+
print(f"Unique states: {analysis.unique_states}")
|
527
|
+
print(f"Execution time: {analysis.execution_time:.1f}s")
|
528
|
+
|
529
|
+
# The analysis returns a MethodStatsSummary object with:
|
530
|
+
# - method: The contract method name
|
531
|
+
# - args: Arguments passed to the method
|
532
|
+
# - total_runs: Total number of simulation runs
|
533
|
+
# - successful_runs: Number of successful executions
|
534
|
+
# - failed_runs: Number of failed executions
|
535
|
+
# - unique_states: Number of unique contract states observed
|
536
|
+
# - reliability_score: Percentage of runs with the most common state
|
537
|
+
# - execution_time: Total time for all simulations
|
538
|
+
```
|
539
|
+
|
540
|
+
The `.analyze()` method helps you:
|
541
|
+
- Test non-deterministic contract methods
|
542
|
+
- Measure consistency of LLM-based operations
|
543
|
+
- Identify edge cases and failure patterns
|
544
|
+
- Benchmark performance across multiple runs
|
483
545
|
|
484
546
|
## 📝 Best Practices
|
485
547
|
|
@@ -523,6 +585,7 @@ Fixtures help maintain clean, DRY test code by:
|
|
523
585
|
```python
|
524
586
|
tx_receipt = contract.set_value(
|
525
587
|
args=["new_value"],
|
588
|
+
).transact(
|
526
589
|
wait_interval=2, # Increase wait interval between status checks
|
527
590
|
wait_retries=20, # Increase number of retry attempts
|
528
591
|
)
|
@@ -535,13 +598,11 @@ Fixtures help maintain clean, DRY test code by:
|
|
535
598
|
# Try with increased consensus parameters
|
536
599
|
contract = factory.deploy(
|
537
600
|
consensus_max_rotations=5, # Increase number of consensus rotations
|
538
|
-
leader_only=True, # Try leader-only mode for faster execution
|
539
601
|
)
|
540
602
|
|
541
603
|
# For critical operations, use more conservative settings
|
542
604
|
contract = factory.deploy(
|
543
605
|
consensus_max_rotations=10, # More rotations for better reliability
|
544
|
-
leader_only=False, # Full consensus for better security
|
545
606
|
wait_interval=3, # Longer wait between checks
|
546
607
|
wait_retries=30 # More retries for consensus
|
547
608
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: genlayer-test
|
3
|
-
Version: 0.
|
3
|
+
Version: 2.0.0
|
4
4
|
Summary: GenLayer Testing Suite
|
5
5
|
Author: GenLayer
|
6
6
|
License-Expression: MIT
|
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
16
16
|
Requires-Dist: pytest
|
17
17
|
Requires-Dist: setuptools>=77.0
|
18
|
-
Requires-Dist: genlayer-py==0.
|
18
|
+
Requires-Dist: genlayer-py==0.7.2
|
19
19
|
Requires-Dist: colorama>=0.4.6
|
20
20
|
Requires-Dist: pyyaml
|
21
21
|
Requires-Dist: python-dotenv
|
@@ -59,8 +59,8 @@ contract = factory.deploy(account=other_account)
|
|
59
59
|
assert contract.account == other_account
|
60
60
|
|
61
61
|
# Interact with the contract
|
62
|
-
result = contract.get_value() # Read method
|
63
|
-
tx_receipt = contract.set_value(args=["new_value"]) # Write method
|
62
|
+
result = contract.get_value().call() # Read method
|
63
|
+
tx_receipt = contract.set_value(args=["new_value"]).transact() # Write method
|
64
64
|
|
65
65
|
assert tx_execution_succeeded(tx_receipt)
|
66
66
|
```
|
@@ -114,6 +114,7 @@ networks:
|
|
114
114
|
|
115
115
|
localnet: # Local development network configuration
|
116
116
|
url: "http://127.0.0.1:4000/api"
|
117
|
+
leader_only: false # Set to true to run all contracts in leader-only mode by default
|
117
118
|
|
118
119
|
testnet_asimov: # Test network configuration
|
119
120
|
id: 4221
|
@@ -125,6 +126,7 @@ networks:
|
|
125
126
|
|
126
127
|
paths:
|
127
128
|
contracts: "contracts" # Path to your contracts directory
|
129
|
+
artifacts: "artifacts" # Path to your artifacts directory
|
128
130
|
|
129
131
|
environment: .env # Path to your environment file containing private keys and other secrets
|
130
132
|
```
|
@@ -137,12 +139,14 @@ Key configuration sections:
|
|
137
139
|
- `url`: The RPC endpoint for the network
|
138
140
|
- `id`: Chain ID
|
139
141
|
- `accounts`: List of account private keys (using environment variables)
|
142
|
+
- `leader_only`: Leader only mode
|
140
143
|
- Special case for `localnet`:
|
141
144
|
- If a network is named `localnet`, missing fields will be filled with default values
|
142
145
|
- For all other network names, `id`, `url`, and `accounts` are required fields
|
143
146
|
|
144
147
|
2. **Paths**: Define important directory paths
|
145
148
|
- `contracts`: Location of your contract files
|
149
|
+
- `artifacts`: Location of your artifacts files (analysis results will be stored here)
|
146
150
|
|
147
151
|
3. **Environment**: Path to your `.env` file containing sensitive information like private keys
|
148
152
|
|
@@ -231,6 +235,20 @@ def test_with_mocked_llm(setup_validators):
|
|
231
235
|
|
232
236
|
Note: This feature is only available when running tests on localnet.
|
233
237
|
|
238
|
+
11. Run tests with leader-only mode enabled
|
239
|
+
```bash
|
240
|
+
$ gltest --leader-only
|
241
|
+
```
|
242
|
+
The `--leader-only` flag configures all contract deployments and write operations to run only on the leader node. This is useful for:
|
243
|
+
- Faster test execution by avoiding consensus
|
244
|
+
- Testing specific leader-only scenarios
|
245
|
+
- Development and debugging purposes
|
246
|
+
- Reducing computational overhead in test environments
|
247
|
+
|
248
|
+
When this flag is enabled, all contracts deployed and all write transactions will automatically use leader-only mode, regardless of individual method parameters.
|
249
|
+
|
250
|
+
**Note:** Leader-only mode is only available for studio-based networks (localhost, 127.0.0.1, *.genlayer.com, *.genlayerlabs.com). When enabled on other networks, it will have no effect and a warning will be logged.
|
251
|
+
|
234
252
|
## 🚀 Key Features
|
235
253
|
|
236
254
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -251,8 +269,9 @@ Before diving into the examples, let's understand the basic project structure:
|
|
251
269
|
genlayer-example/
|
252
270
|
├── contracts/ # Contract definitions
|
253
271
|
│ └── storage.py # Example storage contract
|
254
|
-
|
255
|
-
|
272
|
+
├── test/ # Test files
|
273
|
+
│ └── test_contract.py # Contract test cases
|
274
|
+
└── gltest.config.yaml # Configuration file
|
256
275
|
```
|
257
276
|
|
258
277
|
### Storage Contract Example
|
@@ -311,7 +330,6 @@ def test_deployment():
|
|
311
330
|
args=["initial_value"], # Constructor arguments
|
312
331
|
account=get_default_account(), # Account to deploy from
|
313
332
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
314
|
-
leader_only=False, # Optional: whether to run only on leader
|
315
333
|
)
|
316
334
|
|
317
335
|
# Contract is now deployed and ready to use
|
@@ -320,7 +338,7 @@ def test_deployment():
|
|
320
338
|
|
321
339
|
### Read Methods
|
322
340
|
|
323
|
-
Reading from the contract
|
341
|
+
Reading from the contract requires calling `.call()` on the method:
|
324
342
|
|
325
343
|
```python
|
326
344
|
from gltest import get_contract_factory
|
@@ -332,7 +350,7 @@ def test_read_methods():
|
|
332
350
|
contract = factory.deploy()
|
333
351
|
|
334
352
|
# Call a read-only method
|
335
|
-
result = contract.
|
353
|
+
result = contract.get_storage(args=[]).call()
|
336
354
|
|
337
355
|
# Assert the result matches the initial value
|
338
356
|
assert result == "initial_value"
|
@@ -340,7 +358,7 @@ def test_read_methods():
|
|
340
358
|
|
341
359
|
### Write Methods
|
342
360
|
|
343
|
-
Writing to the contract requires transaction
|
361
|
+
Writing to the contract requires calling `.transact()` on the method. Method arguments are passed to the write method, while transaction parameters are passed to `.transact()`:
|
344
362
|
|
345
363
|
```python
|
346
364
|
from gltest import get_contract_factory
|
@@ -354,9 +372,9 @@ def test_write_methods():
|
|
354
372
|
# Call a write method with arguments
|
355
373
|
tx_receipt = contract.update_storage(
|
356
374
|
args=["new_value"], # Method arguments
|
375
|
+
).transact(
|
357
376
|
value=0, # Optional: amount of native currency to send
|
358
377
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
359
|
-
leader_only=False, # Optional: whether to run only on leader
|
360
378
|
wait_interval=1, # Optional: seconds between status checks
|
361
379
|
wait_retries=10, # Optional: max number of retries
|
362
380
|
)
|
@@ -365,7 +383,7 @@ def test_write_methods():
|
|
365
383
|
assert tx_execution_succeeded(tx_receipt)
|
366
384
|
|
367
385
|
# Verify the value was updated
|
368
|
-
assert contract.get_storage() == "new_value"
|
386
|
+
assert contract.get_storage().call() == "new_value"
|
369
387
|
```
|
370
388
|
|
371
389
|
### Assertions
|
@@ -503,6 +521,50 @@ Fixtures help maintain clean, DRY test code by:
|
|
503
521
|
- Ensuring consistent test environments
|
504
522
|
- Managing resource cleanup automatically
|
505
523
|
- Providing appropriate scoping for performance
|
524
|
+
### Statistical Analysis with `.analyze()`
|
525
|
+
|
526
|
+
The GenLayer Testing Suite provides a powerful `.analyze()` method for write operations that performs statistical analysis through multiple simulation runs. This is particularly useful for testing LLM-based contracts where outputs may vary:
|
527
|
+
|
528
|
+
```python
|
529
|
+
from gltest import get_contract_factory
|
530
|
+
|
531
|
+
def test_analyze_method():
|
532
|
+
factory = get_contract_factory("LlmContract")
|
533
|
+
contract = factory.deploy()
|
534
|
+
|
535
|
+
# Analyze a write method's behavior across multiple runs
|
536
|
+
analysis = contract.process_with_llm(args=["input_data"]).analyze(
|
537
|
+
provider="openai", # LLM provider
|
538
|
+
model="gpt-4o", # Model to use
|
539
|
+
runs=100, # Number of simulation runs (default: 100)
|
540
|
+
config=None, # Optional: provider-specific config
|
541
|
+
plugin=None, # Optional: plugin name
|
542
|
+
plugin_config=None, # Optional: plugin configuration
|
543
|
+
)
|
544
|
+
|
545
|
+
# Access analysis results
|
546
|
+
print(f"Method: {analysis.method}")
|
547
|
+
print(f"Success rate: {analysis.success_rate:.2f}%")
|
548
|
+
print(f"Reliability score: {analysis.reliability_score:.2f}%")
|
549
|
+
print(f"Unique states: {analysis.unique_states}")
|
550
|
+
print(f"Execution time: {analysis.execution_time:.1f}s")
|
551
|
+
|
552
|
+
# The analysis returns a MethodStatsSummary object with:
|
553
|
+
# - method: The contract method name
|
554
|
+
# - args: Arguments passed to the method
|
555
|
+
# - total_runs: Total number of simulation runs
|
556
|
+
# - successful_runs: Number of successful executions
|
557
|
+
# - failed_runs: Number of failed executions
|
558
|
+
# - unique_states: Number of unique contract states observed
|
559
|
+
# - reliability_score: Percentage of runs with the most common state
|
560
|
+
# - execution_time: Total time for all simulations
|
561
|
+
```
|
562
|
+
|
563
|
+
The `.analyze()` method helps you:
|
564
|
+
- Test non-deterministic contract methods
|
565
|
+
- Measure consistency of LLM-based operations
|
566
|
+
- Identify edge cases and failure patterns
|
567
|
+
- Benchmark performance across multiple runs
|
506
568
|
|
507
569
|
## 📝 Best Practices
|
508
570
|
|
@@ -546,6 +608,7 @@ Fixtures help maintain clean, DRY test code by:
|
|
546
608
|
```python
|
547
609
|
tx_receipt = contract.set_value(
|
548
610
|
args=["new_value"],
|
611
|
+
).transact(
|
549
612
|
wait_interval=2, # Increase wait interval between status checks
|
550
613
|
wait_retries=20, # Increase number of retry attempts
|
551
614
|
)
|
@@ -558,13 +621,11 @@ Fixtures help maintain clean, DRY test code by:
|
|
558
621
|
# Try with increased consensus parameters
|
559
622
|
contract = factory.deploy(
|
560
623
|
consensus_max_rotations=5, # Increase number of consensus rotations
|
561
|
-
leader_only=True, # Try leader-only mode for faster execution
|
562
624
|
)
|
563
625
|
|
564
626
|
# For critical operations, use more conservative settings
|
565
627
|
contract = factory.deploy(
|
566
628
|
consensus_max_rotations=10, # More rotations for better reliability
|
567
|
-
leader_only=False, # Full consensus for better security
|
568
629
|
wait_interval=3, # Longer wait between checks
|
569
630
|
wait_retries=30 # More retries for consensus
|
570
631
|
)
|
@@ -8,17 +8,22 @@ genlayer_test.egg-info/entry_points.txt
|
|
8
8
|
genlayer_test.egg-info/requires.txt
|
9
9
|
genlayer_test.egg-info/top_level.txt
|
10
10
|
gltest/__init__.py
|
11
|
+
gltest/accounts.py
|
11
12
|
gltest/assertions.py
|
13
|
+
gltest/clients.py
|
12
14
|
gltest/exceptions.py
|
13
15
|
gltest/fixtures.py
|
14
16
|
gltest/logging.py
|
15
17
|
gltest/types.py
|
16
18
|
gltest/artifacts/__init__.py
|
17
19
|
gltest/artifacts/contract.py
|
18
|
-
gltest/
|
19
|
-
gltest/
|
20
|
-
gltest/
|
21
|
-
gltest/
|
20
|
+
gltest/contracts/__init__.py
|
21
|
+
gltest/contracts/contract.py
|
22
|
+
gltest/contracts/contract_factory.py
|
23
|
+
gltest/contracts/contract_functions.py
|
24
|
+
gltest/contracts/method_stats.py
|
25
|
+
gltest/contracts/stats_collector.py
|
26
|
+
gltest/contracts/utils.py
|
22
27
|
gltest/helpers/__init__.py
|
23
28
|
gltest/helpers/fixture_snapshot.py
|
24
29
|
gltest/helpers/take_snapshot.py
|
@@ -28,6 +33,7 @@ gltest_cli/config/__init__.py
|
|
28
33
|
gltest_cli/config/constants.py
|
29
34
|
gltest_cli/config/general.py
|
30
35
|
gltest_cli/config/plugin.py
|
36
|
+
gltest_cli/config/pytest_context.py
|
31
37
|
gltest_cli/config/types.py
|
32
38
|
gltest_cli/config/user.py
|
33
39
|
tests/__init__.py
|
@@ -48,6 +54,7 @@ tests/examples/contracts/multi_file_contract/other.py
|
|
48
54
|
tests/examples/tests/test_football_prediction_market.py
|
49
55
|
tests/examples/tests/test_intelligent_oracle_factory.py
|
50
56
|
tests/examples/tests/test_llm_erc20.py
|
57
|
+
tests/examples/tests/test_llm_erc20_analyze.py
|
51
58
|
tests/examples/tests/test_log_indexer.py
|
52
59
|
tests/examples/tests/test_multi_file_contract.py
|
53
60
|
tests/examples/tests/test_multi_file_contract_legacy.py
|
@@ -66,5 +73,7 @@ tests/gltest/artifact/contracts/duplicate_ic_contract_2.py
|
|
66
73
|
tests/gltest/artifact/contracts/not_ic_contract.py
|
67
74
|
tests/gltest/assertions/test_assertions.py
|
68
75
|
tests/gltest_cli/__init__.py
|
76
|
+
tests/gltest_cli/config/test_config_integration.py
|
77
|
+
tests/gltest_cli/config/test_general_config.py
|
69
78
|
tests/gltest_cli/config/test_plugin.py
|
70
79
|
tests/gltest_cli/config/test_user.py
|