genlayer-test 0.3.1__tar.gz → 0.4.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.3.1 → genlayer_test-0.4.0}/PKG-INFO +124 -4
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/README.md +123 -3
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/PKG-INFO +124 -4
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/SOURCES.txt +1 -0
- genlayer_test-0.4.0/gltest/fixtures.py +91 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/helpers/fixture_snapshot.py +1 -6
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/plugin.py +13 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/types.py +11 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/pyproject.toml +1 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/football_prediction_market.py +11 -8
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/intelligent_oracle.py +7 -6
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/intelligent_oracle_factory.py +1 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/llm_erc20.py +3 -2
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/log_indexer.py +7 -5
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/multi_file_contract/__init__.py +5 -2
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/multi_read_erc20.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/multi_tenant_storage.py +3 -2
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/read_erc20.py +6 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/storage.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/user_storage.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/wizard_of_coin.py +4 -3
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_football_prediction_market.py +20 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_intelligent_oracle_factory.py +62 -9
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_llm_erc20.py +20 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_log_indexer.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_multi_file_contract.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_multi_file_contract_legacy.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_multi_read_erc20.py +9 -3
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_multi_tenant_storage.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_read_erc20.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_storage.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_storage_legacy.py +2 -1
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/tests/test_user_storage.py +9 -3
- genlayer_test-0.4.0/tests/examples/tests/test_wizard_of_coin.py +27 -0
- genlayer_test-0.3.1/tests/examples/tests/test_wizard_of_coin.py +0 -12
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/LICENSE +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/dependency_links.txt +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/entry_points.txt +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/requires.txt +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/genlayer_test.egg-info/top_level.txt +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/artifacts/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/artifacts/contract.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/assertions.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/exceptions.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/glchain/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/glchain/account.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/glchain/client.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/glchain/contract.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/helpers/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/helpers/take_snapshot.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest/types.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/constants.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/general.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/config/user.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/logging.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/gltest_cli/main.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/setup.cfg +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/conftest.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/examples/contracts/multi_file_contract/other.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/artifact/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_1.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_2.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/artifact/contracts/not_ic_contract.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/artifact/test_contract_definition.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest/assertions/test_assertions.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest_cli/__init__.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest_cli/config/test_plugin.py +0 -0
- {genlayer_test-0.3.1 → genlayer_test-0.4.0}/tests/gltest_cli/config/test_user.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: genlayer-test
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: GenLayer Testing Suite
|
5
5
|
Author: GenLayer
|
6
6
|
License-Expression: MIT
|
@@ -50,7 +50,7 @@ from gltest.assertions import tx_execution_succeeded
|
|
50
50
|
|
51
51
|
factory = get_contract_factory("MyContract")
|
52
52
|
# Deploy a contract with default account
|
53
|
-
contract = factory.deploy() # This will be deployed with
|
53
|
+
contract = factory.deploy() # This will be deployed with the default account
|
54
54
|
assert contract.account == get_default_account()
|
55
55
|
|
56
56
|
# Deploy a contract with other account
|
@@ -207,6 +207,30 @@ $ gltest --default-wait-interval <default_wait_interval>
|
|
207
207
|
$ gltest --default-wait-retries <default_wait_retries>
|
208
208
|
```
|
209
209
|
|
210
|
+
10. Run tests with mocked LLM responses (localnet only)
|
211
|
+
```bash
|
212
|
+
$ gltest --test-with-mocks
|
213
|
+
```
|
214
|
+
The `--test-with-mocks` flag enables mocking of LLM responses when creating validators. This is particularly useful for:
|
215
|
+
- Testing without actual LLM API calls
|
216
|
+
- Ensuring deterministic test results
|
217
|
+
- Faster test execution
|
218
|
+
- Testing specific edge cases with controlled responses
|
219
|
+
|
220
|
+
When using this flag with the `setup_validators` fixture, you can provide custom mock responses:
|
221
|
+
```python
|
222
|
+
def test_with_mocked_llm(setup_validators):
|
223
|
+
# Setup validators with a specific mock response
|
224
|
+
mock_response = {"result": "This is a mocked LLM response"}
|
225
|
+
setup_validators(mock_response=mock_response)
|
226
|
+
|
227
|
+
# Your LLM-based contract will receive the mocked response
|
228
|
+
contract = factory.deploy()
|
229
|
+
result = contract.llm_method() # Will use the mocked response
|
230
|
+
```
|
231
|
+
|
232
|
+
Note: This feature is only available when running tests on localnet.
|
233
|
+
|
210
234
|
## 🚀 Key Features
|
211
235
|
|
212
236
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -275,7 +299,7 @@ Key features demonstrated in this contract:
|
|
275
299
|
Here's how to deploy the Storage contract:
|
276
300
|
|
277
301
|
```python
|
278
|
-
from gltest import get_contract_factory,
|
302
|
+
from gltest import get_contract_factory, get_default_account
|
279
303
|
|
280
304
|
def test_deployment():
|
281
305
|
# Get the contract factory for your contract
|
@@ -285,7 +309,7 @@ def test_deployment():
|
|
285
309
|
# Deploy the contract with constructor arguments
|
286
310
|
contract = factory.deploy(
|
287
311
|
args=["initial_value"], # Constructor arguments
|
288
|
-
account=
|
312
|
+
account=get_default_account(), # Account to deploy from
|
289
313
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
290
314
|
leader_only=False, # Optional: whether to run only on leader
|
291
315
|
)
|
@@ -384,6 +408,102 @@ Both `tx_execution_succeeded` and `tx_execution_failed` accept the following par
|
|
384
408
|
|
385
409
|
For more example contracts, check out the [contracts directory](tests/examples/contracts) which contains various sample contracts demonstrating different features and use cases.
|
386
410
|
|
411
|
+
### Test Fixtures
|
412
|
+
|
413
|
+
The GenLayer Testing Suite provides reusable pytest fixtures in `gltest.fixtures` to simplify common testing operations. These fixtures can be imported and used in your test files to avoid repetitive setup code.
|
414
|
+
|
415
|
+
#### Available Fixtures
|
416
|
+
|
417
|
+
The following fixtures are available in `gltest.fixtures`:
|
418
|
+
|
419
|
+
- **`gl_client`** (session scope) - GenLayer client instance for network operations
|
420
|
+
- **`default_account`** (session scope) - Default account for testing and deployments
|
421
|
+
- **`accounts`** (session scope) - List of test accounts for multi-account scenarios
|
422
|
+
- **`setup_validators`** (function scope) - Function to create test validators for LLM operations
|
423
|
+
|
424
|
+
##### 1. `gl_client` (session scope)
|
425
|
+
Provides a GenLayer PY client instance that's created once per test session. This is useful for operations that interact directly with the GenLayer network.
|
426
|
+
|
427
|
+
```python
|
428
|
+
def test_client_operations(gl_client):
|
429
|
+
# Use the client for network operations
|
430
|
+
tx_hash = "0x1234..."
|
431
|
+
transaction = gl_client.get_transaction(tx_hash)
|
432
|
+
```
|
433
|
+
|
434
|
+
##### 2. `default_account` (session scope)
|
435
|
+
Provides the default account used to execute transactions when no account is specified.
|
436
|
+
|
437
|
+
```python
|
438
|
+
def test_with_default_account(default_account):
|
439
|
+
# Use the default account for deployments
|
440
|
+
factory = get_contract_factory("MyContract")
|
441
|
+
contract = factory.deploy(account=default_account)
|
442
|
+
```
|
443
|
+
|
444
|
+
##### 3. `accounts` (session scope)
|
445
|
+
Provides a list of account objects loaded from the private keys defined in `gltest.config.yaml` for the current network, or pre-created test accounts if no config is present
|
446
|
+
|
447
|
+
```python
|
448
|
+
def test_multiple_accounts(accounts):
|
449
|
+
# Get multiple accounts for testing
|
450
|
+
sender = accounts[0]
|
451
|
+
receiver = accounts[1]
|
452
|
+
|
453
|
+
# Test transfers or multi-party interactions
|
454
|
+
contract.transfer(args=[receiver.address, 100], account=sender)
|
455
|
+
```
|
456
|
+
|
457
|
+
##### 4. `setup_validators` (function scope)
|
458
|
+
Creates test validators for localnet environment. This fixture is particularly useful for testing LLM-based contract methods and consensus behavior. It yields a function that allows you to configure validators with custom settings.
|
459
|
+
|
460
|
+
```python
|
461
|
+
def test_with_validators(setup_validators):
|
462
|
+
# Setup validators with default configuration
|
463
|
+
setup_validators()
|
464
|
+
|
465
|
+
# Or setup with custom mock responses for testing
|
466
|
+
mock_response = {"result": "mocked LLM response"}
|
467
|
+
setup_validators(mock_response=mock_response, n_validators=3)
|
468
|
+
|
469
|
+
# Now test your LLM-based contract methods
|
470
|
+
contract = factory.deploy()
|
471
|
+
result = contract.llm_based_method()
|
472
|
+
```
|
473
|
+
|
474
|
+
Parameters for `setup_validators`:
|
475
|
+
- `mock_response` (dict, optional): Mock validator response when using `--test-with-mocks` flag
|
476
|
+
- `n_validators` (int, optional): Number of validators to create (default: 5)
|
477
|
+
|
478
|
+
#### Using Fixtures in Your Tests
|
479
|
+
|
480
|
+
To use these fixtures, simply import them and include them as parameters in your test functions:
|
481
|
+
|
482
|
+
```python
|
483
|
+
from gltest import get_contract_factory
|
484
|
+
from gltest.assertions import tx_execution_succeeded
|
485
|
+
|
486
|
+
def test_complete_workflow(gl_client, default_account, accounts, setup_validators):
|
487
|
+
# Setup validators for LLM operations
|
488
|
+
setup_validators()
|
489
|
+
|
490
|
+
# Deploy contract with default account
|
491
|
+
factory = get_contract_factory("MyContract")
|
492
|
+
contract = factory.deploy(account=default_account)
|
493
|
+
|
494
|
+
# Interact using other accounts
|
495
|
+
other_account = accounts[1]
|
496
|
+
tx_receipt = contract.some_method(args=["value"], account=other_account)
|
497
|
+
|
498
|
+
assert tx_execution_succeeded(tx_receipt)
|
499
|
+
```
|
500
|
+
|
501
|
+
Fixtures help maintain clean, DRY test code by:
|
502
|
+
- Eliminating repetitive setup code
|
503
|
+
- Ensuring consistent test environments
|
504
|
+
- Managing resource cleanup automatically
|
505
|
+
- Providing appropriate scoping for performance
|
506
|
+
|
387
507
|
## 📝 Best Practices
|
388
508
|
|
389
509
|
1. **Test Organization**
|
@@ -27,7 +27,7 @@ from gltest.assertions import tx_execution_succeeded
|
|
27
27
|
|
28
28
|
factory = get_contract_factory("MyContract")
|
29
29
|
# Deploy a contract with default account
|
30
|
-
contract = factory.deploy() # This will be deployed with
|
30
|
+
contract = factory.deploy() # This will be deployed with the default account
|
31
31
|
assert contract.account == get_default_account()
|
32
32
|
|
33
33
|
# Deploy a contract with other account
|
@@ -184,6 +184,30 @@ $ gltest --default-wait-interval <default_wait_interval>
|
|
184
184
|
$ gltest --default-wait-retries <default_wait_retries>
|
185
185
|
```
|
186
186
|
|
187
|
+
10. Run tests with mocked LLM responses (localnet only)
|
188
|
+
```bash
|
189
|
+
$ gltest --test-with-mocks
|
190
|
+
```
|
191
|
+
The `--test-with-mocks` flag enables mocking of LLM responses when creating validators. This is particularly useful for:
|
192
|
+
- Testing without actual LLM API calls
|
193
|
+
- Ensuring deterministic test results
|
194
|
+
- Faster test execution
|
195
|
+
- Testing specific edge cases with controlled responses
|
196
|
+
|
197
|
+
When using this flag with the `setup_validators` fixture, you can provide custom mock responses:
|
198
|
+
```python
|
199
|
+
def test_with_mocked_llm(setup_validators):
|
200
|
+
# Setup validators with a specific mock response
|
201
|
+
mock_response = {"result": "This is a mocked LLM response"}
|
202
|
+
setup_validators(mock_response=mock_response)
|
203
|
+
|
204
|
+
# Your LLM-based contract will receive the mocked response
|
205
|
+
contract = factory.deploy()
|
206
|
+
result = contract.llm_method() # Will use the mocked response
|
207
|
+
```
|
208
|
+
|
209
|
+
Note: This feature is only available when running tests on localnet.
|
210
|
+
|
187
211
|
## 🚀 Key Features
|
188
212
|
|
189
213
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -252,7 +276,7 @@ Key features demonstrated in this contract:
|
|
252
276
|
Here's how to deploy the Storage contract:
|
253
277
|
|
254
278
|
```python
|
255
|
-
from gltest import get_contract_factory,
|
279
|
+
from gltest import get_contract_factory, get_default_account
|
256
280
|
|
257
281
|
def test_deployment():
|
258
282
|
# Get the contract factory for your contract
|
@@ -262,7 +286,7 @@ def test_deployment():
|
|
262
286
|
# Deploy the contract with constructor arguments
|
263
287
|
contract = factory.deploy(
|
264
288
|
args=["initial_value"], # Constructor arguments
|
265
|
-
account=
|
289
|
+
account=get_default_account(), # Account to deploy from
|
266
290
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
267
291
|
leader_only=False, # Optional: whether to run only on leader
|
268
292
|
)
|
@@ -361,6 +385,102 @@ Both `tx_execution_succeeded` and `tx_execution_failed` accept the following par
|
|
361
385
|
|
362
386
|
For more example contracts, check out the [contracts directory](tests/examples/contracts) which contains various sample contracts demonstrating different features and use cases.
|
363
387
|
|
388
|
+
### Test Fixtures
|
389
|
+
|
390
|
+
The GenLayer Testing Suite provides reusable pytest fixtures in `gltest.fixtures` to simplify common testing operations. These fixtures can be imported and used in your test files to avoid repetitive setup code.
|
391
|
+
|
392
|
+
#### Available Fixtures
|
393
|
+
|
394
|
+
The following fixtures are available in `gltest.fixtures`:
|
395
|
+
|
396
|
+
- **`gl_client`** (session scope) - GenLayer client instance for network operations
|
397
|
+
- **`default_account`** (session scope) - Default account for testing and deployments
|
398
|
+
- **`accounts`** (session scope) - List of test accounts for multi-account scenarios
|
399
|
+
- **`setup_validators`** (function scope) - Function to create test validators for LLM operations
|
400
|
+
|
401
|
+
##### 1. `gl_client` (session scope)
|
402
|
+
Provides a GenLayer PY client instance that's created once per test session. This is useful for operations that interact directly with the GenLayer network.
|
403
|
+
|
404
|
+
```python
|
405
|
+
def test_client_operations(gl_client):
|
406
|
+
# Use the client for network operations
|
407
|
+
tx_hash = "0x1234..."
|
408
|
+
transaction = gl_client.get_transaction(tx_hash)
|
409
|
+
```
|
410
|
+
|
411
|
+
##### 2. `default_account` (session scope)
|
412
|
+
Provides the default account used to execute transactions when no account is specified.
|
413
|
+
|
414
|
+
```python
|
415
|
+
def test_with_default_account(default_account):
|
416
|
+
# Use the default account for deployments
|
417
|
+
factory = get_contract_factory("MyContract")
|
418
|
+
contract = factory.deploy(account=default_account)
|
419
|
+
```
|
420
|
+
|
421
|
+
##### 3. `accounts` (session scope)
|
422
|
+
Provides a list of account objects loaded from the private keys defined in `gltest.config.yaml` for the current network, or pre-created test accounts if no config is present
|
423
|
+
|
424
|
+
```python
|
425
|
+
def test_multiple_accounts(accounts):
|
426
|
+
# Get multiple accounts for testing
|
427
|
+
sender = accounts[0]
|
428
|
+
receiver = accounts[1]
|
429
|
+
|
430
|
+
# Test transfers or multi-party interactions
|
431
|
+
contract.transfer(args=[receiver.address, 100], account=sender)
|
432
|
+
```
|
433
|
+
|
434
|
+
##### 4. `setup_validators` (function scope)
|
435
|
+
Creates test validators for localnet environment. This fixture is particularly useful for testing LLM-based contract methods and consensus behavior. It yields a function that allows you to configure validators with custom settings.
|
436
|
+
|
437
|
+
```python
|
438
|
+
def test_with_validators(setup_validators):
|
439
|
+
# Setup validators with default configuration
|
440
|
+
setup_validators()
|
441
|
+
|
442
|
+
# Or setup with custom mock responses for testing
|
443
|
+
mock_response = {"result": "mocked LLM response"}
|
444
|
+
setup_validators(mock_response=mock_response, n_validators=3)
|
445
|
+
|
446
|
+
# Now test your LLM-based contract methods
|
447
|
+
contract = factory.deploy()
|
448
|
+
result = contract.llm_based_method()
|
449
|
+
```
|
450
|
+
|
451
|
+
Parameters for `setup_validators`:
|
452
|
+
- `mock_response` (dict, optional): Mock validator response when using `--test-with-mocks` flag
|
453
|
+
- `n_validators` (int, optional): Number of validators to create (default: 5)
|
454
|
+
|
455
|
+
#### Using Fixtures in Your Tests
|
456
|
+
|
457
|
+
To use these fixtures, simply import them and include them as parameters in your test functions:
|
458
|
+
|
459
|
+
```python
|
460
|
+
from gltest import get_contract_factory
|
461
|
+
from gltest.assertions import tx_execution_succeeded
|
462
|
+
|
463
|
+
def test_complete_workflow(gl_client, default_account, accounts, setup_validators):
|
464
|
+
# Setup validators for LLM operations
|
465
|
+
setup_validators()
|
466
|
+
|
467
|
+
# Deploy contract with default account
|
468
|
+
factory = get_contract_factory("MyContract")
|
469
|
+
contract = factory.deploy(account=default_account)
|
470
|
+
|
471
|
+
# Interact using other accounts
|
472
|
+
other_account = accounts[1]
|
473
|
+
tx_receipt = contract.some_method(args=["value"], account=other_account)
|
474
|
+
|
475
|
+
assert tx_execution_succeeded(tx_receipt)
|
476
|
+
```
|
477
|
+
|
478
|
+
Fixtures help maintain clean, DRY test code by:
|
479
|
+
- Eliminating repetitive setup code
|
480
|
+
- Ensuring consistent test environments
|
481
|
+
- Managing resource cleanup automatically
|
482
|
+
- Providing appropriate scoping for performance
|
483
|
+
|
364
484
|
## 📝 Best Practices
|
365
485
|
|
366
486
|
1. **Test Organization**
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: genlayer-test
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: GenLayer Testing Suite
|
5
5
|
Author: GenLayer
|
6
6
|
License-Expression: MIT
|
@@ -50,7 +50,7 @@ from gltest.assertions import tx_execution_succeeded
|
|
50
50
|
|
51
51
|
factory = get_contract_factory("MyContract")
|
52
52
|
# Deploy a contract with default account
|
53
|
-
contract = factory.deploy() # This will be deployed with
|
53
|
+
contract = factory.deploy() # This will be deployed with the default account
|
54
54
|
assert contract.account == get_default_account()
|
55
55
|
|
56
56
|
# Deploy a contract with other account
|
@@ -207,6 +207,30 @@ $ gltest --default-wait-interval <default_wait_interval>
|
|
207
207
|
$ gltest --default-wait-retries <default_wait_retries>
|
208
208
|
```
|
209
209
|
|
210
|
+
10. Run tests with mocked LLM responses (localnet only)
|
211
|
+
```bash
|
212
|
+
$ gltest --test-with-mocks
|
213
|
+
```
|
214
|
+
The `--test-with-mocks` flag enables mocking of LLM responses when creating validators. This is particularly useful for:
|
215
|
+
- Testing without actual LLM API calls
|
216
|
+
- Ensuring deterministic test results
|
217
|
+
- Faster test execution
|
218
|
+
- Testing specific edge cases with controlled responses
|
219
|
+
|
220
|
+
When using this flag with the `setup_validators` fixture, you can provide custom mock responses:
|
221
|
+
```python
|
222
|
+
def test_with_mocked_llm(setup_validators):
|
223
|
+
# Setup validators with a specific mock response
|
224
|
+
mock_response = {"result": "This is a mocked LLM response"}
|
225
|
+
setup_validators(mock_response=mock_response)
|
226
|
+
|
227
|
+
# Your LLM-based contract will receive the mocked response
|
228
|
+
contract = factory.deploy()
|
229
|
+
result = contract.llm_method() # Will use the mocked response
|
230
|
+
```
|
231
|
+
|
232
|
+
Note: This feature is only available when running tests on localnet.
|
233
|
+
|
210
234
|
## 🚀 Key Features
|
211
235
|
|
212
236
|
- **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
|
@@ -275,7 +299,7 @@ Key features demonstrated in this contract:
|
|
275
299
|
Here's how to deploy the Storage contract:
|
276
300
|
|
277
301
|
```python
|
278
|
-
from gltest import get_contract_factory,
|
302
|
+
from gltest import get_contract_factory, get_default_account
|
279
303
|
|
280
304
|
def test_deployment():
|
281
305
|
# Get the contract factory for your contract
|
@@ -285,7 +309,7 @@ def test_deployment():
|
|
285
309
|
# Deploy the contract with constructor arguments
|
286
310
|
contract = factory.deploy(
|
287
311
|
args=["initial_value"], # Constructor arguments
|
288
|
-
account=
|
312
|
+
account=get_default_account(), # Account to deploy from
|
289
313
|
consensus_max_rotations=3, # Optional: max consensus rotations
|
290
314
|
leader_only=False, # Optional: whether to run only on leader
|
291
315
|
)
|
@@ -384,6 +408,102 @@ Both `tx_execution_succeeded` and `tx_execution_failed` accept the following par
|
|
384
408
|
|
385
409
|
For more example contracts, check out the [contracts directory](tests/examples/contracts) which contains various sample contracts demonstrating different features and use cases.
|
386
410
|
|
411
|
+
### Test Fixtures
|
412
|
+
|
413
|
+
The GenLayer Testing Suite provides reusable pytest fixtures in `gltest.fixtures` to simplify common testing operations. These fixtures can be imported and used in your test files to avoid repetitive setup code.
|
414
|
+
|
415
|
+
#### Available Fixtures
|
416
|
+
|
417
|
+
The following fixtures are available in `gltest.fixtures`:
|
418
|
+
|
419
|
+
- **`gl_client`** (session scope) - GenLayer client instance for network operations
|
420
|
+
- **`default_account`** (session scope) - Default account for testing and deployments
|
421
|
+
- **`accounts`** (session scope) - List of test accounts for multi-account scenarios
|
422
|
+
- **`setup_validators`** (function scope) - Function to create test validators for LLM operations
|
423
|
+
|
424
|
+
##### 1. `gl_client` (session scope)
|
425
|
+
Provides a GenLayer PY client instance that's created once per test session. This is useful for operations that interact directly with the GenLayer network.
|
426
|
+
|
427
|
+
```python
|
428
|
+
def test_client_operations(gl_client):
|
429
|
+
# Use the client for network operations
|
430
|
+
tx_hash = "0x1234..."
|
431
|
+
transaction = gl_client.get_transaction(tx_hash)
|
432
|
+
```
|
433
|
+
|
434
|
+
##### 2. `default_account` (session scope)
|
435
|
+
Provides the default account used to execute transactions when no account is specified.
|
436
|
+
|
437
|
+
```python
|
438
|
+
def test_with_default_account(default_account):
|
439
|
+
# Use the default account for deployments
|
440
|
+
factory = get_contract_factory("MyContract")
|
441
|
+
contract = factory.deploy(account=default_account)
|
442
|
+
```
|
443
|
+
|
444
|
+
##### 3. `accounts` (session scope)
|
445
|
+
Provides a list of account objects loaded from the private keys defined in `gltest.config.yaml` for the current network, or pre-created test accounts if no config is present
|
446
|
+
|
447
|
+
```python
|
448
|
+
def test_multiple_accounts(accounts):
|
449
|
+
# Get multiple accounts for testing
|
450
|
+
sender = accounts[0]
|
451
|
+
receiver = accounts[1]
|
452
|
+
|
453
|
+
# Test transfers or multi-party interactions
|
454
|
+
contract.transfer(args=[receiver.address, 100], account=sender)
|
455
|
+
```
|
456
|
+
|
457
|
+
##### 4. `setup_validators` (function scope)
|
458
|
+
Creates test validators for localnet environment. This fixture is particularly useful for testing LLM-based contract methods and consensus behavior. It yields a function that allows you to configure validators with custom settings.
|
459
|
+
|
460
|
+
```python
|
461
|
+
def test_with_validators(setup_validators):
|
462
|
+
# Setup validators with default configuration
|
463
|
+
setup_validators()
|
464
|
+
|
465
|
+
# Or setup with custom mock responses for testing
|
466
|
+
mock_response = {"result": "mocked LLM response"}
|
467
|
+
setup_validators(mock_response=mock_response, n_validators=3)
|
468
|
+
|
469
|
+
# Now test your LLM-based contract methods
|
470
|
+
contract = factory.deploy()
|
471
|
+
result = contract.llm_based_method()
|
472
|
+
```
|
473
|
+
|
474
|
+
Parameters for `setup_validators`:
|
475
|
+
- `mock_response` (dict, optional): Mock validator response when using `--test-with-mocks` flag
|
476
|
+
- `n_validators` (int, optional): Number of validators to create (default: 5)
|
477
|
+
|
478
|
+
#### Using Fixtures in Your Tests
|
479
|
+
|
480
|
+
To use these fixtures, simply import them and include them as parameters in your test functions:
|
481
|
+
|
482
|
+
```python
|
483
|
+
from gltest import get_contract_factory
|
484
|
+
from gltest.assertions import tx_execution_succeeded
|
485
|
+
|
486
|
+
def test_complete_workflow(gl_client, default_account, accounts, setup_validators):
|
487
|
+
# Setup validators for LLM operations
|
488
|
+
setup_validators()
|
489
|
+
|
490
|
+
# Deploy contract with default account
|
491
|
+
factory = get_contract_factory("MyContract")
|
492
|
+
contract = factory.deploy(account=default_account)
|
493
|
+
|
494
|
+
# Interact using other accounts
|
495
|
+
other_account = accounts[1]
|
496
|
+
tx_receipt = contract.some_method(args=["value"], account=other_account)
|
497
|
+
|
498
|
+
assert tx_execution_succeeded(tx_receipt)
|
499
|
+
```
|
500
|
+
|
501
|
+
Fixtures help maintain clean, DRY test code by:
|
502
|
+
- Eliminating repetitive setup code
|
503
|
+
- Ensuring consistent test environments
|
504
|
+
- Managing resource cleanup automatically
|
505
|
+
- Providing appropriate scoping for performance
|
506
|
+
|
387
507
|
## 📝 Best Practices
|
388
508
|
|
389
509
|
1. **Test Organization**
|
@@ -0,0 +1,91 @@
|
|
1
|
+
"""
|
2
|
+
This module provides reusable pytest fixtures for common gltest operations.
|
3
|
+
These fixtures can be imported and used in test files.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import pytest
|
7
|
+
from gltest.glchain import (
|
8
|
+
get_gl_client,
|
9
|
+
get_accounts,
|
10
|
+
get_default_account,
|
11
|
+
get_gl_provider,
|
12
|
+
)
|
13
|
+
from gltest_cli.config.general import get_general_config
|
14
|
+
|
15
|
+
|
16
|
+
@pytest.fixture(scope="session")
|
17
|
+
def gl_client():
|
18
|
+
"""
|
19
|
+
Provides a GenLayer client instance.
|
20
|
+
|
21
|
+
Scope: session - created once per test session
|
22
|
+
"""
|
23
|
+
return get_gl_client()
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture(scope="session")
|
27
|
+
def default_account():
|
28
|
+
"""
|
29
|
+
Provides the default account for testing.
|
30
|
+
|
31
|
+
Scope: session - created once per test session
|
32
|
+
"""
|
33
|
+
return get_default_account()
|
34
|
+
|
35
|
+
|
36
|
+
@pytest.fixture(scope="session")
|
37
|
+
def accounts():
|
38
|
+
"""
|
39
|
+
Provides a list of test accounts.
|
40
|
+
|
41
|
+
Scope: session - created once per test session
|
42
|
+
"""
|
43
|
+
return get_accounts()
|
44
|
+
|
45
|
+
|
46
|
+
@pytest.fixture(scope="function")
|
47
|
+
def setup_validators():
|
48
|
+
"""
|
49
|
+
Creates test validators for localnet environment.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
mock_response (dict, optional): Mock validator response when using --test-with-mocks flag
|
53
|
+
n_validators (int, optional): Number of validators to create (default: 5)
|
54
|
+
|
55
|
+
Scope: function - created fresh for each test
|
56
|
+
"""
|
57
|
+
general_config = get_general_config()
|
58
|
+
provider = get_gl_provider()
|
59
|
+
|
60
|
+
def _setup(mock_response=None, n_validators=5):
|
61
|
+
if not general_config.check_local_rpc():
|
62
|
+
return
|
63
|
+
if general_config.get_test_with_mocks():
|
64
|
+
for _ in range(n_validators):
|
65
|
+
provider.make_request(
|
66
|
+
method="sim_createValidator",
|
67
|
+
params=[
|
68
|
+
8,
|
69
|
+
"openai",
|
70
|
+
"gpt-4o",
|
71
|
+
{"temperature": 0.75, "max_tokens": 500},
|
72
|
+
"openai-compatible",
|
73
|
+
{
|
74
|
+
"api_key_env_var": "OPENAIKEY",
|
75
|
+
"api_url": "https://api.openai.com",
|
76
|
+
"mock_response": mock_response if mock_response else {},
|
77
|
+
},
|
78
|
+
],
|
79
|
+
)
|
80
|
+
else:
|
81
|
+
provider.make_request(
|
82
|
+
method="sim_createRandomValidators",
|
83
|
+
params=[n_validators, 8, 12],
|
84
|
+
)
|
85
|
+
|
86
|
+
yield _setup
|
87
|
+
|
88
|
+
if not general_config.check_local_rpc():
|
89
|
+
return
|
90
|
+
|
91
|
+
provider.make_request(method="sim_deleteAllValidators", params=[])
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from typing import TypeVar, Callable, List, Any
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from urllib.parse import urlparse
|
4
3
|
from .take_snapshot import SnapshotRestorer, take_snapshot
|
5
4
|
from gltest.exceptions import (
|
6
5
|
FixtureSnapshotError,
|
@@ -9,8 +8,6 @@ from gltest.exceptions import (
|
|
9
8
|
)
|
10
9
|
from gltest_cli.config.general import get_general_config
|
11
10
|
|
12
|
-
SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
|
13
|
-
|
14
11
|
T = TypeVar("T")
|
15
12
|
|
16
13
|
|
@@ -35,9 +32,7 @@ def load_fixture(fixture: Callable[[], T]) -> T:
|
|
35
32
|
raise FixtureAnonymousFunctionError("Fixtures must be named functions")
|
36
33
|
|
37
34
|
general_config = get_general_config()
|
38
|
-
|
39
|
-
domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
|
40
|
-
if domain not in SUPPORTED_RPC_DOMAINS:
|
35
|
+
if not general_config.check_local_rpc():
|
41
36
|
return fixture()
|
42
37
|
|
43
38
|
# Find existing snapshot for this fixture
|
@@ -48,6 +48,13 @@ def pytest_addoption(parser):
|
|
48
48
|
help="Target network (defaults to 'localnet' if no config file)",
|
49
49
|
)
|
50
50
|
|
51
|
+
group.addoption(
|
52
|
+
"--test-with-mocks",
|
53
|
+
action="store_true",
|
54
|
+
default=False,
|
55
|
+
help="Test with mocks",
|
56
|
+
)
|
57
|
+
|
51
58
|
|
52
59
|
def pytest_configure(config):
|
53
60
|
general_config = get_general_config()
|
@@ -73,6 +80,7 @@ def pytest_configure(config):
|
|
73
80
|
default_wait_retries = config.getoption("--default-wait-retries")
|
74
81
|
rpc_url = config.getoption("--rpc-url")
|
75
82
|
network = config.getoption("--network")
|
83
|
+
test_with_mocks = config.getoption("--test-with-mocks")
|
76
84
|
|
77
85
|
plugin_config = PluginConfig()
|
78
86
|
plugin_config.contracts_dir = (
|
@@ -82,6 +90,7 @@ def pytest_configure(config):
|
|
82
90
|
plugin_config.default_wait_retries = int(default_wait_retries)
|
83
91
|
plugin_config.rpc_url = rpc_url
|
84
92
|
plugin_config.network_name = network
|
93
|
+
plugin_config.test_with_mocks = test_with_mocks
|
85
94
|
|
86
95
|
general_config.plugin_config = plugin_config
|
87
96
|
|
@@ -100,3 +109,7 @@ def pytest_sessionstart(session):
|
|
100
109
|
f" Default wait interval: {general_config.get_default_wait_interval()} ms"
|
101
110
|
)
|
102
111
|
logger.info(f" Default wait retries: {general_config.get_default_wait_retries()}")
|
112
|
+
logger.info(f" Test with mocks: {general_config.get_test_with_mocks()}")
|
113
|
+
|
114
|
+
|
115
|
+
pytest_plugins = ["gltest.fixtures"]
|
@@ -4,6 +4,7 @@ from pathlib import Path
|
|
4
4
|
from typing import Dict, List, Optional
|
5
5
|
from genlayer_py.chains import localnet, testnet_asimov
|
6
6
|
from genlayer_py.types import GenLayerChain
|
7
|
+
from urllib.parse import urlparse
|
7
8
|
|
8
9
|
|
9
10
|
class NetworkConfig(str, Enum):
|
@@ -18,6 +19,7 @@ class PluginConfig:
|
|
18
19
|
default_wait_interval: Optional[int] = None
|
19
20
|
default_wait_retries: Optional[int] = None
|
20
21
|
network_name: Optional[str] = None
|
22
|
+
test_with_mocks: bool = False
|
21
23
|
|
22
24
|
|
23
25
|
@dataclass
|
@@ -135,3 +137,12 @@ class GeneralConfig:
|
|
135
137
|
if self.plugin_config.network_name is not None:
|
136
138
|
return self.plugin_config.network_name
|
137
139
|
return self.user_config.default_network
|
140
|
+
|
141
|
+
def get_test_with_mocks(self) -> bool:
|
142
|
+
return self.plugin_config.test_with_mocks
|
143
|
+
|
144
|
+
def check_local_rpc(self) -> bool:
|
145
|
+
SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
|
146
|
+
rpc_url = self.get_rpc_url()
|
147
|
+
domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
|
148
|
+
return domain in SUPPORTED_RPC_DOMAINS
|