genlayer-test 0.1.0b3__py3-none-any.whl → 0.1.0b5__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.
@@ -0,0 +1,402 @@
1
+ Metadata-Version: 2.4
2
+ Name: genlayer-test
3
+ Version: 0.1.0b5
4
+ Summary: GenLayer Testing Suite
5
+ Author: GenLayer
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pytest
19
+ Requires-Dist: genlayer-py==0.1.0b1
20
+ Dynamic: license-file
21
+
22
+ # GenLayer Testing Suite
23
+
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
25
+ [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
26
+ [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
27
+ [![PyPI version](https://badge.fury.io/py/genlayer-test.svg)](https://badge.fury.io/py/genlayer-test)
28
+ [![Documentation](https://img.shields.io/badge/docs-genlayer-blue)](https://docs.genlayer.com/api-references/genlayer-test)
29
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
30
+
31
+ ## About
32
+
33
+ The GenLayer Testing Suite is a powerful testing framework designed to streamline the development and validation of intelligent contracts within the GenLayer ecosystem. Built on top of [pytest](https://docs.pytest.org/en/stable/) and [genlayer-py](https://docs.genlayer.com/api-references/genlayer-py), this suite provides developers with a comprehensive set of tools for deploying, interacting with, and testing intelligent contracts efficiently in a simulated GenLayer environment.
34
+
35
+ ## 🚀 Quick Start
36
+
37
+ ### Installation
38
+
39
+ ```bash
40
+ pip install genlayer-test
41
+ ```
42
+
43
+ ### Basic Usage
44
+
45
+ ```python
46
+ from gltest import get_contract_factory, default_account, create_account
47
+ from gltest.assertions import tx_execution_succeeded
48
+
49
+ factory = get_contract_factory("MyContract")
50
+ # Deploy a contract with default account
51
+ contract = factory.deploy() # This will be deployed with default_account
52
+ assert contract.account == default_account
53
+
54
+ # Deploy a contract with other account
55
+ other_account = create_account()
56
+ contract = factory.deploy(account=other_account)
57
+ assert contract.account == other_account
58
+
59
+ # Interact with the contract
60
+ result = contract.get_value() # Read method
61
+ tx_receipt = contract.set_value(args=["new_value"]) # Write method
62
+
63
+ assert tx_execution_succeeded(tx_receipt)
64
+ ```
65
+
66
+ ## 📋 Table of Contents
67
+
68
+ - [About](#about)
69
+ - [Quick Start](#-quick-start)
70
+ - [Prerequisites](#prerequisites)
71
+ - [Installation and Usage](#installation-and-usage)
72
+ - [Key Features](#-key-features)
73
+ - [Examples](#-examples)
74
+ - [Best Practices](#-best-practices)
75
+ - [Troubleshooting](#-troubleshooting)
76
+ - [Contributing](#-contributing)
77
+ - [License](#-license)
78
+ - [Support](#-support)
79
+
80
+ ## Prerequisites
81
+
82
+ Before installing GenLayer Testing Suite, ensure you have the following prerequisites installed:
83
+
84
+ - Python (>=3.12)
85
+ - GenLayer Studio (Docker deployment)
86
+ - pip (Python package installer)
87
+
88
+ ## Installation and Usage
89
+
90
+ ### Installation Options
91
+
92
+ 1. Install from PyPI (recommended):
93
+ ```bash
94
+ $ pip install genlayer-test
95
+ ```
96
+
97
+ 2. Install from source:
98
+ ```bash
99
+ $ git clone https://github.com/yeagerai/genlayer-testing-suite
100
+ $ cd genlayer-testing-suite
101
+ $ pip install -e .
102
+ ```
103
+
104
+ ### Running Tests
105
+
106
+ 1. Run all tests:
107
+ ```bash
108
+ $ gltest
109
+ ```
110
+
111
+ 2. Run specific test file:
112
+ ```bash
113
+ $ gltest tests/test_mycontract.py
114
+ ```
115
+
116
+ 3. Run tests with specific markers:
117
+ ```bash
118
+ $ gltest -m "integration"
119
+ ```
120
+
121
+ 4. Run tests with verbose output:
122
+ ```bash
123
+ $ gltest -v
124
+ ```
125
+
126
+ 5. Run tests in specific contracts directories, by default `<path_to_contracts>` is set to `contracts/`
127
+ ```bash
128
+ $ gltest --contracts-dir <path_to_contracts>
129
+ ```
130
+
131
+ ## 🚀 Key Features
132
+
133
+ - **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
134
+ - **Account & Transaction Management** – Create, fund, and track accounts and transactions within the GenLayer Simulator.
135
+ - **Contract Deployment & Interaction** – Deploy contracts, call methods, and monitor events seamlessly.
136
+ - **CLI Compatibility** – Run tests directly from the command line, ensuring smooth integration with the GenLayer CLI.
137
+ - **State Injection & Consensus Simulation** – Modify contract states dynamically and simulate consensus scenarios for advanced testing.
138
+ - **Prompt Testing & Statistical Analysis** – Evaluate and statistically test prompts for AI-driven contract execution.
139
+ - **Scalability to Security & Audit Tools** – Designed to extend into security testing and smart contract auditing.
140
+
141
+ ## 📚 Examples
142
+
143
+ ### Project Structure
144
+
145
+ Before diving into the examples, let's understand the basic project structure:
146
+
147
+ ```
148
+ genlayer-example/
149
+ ├── contracts/ # Contract definitions
150
+ │ └── storage.gpy # Example storage contract
151
+ └── test/ # Test files
152
+ └── test_contract.py # Contract test cases
153
+ ```
154
+
155
+ ### Storage Contract Example
156
+
157
+ Let's examine a simple Storage contract that demonstrates basic read and write operations:
158
+
159
+ ```python
160
+ # { "Depends": "py-genlayer:test" }
161
+
162
+ from genlayer import *
163
+
164
+
165
+ # contract class
166
+ class Storage(gl.Contract):
167
+ # State variable to store data
168
+ storage: str
169
+
170
+ # Constructor - initializes the contract state
171
+ def __init__(self, initial_storage: str):
172
+ self.storage = initial_storage
173
+
174
+ # Read method - marked with @gl.public.view decorator
175
+ # Returns the current storage value
176
+ @gl.public.view
177
+ def get_storage(self) -> str:
178
+ return self.storage
179
+
180
+ # Write method - marked with @gl.public.write decorator
181
+ # Updates the storage value
182
+ @gl.public.write
183
+ def update_storage(self, new_storage: str) -> None:
184
+ self.storage = new_storage
185
+ ```
186
+
187
+ Key features demonstrated in this contract:
188
+ - State variable declaration
189
+ - Constructor with initialization
190
+ - Read-only method with `@gl.public.view` decorator
191
+ - State-modifying method with `@gl.public.write` decorator
192
+ - Type hints for better code clarity
193
+
194
+ ### Contract Deployment
195
+
196
+ Here's how to deploy the Storage contract:
197
+
198
+ ```python
199
+ from gltest import get_contract_factory, default_account
200
+
201
+ def test_deployment():
202
+ # Get the contract factory for your contract
203
+ # it will search in the contracts directory
204
+ factory = get_contract_factory("Storage")
205
+
206
+ # Deploy the contract with constructor arguments
207
+ contract = factory.deploy(
208
+ args=["initial_value"], # Constructor arguments
209
+ account=default_account, # Account to deploy from
210
+ consensus_max_rotations=3, # Optional: max consensus rotations
211
+ leader_only=False, # Optional: whether to run only on leader
212
+ )
213
+
214
+ # Contract is now deployed and ready to use
215
+ assert contract.address is not None
216
+ ```
217
+
218
+ ### Read Methods
219
+
220
+ Reading from the contract is straightforward:
221
+
222
+ ```python
223
+ from gltest import get_contract_factory, default_account
224
+
225
+ def test_read_methods():
226
+ # Get the contract factory and deploy the contract
227
+ factory = get_contract_factory("Storage")
228
+ contract = factory.deploy(account=default_account)
229
+
230
+ # Call a read-only method
231
+ result = contract.get_value(args=[])
232
+
233
+ # Assert the result matches the initial value
234
+ assert result == "initial_value"
235
+ ```
236
+
237
+ ### Write Methods
238
+
239
+ Writing to the contract requires transaction handling:
240
+
241
+ ```python
242
+ from gltest import get_contract_factory
243
+ from gltest.assertions import tx_execution_succeeded
244
+
245
+ def test_write_methods():
246
+ # Get the contract factory and deploy the contract
247
+ factory = get_contract_factory("Storage")
248
+ contract = factory.deploy()
249
+
250
+ # Call a write method with arguments
251
+ tx_receipt = contract.update_storage(
252
+ args=["new_value"], # Method arguments
253
+ value=0, # Optional: amount of native currency to send
254
+ consensus_max_rotations=3, # Optional: max consensus rotations
255
+ leader_only=False, # Optional: whether to run only on leader
256
+ wait_interval=1, # Optional: seconds between status checks
257
+ wait_retries=10, # Optional: max number of retries
258
+ )
259
+
260
+ # Verify the transaction was successful
261
+ assert tx_execution_succeeded(tx_receipt)
262
+
263
+ # Verify the value was updated
264
+ assert contract.get_storage() == "new_value"
265
+ ```
266
+
267
+ ## 📝 Best Practices
268
+
269
+ 1. **Test Organization**
270
+ - Keep tests in a dedicated `tests` directory
271
+ - Use descriptive test names
272
+ - Group related tests using pytest markers
273
+
274
+ 2. **Contract Deployment**
275
+ - Always verify deployment success
276
+ - Use appropriate consensus parameters
277
+ - Handle deployment errors gracefully
278
+
279
+ 3. **Transaction Handling**
280
+ - Always wait for transaction finalization
281
+ - Verify transaction status
282
+ - Handle transaction failures appropriately
283
+
284
+ 4. **State Management**
285
+ - Reset state between tests
286
+ - Use fixtures for common setup
287
+ - Avoid test dependencies
288
+
289
+ ## 🔧 Troubleshooting
290
+
291
+ ### Common Issues
292
+
293
+ 1. **Deployment Failures**
294
+ - **Problem**: Contract deployment fails due to various reasons like insufficient funds, invalid contract code, or network issues.
295
+ - **Solution**: Implement proper error handling
296
+ ```python
297
+ try:
298
+ contract = factory.deploy(args=["initial_value"])
299
+ except DeploymentError as e:
300
+ print(f"Deployment failed: {e}")
301
+ ```
302
+
303
+ 2. **Transaction Timeouts**
304
+ - **Problem**: Transactions take too long to complete or fail due to network congestion or consensus delays.
305
+ - **Solution**: Adjust timeout parameters and implement retry logic:
306
+ ```python
307
+ tx_receipt = contract.set_value(
308
+ args=["new_value"],
309
+ wait_interval=2, # Increase wait interval between status checks
310
+ wait_retries=20, # Increase number of retry attempts
311
+ )
312
+ ```
313
+
314
+ 3. **Consensus Issues**
315
+ - **Problem**: Transactions fail due to consensus-related problems like network partitions or slow consensus.
316
+ - **Solution**: Adjust consensus parameters and try different modes:
317
+ ```python
318
+ # Try with increased consensus parameters
319
+ contract = factory.deploy(
320
+ consensus_max_rotations=5, # Increase number of consensus rotations
321
+ leader_only=True, # Try leader-only mode for faster execution
322
+ )
323
+
324
+ # For critical operations, use more conservative settings
325
+ contract = factory.deploy(
326
+ consensus_max_rotations=10, # More rotations for better reliability
327
+ leader_only=False, # Full consensus for better security
328
+ wait_interval=3, # Longer wait between checks
329
+ wait_retries=30 # More retries for consensus
330
+ )
331
+ ```
332
+
333
+ 4. **Contracts Directory Issues**
334
+ - **Problem**: `get_contract_factory` can't find your contract files.
335
+ - **Solution**: Ensure proper directory structure and configuration:
336
+ ```bash
337
+ # Default structure
338
+ your_project/
339
+ ├── contracts/ # Default contracts directory
340
+ │ └── my_contract.gpy # Your contract file
341
+ └── tests/
342
+ └── test_contract.py # Your test file
343
+
344
+ # If using a different directory structure
345
+ gltest --contracts-dir /path/to/your/contracts
346
+ ```
347
+
348
+ 5. **Contract File Naming and Structure**
349
+ - **Problem**: Contracts aren't being recognized or loaded properly.
350
+ - **Solution**: Follow the correct naming and structure conventions:
351
+ ```python
352
+ # Correct file: contracts/my_contract.gpy
353
+ # Correct structure:
354
+ from genlayer import *
355
+
356
+ class MyContract(gl.Contract):
357
+ # Contract code here
358
+ pass
359
+
360
+ # Incorrect file: contracts/my_contract.py # Wrong extension
361
+ # Incorrect structure:
362
+ class MyContract: # Missing gl.Contract inheritance
363
+ pass
364
+ ```
365
+
366
+ 6. **Environment Setup Issues**
367
+ - **Problem**: Tests fail due to missing or incorrect environment setup.
368
+ - **Solution**: Verify your environment:
369
+ ```bash
370
+ # Check Python version
371
+ python --version # Should be >= 3.8
372
+
373
+ # Check GenLayer Studio status
374
+ docker ps # Should show GenLayer Studio running
375
+
376
+ # Verify package installation
377
+ pip list | grep genlayer-test # Should show installed version
378
+ ```
379
+
380
+ ## 🤝 Contributing
381
+
382
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
383
+
384
+ 1. Fork the repository
385
+ 2. Create your feature branch
386
+ 3. Commit your changes
387
+ 4. Push to the branch
388
+ 5. Create a Pull Request
389
+
390
+ ## 📄 License
391
+
392
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
393
+
394
+ ## 💬 Support
395
+
396
+ - [Documentation](https://docs.genlayer.com/api-references/genlayer-test)
397
+ - [Discord Community](https://discord.gg/VpfmXEMN66)
398
+ - [GitHub Issues](https://github.com/yeagerai/genlayer-testing-suite/issues)
399
+ - [Twitter](https://x.com/GenLayer)
400
+
401
+
402
+
@@ -0,0 +1,24 @@
1
+ genlayer_test-0.1.0b5.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
2
+ gltest/__init__.py,sha256=AK_YfRvwlhrOheOelUG8qIRG17on0-nFCF747dopg2w,332
3
+ gltest/assertions.py,sha256=wrT5R8yLAxm0B1EzmdZjOK5gBriugIrdtOIt9Qi0G-k,591
4
+ gltest/exceptions.py,sha256=V3OJlvW5iiv7lYmwV6h_EtAi2vqy63b7RcLB6ohDZ20,502
5
+ gltest/plugin_config.py,sha256=8Z97RtEJ89OcRbki_oRuBBVct_q56BFmKvthan1y9Y4,840
6
+ gltest/plugin_hooks.py,sha256=py1rzIR9QSsFOt8SEePPL96e-8DeiPFxvcPZurRlExM,1436
7
+ gltest/types.py,sha256=BODmwTr2gAUEiO9FjiuTiWwuKvXgo4xZWstQWNUfnlw,156
8
+ gltest/artifacts/__init__.py,sha256=QCsQI8BSOHQwGWFxZVDRL-HA0Dyae3Re24E-yAZrflA,86
9
+ gltest/artifacts/contract.py,sha256=2wmfQF6FAiNuMlr9pePK7ZJjQj5Tl7-3SefeH3OnSRQ,3089
10
+ gltest/glchain/__init__.py,sha256=X-mEbREoAOe9K4n74C55gCiXH4wItzY5HTJcg3_F3mI,412
11
+ gltest/glchain/account.py,sha256=vOXxcR09NkT6iFw_m49DW1nVC7kP8wf23555iPPXRkw,402
12
+ gltest/glchain/client.py,sha256=q04LIQy5SCIrYZflZiTapfeQ-AaSTa0w369ehnVbJLM,532
13
+ gltest/glchain/contract.py,sha256=bma3aepoZkVOsGdbNbUKAMvbgW6kzmnWnTs9PrgVdas,7719
14
+ gltest/helpers/__init__.py,sha256=a9uBXWj44P-Gu0vKKgVBPOSuZ5yumVFLYAzywrGZ36Y,179
15
+ gltest/helpers/fixture_snapshot.py,sha256=r7olLq8Aq0h0mMydlvmr3sNxWZeUns_wx1WK3HFAVaY,1774
16
+ gltest/helpers/take_snapshot.py,sha256=eXqEKXM2hcox3pLGIcNddobU8zXPQvD-Iwf87eHqW2s,1276
17
+ gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
18
+ tests/conftest.py,sha256=vY6XQ9K2kZ8Qo-h9-PmkhmEIRyR_ILjbM7R0dPPjU_g,28
19
+ tests/test_plugin_hooks.py,sha256=FQOrkhoXLinq0sjvoYjr63Oqg-ZVPcNFeUrK4bqrn4E,2020
20
+ genlayer_test-0.1.0b5.dist-info/METADATA,sha256=Ox0jwGv_RkvZL5baRbQjpBENdJPmhl5DjHC58zCdl-s,12925
21
+ genlayer_test-0.1.0b5.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
22
+ genlayer_test-0.1.0b5.dist-info/entry_points.txt,sha256=rXhrPVq2IhVsd4uWzxzwCTx7jA1KcQIVNxDCUuxq4f8,89
23
+ genlayer_test-0.1.0b5.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
24
+ genlayer_test-0.1.0b5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 YeagerAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
gltest/exceptions.py CHANGED
@@ -2,3 +2,19 @@ class DeploymentError(Exception):
2
2
  """Raised when a contract deployment fails."""
3
3
 
4
4
  pass
5
+ class FixtureSnapshotError(Exception):
6
+ """Raised when there's an error restoring a snapshot."""
7
+ pass
8
+
9
+ class FixtureAnonymousFunctionError(Exception):
10
+ """Raised when a fixture is an anonymous function."""
11
+ pass
12
+
13
+ class InvalidSnapshotError(Exception):
14
+ """Raised when a snapshot is invalid."""
15
+ pass
16
+
17
+ class HelperError(Exception):
18
+ """Raised when a helper function fails."""
19
+ pass
20
+
@@ -1,5 +1,5 @@
1
1
  from .contract import Contract, ContractFactory, get_contract_factory
2
- from .client import gl_client, get_gl_client
2
+ from .client import get_gl_client, get_gl_provider
3
3
  from .account import create_accounts, create_account, accounts, default_account
4
4
 
5
5
 
@@ -7,10 +7,10 @@ __all__ = [
7
7
  "Contract",
8
8
  "ContractFactory",
9
9
  "get_contract_factory",
10
- "gl_client",
11
10
  "create_account",
12
11
  "default_account",
13
12
  "accounts",
14
13
  "create_accounts",
15
14
  "get_gl_client",
15
+ "get_gl_provider",
16
16
  ]
gltest/glchain/client.py CHANGED
@@ -2,9 +2,7 @@ from genlayer_py.chains import localnet
2
2
  from genlayer_py import create_client
3
3
  from .account import default_account
4
4
  from functools import lru_cache
5
-
6
- # Create the client
7
- gl_client = create_client(chain=localnet, account=default_account)
5
+ from gltest.plugin_config import get_rpc_url
8
6
 
9
7
 
10
8
  @lru_cache(maxsize=1)
@@ -12,4 +10,14 @@ def get_gl_client():
12
10
  """
13
11
  Get the GenLayer client instance.
14
12
  """
15
- return gl_client
13
+ return create_client(
14
+ chain=localnet, account=default_account, endpoint=get_rpc_url()
15
+ )
16
+
17
+
18
+ def get_gl_provider():
19
+ """
20
+ Get the GenLayer provider instance.
21
+ """
22
+ client = get_gl_client()
23
+ return client.provider
@@ -7,6 +7,7 @@ from gltest.types import CalldataEncodable, GenLayerTransaction, TransactionStat
7
7
  from eth_account.signers.local import LocalAccount
8
8
  from typing import List, Any, Type, Optional, Dict, Callable
9
9
  import types
10
+ from gltest.plugin_config import get_default_wait_interval, get_default_wait_retries
10
11
 
11
12
 
12
13
  @dataclass
@@ -87,9 +88,9 @@ class Contract:
87
88
  value: int = 0,
88
89
  consensus_max_rotations: Optional[int] = None,
89
90
  leader_only: bool = False,
90
- wait_interval: Optional[int] = None,
91
- wait_retries: Optional[int] = None,
92
91
  wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
92
+ wait_interval: int = get_default_wait_interval(),
93
+ wait_retries: int = get_default_wait_retries(),
93
94
  wait_triggered_transactions: bool = True,
94
95
  wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
95
96
  ) -> GenLayerTransaction:
@@ -106,15 +107,11 @@ class Contract:
106
107
  leader_only=leader_only,
107
108
  args=args,
108
109
  )
109
- extra_args = {}
110
- if wait_interval is not None:
111
- extra_args["interval"] = wait_interval
112
- if wait_retries is not None:
113
- extra_args["retries"] = wait_retries
114
110
  receipt = client.wait_for_transaction_receipt(
115
111
  transaction_hash=tx_hash,
116
112
  status=wait_transaction_status,
117
- **extra_args,
113
+ interval=wait_interval,
114
+ retries=wait_retries,
118
115
  )
119
116
  if wait_triggered_transactions:
120
117
  triggered_transactions = receipt["triggered_transactions"]
@@ -122,7 +119,8 @@ class Contract:
122
119
  client.wait_for_transaction_receipt(
123
120
  transaction_hash=triggered_transaction,
124
121
  status=wait_triggered_transactions_status,
125
- **extra_args,
122
+ interval=wait_interval,
123
+ retries=wait_retries,
126
124
  )
127
125
  return receipt
128
126
 
@@ -160,8 +158,8 @@ class ContractFactory:
160
158
  account: Optional[LocalAccount] = None,
161
159
  consensus_max_rotations: Optional[int] = None,
162
160
  leader_only: bool = False,
163
- wait_interval: Optional[int] = None,
164
- wait_retries: Optional[int] = None,
161
+ wait_interval: int = get_default_wait_interval(),
162
+ wait_retries: int = get_default_wait_retries(),
165
163
  wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
166
164
  ) -> Contract:
167
165
  """
@@ -176,13 +174,11 @@ class ContractFactory:
176
174
  consensus_max_rotations=consensus_max_rotations,
177
175
  leader_only=leader_only,
178
176
  )
179
- extra_args = {}
180
- if wait_interval is not None:
181
- extra_args["interval"] = wait_interval
182
- if wait_retries is not None:
183
- extra_args["retries"] = wait_retries
184
177
  tx_receipt = client.wait_for_transaction_receipt(
185
- transaction_hash=tx_hash, status=wait_transaction_status, **extra_args
178
+ transaction_hash=tx_hash,
179
+ status=wait_transaction_status,
180
+ interval=wait_interval,
181
+ retries=wait_retries,
186
182
  )
187
183
  if (
188
184
  not tx_receipt
@@ -0,0 +1,8 @@
1
+ from .take_snapshot import take_snapshot
2
+ from .fixture_snapshot import load_fixture, clear_snapshots
3
+
4
+ __all__ = [
5
+ "take_snapshot",
6
+ "load_fixture",
7
+ "clear_snapshots",
8
+ ]
@@ -0,0 +1,67 @@
1
+ from typing import TypeVar, Callable, List, Any
2
+ from dataclasses import dataclass
3
+ from .take_snapshot import SnapshotRestorer, take_snapshot
4
+ from gltest.exceptions import (
5
+ FixtureSnapshotError,
6
+ InvalidSnapshotError,
7
+ FixtureAnonymousFunctionError,
8
+ )
9
+
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ @dataclass
15
+ class Snapshot:
16
+ """Represents a snapshot of the blockchain state."""
17
+
18
+ restorer: SnapshotRestorer
19
+ fixture: Callable[[], Any]
20
+ data: Any
21
+
22
+
23
+ # Global storage for snapshots
24
+ _snapshots: List[Snapshot] = []
25
+
26
+
27
+ def load_fixture(fixture: Callable[[], T]) -> T:
28
+ """
29
+ Useful in tests for setting up the desired state of the network.
30
+ """
31
+ if fixture.__name__ == "<lambda>":
32
+ raise FixtureAnonymousFunctionError("Fixtures must be named functions")
33
+
34
+ # Find existing snapshot for this fixture
35
+ global _snapshots
36
+ snapshot = next((s for s in _snapshots if s.fixture == fixture), None)
37
+
38
+ if snapshot is not None:
39
+ try:
40
+ snapshot.restorer.restore()
41
+ # Remove snapshots that were taken after this one
42
+
43
+ _snapshots = [
44
+ s
45
+ for s in _snapshots
46
+ if int(s.restorer.snapshot_id) <= int(snapshot.restorer.snapshot_id)
47
+ ]
48
+ except Exception as e:
49
+ if isinstance(e, InvalidSnapshotError):
50
+ raise FixtureSnapshotError(e) from e
51
+ raise e
52
+
53
+ return snapshot.data
54
+ else:
55
+ # Execute the fixture and take a snapshot
56
+ data = fixture()
57
+ restorer = take_snapshot()
58
+
59
+ _snapshots.append(Snapshot(restorer=restorer, fixture=fixture, data=data))
60
+
61
+ return data
62
+
63
+
64
+ def clear_snapshots() -> None:
65
+ """Clears every existing snapshot."""
66
+ global _snapshots
67
+ _snapshots = []
@@ -0,0 +1,41 @@
1
+ from gltest.glchain import get_gl_provider
2
+ from dataclasses import dataclass
3
+ from typing import Callable
4
+ from gltest.exceptions import HelperError, InvalidSnapshotError
5
+
6
+
7
+ @dataclass
8
+ class SnapshotRestorer:
9
+ """Class responsible for restoring blockchain state to a snapshot."""
10
+
11
+ restore: Callable[[], None]
12
+ snapshot_id: int
13
+
14
+
15
+ def take_snapshot() -> SnapshotRestorer:
16
+ """
17
+ Take a snapshot of the current blockchain state and return a function to restore the state and the snapshot ID.
18
+ """
19
+ provider = get_gl_provider()
20
+ snapshot_id = provider.make_request(method="sim_createSnapshot", params=[])[
21
+ "result"
22
+ ]
23
+ if not isinstance(snapshot_id, int):
24
+ raise HelperError(
25
+ "Assertion error: the value returned by evm_snapshot should be a int"
26
+ )
27
+
28
+ def restore() -> None:
29
+ reverted = provider.make_request(
30
+ method="sim_restoreSnapshot", params=[snapshot_id]
31
+ )["result"]
32
+
33
+ if not isinstance(reverted, bool):
34
+ raise HelperError(
35
+ "Assertion error: the value returned by evm_revert should be a boolean"
36
+ )
37
+
38
+ if not reverted:
39
+ raise InvalidSnapshotError("")
40
+
41
+ return SnapshotRestorer(restore=restore, snapshot_id=snapshot_id)
gltest/plugin_config.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from pathlib import Path
2
2
 
3
3
  _contracts_dir = None
4
+ _rpc_url = None
5
+ _default_wait_interval = None
6
+ _default_wait_retries = None
4
7
 
5
8
 
6
9
  def set_contracts_dir(path: Path):
@@ -10,3 +13,30 @@ def set_contracts_dir(path: Path):
10
13
 
11
14
  def get_contracts_dir() -> Path:
12
15
  return Path(_contracts_dir)
16
+
17
+
18
+ def set_rpc_url(rpc_url: str):
19
+ global _rpc_url
20
+ _rpc_url = rpc_url
21
+
22
+
23
+ def get_rpc_url() -> str:
24
+ return _rpc_url
25
+
26
+
27
+ def set_default_wait_interval(default_wait_interval: int):
28
+ global _default_wait_interval
29
+ _default_wait_interval = default_wait_interval
30
+
31
+
32
+ def get_default_wait_interval() -> int:
33
+ return _default_wait_interval
34
+
35
+
36
+ def set_default_wait_retries(default_wait_retries: int):
37
+ global _default_wait_retries
38
+ _default_wait_retries = default_wait_retries
39
+
40
+
41
+ def get_default_wait_retries() -> int:
42
+ return _default_wait_retries
gltest/plugin_hooks.py CHANGED
@@ -1,16 +1,51 @@
1
- from gltest.plugin_config import set_contracts_dir
1
+ from gltest.plugin_config import (
2
+ set_contracts_dir,
3
+ set_default_wait_interval,
4
+ set_default_wait_retries,
5
+ set_rpc_url,
6
+ )
2
7
  from pathlib import Path
8
+ from genlayer_py.chains.localnet import SIMULATOR_JSON_RPC_URL
3
9
 
4
10
 
5
11
  def pytest_addoption(parser):
6
- parser.addoption(
12
+ group = parser.getgroup("gltest")
13
+ group.addoption(
7
14
  "--contracts-dir",
8
15
  action="store",
9
16
  default="contracts",
10
17
  help="Directory containing contract files",
11
18
  )
12
19
 
20
+ group.addoption(
21
+ "--default-wait-interval",
22
+ action="store",
23
+ default=10000,
24
+ help="Default wait interval for waiting transaction receipts",
25
+ )
26
+
27
+ group.addoption(
28
+ "--default-wait-retries",
29
+ action="store",
30
+ default=15,
31
+ help="Default wait retries for waiting transaction receipts",
32
+ )
33
+
34
+ group.addoption(
35
+ "--rpc-url",
36
+ action="store",
37
+ default=SIMULATOR_JSON_RPC_URL,
38
+ help="RPC URL for the genlayer network",
39
+ )
40
+
13
41
 
14
42
  def pytest_configure(config):
15
43
  contracts_dir = config.getoption("--contracts-dir")
44
+ default_wait_interval = config.getoption("--default-wait-interval")
45
+ default_wait_retries = config.getoption("--default-wait-retries")
46
+ rpc_url = config.getoption("--rpc-url")
47
+
16
48
  set_contracts_dir(Path(contracts_dir))
49
+ set_default_wait_interval(int(default_wait_interval))
50
+ set_default_wait_retries(int(default_wait_retries))
51
+ set_rpc_url(str(rpc_url))
tests/conftest.py ADDED
@@ -0,0 +1 @@
1
+ pytest_plugins = 'pytester'
@@ -0,0 +1,78 @@
1
+ def test_help_message(pytester):
2
+ result = pytester.runpytest(
3
+ "--help",
4
+ )
5
+ # fnmatch_lines does an assertion internally
6
+ result.stdout.fnmatch_lines(
7
+ [
8
+ "gltest:",
9
+ "*--contracts-dir=CONTRACTS_DIR",
10
+ "*Directory containing contract files",
11
+ "*--default-wait-interval=DEFAULT_WAIT_INTERVAL",
12
+ "*Default wait interval for waiting transaction receipts",
13
+ "*--default-wait-retries=DEFAULT_WAIT_RETRIES",
14
+ "*Default wait retries for waiting transaction receipts",
15
+ "*--rpc-url=RPC_URL*RPC URL for the genlayer network",
16
+ ]
17
+ )
18
+
19
+
20
+ def test_default_wait_interval(pytester):
21
+
22
+ pytester.makepyfile(
23
+ """
24
+ from gltest.plugin_config import get_default_wait_interval
25
+
26
+ def test_default_wait_interval():
27
+ assert get_default_wait_interval() == 5000
28
+ """
29
+ )
30
+
31
+ result = pytester.runpytest("--default-wait-interval=5000", "-v")
32
+
33
+ result.stdout.fnmatch_lines(
34
+ [
35
+ "*::test_default_wait_interval PASSED*",
36
+ ]
37
+ )
38
+ assert result.ret == 0
39
+
40
+
41
+ def test_default_wait_retries(pytester):
42
+ pytester.makepyfile(
43
+ """
44
+ from gltest.plugin_config import get_default_wait_retries
45
+
46
+ def test_default_wait_retries():
47
+ assert get_default_wait_retries() == 4000
48
+ """
49
+ )
50
+
51
+ result = pytester.runpytest("--default-wait-retries=4000", "-v")
52
+
53
+ result.stdout.fnmatch_lines(
54
+ [
55
+ "*::test_default_wait_retries PASSED*",
56
+ ]
57
+ )
58
+ assert result.ret == 0
59
+
60
+
61
+ def test_rpc_url(pytester):
62
+ pytester.makepyfile(
63
+ """
64
+ from gltest.plugin_config import get_rpc_url
65
+
66
+ def test_rpc_url():
67
+ assert get_rpc_url() == 'http://custom-rpc-url:8545'
68
+ """
69
+ )
70
+
71
+ result = pytester.runpytest("--rpc-url=http://custom-rpc-url:8545", "-v")
72
+
73
+ result.stdout.fnmatch_lines(
74
+ [
75
+ "*::test_rpc_url PASSED*",
76
+ ]
77
+ )
78
+ assert result.ret == 0
@@ -1,97 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: genlayer-test
3
- Version: 0.1.0b3
4
- Summary: GenLayer Testing Suite
5
- Author: GenLayer
6
- License-Expression: MIT
7
- Classifier: Development Status :: 4 - Beta
8
- Classifier: Intended Audience :: Developers
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.8
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Topic :: Software Development :: Testing
15
- Requires-Python: >=3.8
16
- Description-Content-Type: text/markdown
17
- Requires-Dist: pytest
18
- Requires-Dist: genlayer-py==0.1.0b1
19
-
20
- # GenLayer Testing Suite
21
-
22
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
23
- [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
24
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
25
-
26
-
27
- ## About
28
-
29
- The GenLayer Testing Suite is a powerful testing framework designed to streamline the development and validation of intelligent contracts within the GenLayer ecosystem. Built on top of [pytest](https://docs.pytest.org/en/stable/), this suite provides developers with a comprehensive set of tools for deploying, interacting with, and testing intelligent contracts efficiently in a simulated GenLayer environment.
30
-
31
-
32
- ## Prerequisites
33
-
34
- Before installing GenLayer Testing Suite, ensure you have the following prerequisites installed:
35
-
36
- - Python (>=3.8)
37
- - GenLayer Studio (Docker deployment)
38
- - pip (Python package installer)
39
-
40
- ## 🛠️ Installation and Usage
41
-
42
- ### Installation Options
43
-
44
- 1. Install from PyPI (recommended):
45
- ```bash
46
- $ pip install genlayer-test
47
- ```
48
-
49
- 2. Install from source:
50
- ```bash
51
- $ git clone https://github.com/yeagerai/genlayer-testing-suite
52
- $ cd genlayer-testing-suite
53
- $ pip install -e .
54
- ```
55
-
56
-
57
- ### Running Tests
58
-
59
- 1. Run all tests:
60
- ```bash
61
- $ gltest
62
- ```
63
-
64
- 2. Run specific test file:
65
- ```bash
66
- $ gltest tests/test_mycontract.py
67
- ```
68
-
69
- 3. Run tests with specific markers:
70
- ```bash
71
- $ gltest -m "integration"
72
- ```
73
-
74
- 4. Run tests with verbose output:
75
- ```bash
76
- $ gltest -v
77
- ```
78
-
79
- For more detailed information and advanced usage, please refer to our [documentation](https://docs.genlayer.com/api-references/genlayer-testing-suite).
80
-
81
- ## 🚀 Key Features
82
- - **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
83
-
84
- - **Account & Transaction Management** – Create, fund, and track accounts and transactions within the GenLayer Simulator.
85
-
86
- - **Contract Deployment & Interaction** – Deploy contracts, call methods, and monitor events seamlessly.
87
-
88
- - **CLI Compatibility** – Run tests directly from the command line, ensuring smooth integration with the GenLayer CLI.
89
-
90
- - **State Injection & Consensus Simulation** – Modify contract states dynamically and simulate consensus scenarios for advanced testing.
91
-
92
- - **Prompt Testing & Statistical Analysis** – Evaluate and statistically test prompts for AI-driven contract execution.
93
-
94
- - **Scalability to Security & Audit Tools** – Designed to extend into security testing and smart contract auditing.
95
-
96
-
97
- By leveraging the GenLayer Testing Suite, developers can ensure their contracts perform reliably and securely before deployment on the live network.
@@ -1,18 +0,0 @@
1
- gltest/__init__.py,sha256=AK_YfRvwlhrOheOelUG8qIRG17on0-nFCF747dopg2w,332
2
- gltest/assertions.py,sha256=wrT5R8yLAxm0B1EzmdZjOK5gBriugIrdtOIt9Qi0G-k,591
3
- gltest/exceptions.py,sha256=g7IF0qA8L0PdonXHTsbFnWx05U3jEYWOL3EFTW21IL4,95
4
- gltest/plugin_config.py,sha256=O9Dx9IejbBJe-CJ9h8iMVtLzjWlzLGcTGOIgJM6gLWc,204
5
- gltest/plugin_hooks.py,sha256=cy4-LPm3Q-ZaO94t5vgjbPjQB8MRxYGGRXeDBeJ7buU,399
6
- gltest/types.py,sha256=BODmwTr2gAUEiO9FjiuTiWwuKvXgo4xZWstQWNUfnlw,156
7
- gltest/artifacts/__init__.py,sha256=QCsQI8BSOHQwGWFxZVDRL-HA0Dyae3Re24E-yAZrflA,86
8
- gltest/artifacts/contract.py,sha256=2wmfQF6FAiNuMlr9pePK7ZJjQj5Tl7-3SefeH3OnSRQ,3089
9
- gltest/glchain/__init__.py,sha256=wD2Hzdz_wKpu-mCAX5iBINZDCNdKezLVI2lywkSkjvw,400
10
- gltest/glchain/account.py,sha256=vOXxcR09NkT6iFw_m49DW1nVC7kP8wf23555iPPXRkw,402
11
- gltest/glchain/client.py,sha256=U_JJJQ0V8oLPLeIbGpFcHOtnCVPJIoN9DMlvHSD1C0I,355
12
- gltest/glchain/contract.py,sha256=46QFDjwwg6n0qmjMTPXR1wueV2ZdFOc1g2nas0U9C_c,7837
13
- gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
14
- genlayer_test-0.1.0b3.dist-info/METADATA,sha256=zzWsz5KgLuoQO7urlzrLz9BYQMmpP-QOiyZZB3zbeiw,3409
15
- genlayer_test-0.1.0b3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
16
- genlayer_test-0.1.0b3.dist-info/entry_points.txt,sha256=rXhrPVq2IhVsd4uWzxzwCTx7jA1KcQIVNxDCUuxq4f8,89
17
- genlayer_test-0.1.0b3.dist-info/top_level.txt,sha256=GSdrnQbiLcZssmtCpbDgBTygsc8Bt_TPeYjwm0FmpdA,18
18
- genlayer_test-0.1.0b3.dist-info/RECORD,,