genlayer-test 0.0.1__py3-none-any.whl → 0.1.0b1__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,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: genlayer-test
3
+ Version: 0.1.0b1
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.
@@ -0,0 +1,15 @@
1
+ gltest/__init__.py,sha256=AK_YfRvwlhrOheOelUG8qIRG17on0-nFCF747dopg2w,332
2
+ gltest/assertions.py,sha256=wrT5R8yLAxm0B1EzmdZjOK5gBriugIrdtOIt9Qi0G-k,591
3
+ gltest/cli.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
4
+ gltest/exceptions.py,sha256=g7IF0qA8L0PdonXHTsbFnWx05U3jEYWOL3EFTW21IL4,95
5
+ gltest/artifacts/__init__.py,sha256=QCsQI8BSOHQwGWFxZVDRL-HA0Dyae3Re24E-yAZrflA,86
6
+ gltest/artifacts/contract.py,sha256=tOXv5rOGkoEeBndEp2wB9YnaTNvBRqACkGMkzC_tt4E,1761
7
+ gltest/glchain/__init__.py,sha256=wD2Hzdz_wKpu-mCAX5iBINZDCNdKezLVI2lywkSkjvw,400
8
+ gltest/glchain/account.py,sha256=vOXxcR09NkT6iFw_m49DW1nVC7kP8wf23555iPPXRkw,402
9
+ gltest/glchain/client.py,sha256=U_JJJQ0V8oLPLeIbGpFcHOtnCVPJIoN9DMlvHSD1C0I,355
10
+ gltest/glchain/contract.py,sha256=pt-LdF8k8jWCmWhU2-LI72w-vdVyi2g9Ux59MR8W3Es,6469
11
+ genlayer_test-0.1.0b1.dist-info/METADATA,sha256=2nkEPJTInKomnfv-DnvCJoDTkyWrImvUsWyctCqu8mo,3409
12
+ genlayer_test-0.1.0b1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
13
+ genlayer_test-0.1.0b1.dist-info/entry_points.txt,sha256=JfU-i_s5OciWVqtb0Ye5Pu_A1jJkDkitwLn4esJPZz4,43
14
+ genlayer_test-0.1.0b1.dist-info/top_level.txt,sha256=xTaZGUzUZ0S1U6_lp5SwNMQWZJKFaXI1Yg8gGKFKFJg,7
15
+ genlayer_test-0.1.0b1.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gltest = gltest.cli:main
@@ -0,0 +1 @@
1
+ gltest
gltest/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ from gltest.glchain import (
2
+ create_account,
3
+ create_accounts,
4
+ get_contract_factory,
5
+ get_gl_client,
6
+ default_account,
7
+ accounts,
8
+ )
9
+
10
+ __all__ = [
11
+ "find_contract_definition",
12
+ "create_account",
13
+ "create_accounts",
14
+ "get_contract_factory",
15
+ "get_gl_client",
16
+ "default_account",
17
+ "accounts",
18
+ ]
@@ -0,0 +1,3 @@
1
+ from .contract import find_contract_definition
2
+
3
+ __all__ = ["find_contract_definition"]
@@ -0,0 +1,52 @@
1
+ import ast
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class ContractDefinition:
9
+ """Class that represents a contract definition from a .gpy file."""
10
+
11
+ contract_name: str
12
+ contract_code: str
13
+ source_file: str
14
+ ast_node: ast.ClassDef
15
+
16
+
17
+ def find_contract_definition(
18
+ contract_name: str, relative_contracts_dir: str = "contracts"
19
+ ) -> Optional[ContractDefinition]:
20
+ """
21
+ Search in the contracts directory for a contract definition.
22
+ TODO: Make this more robust to handle imports and other files.
23
+ """
24
+ # Use provided directory or default to 'contracts' in current working directory
25
+ contracts_dir = Path.cwd() / relative_contracts_dir
26
+
27
+ if not contracts_dir.exists():
28
+ raise FileNotFoundError(f"Contracts directory not found at: {contracts_dir}")
29
+
30
+ # Search through all .gpy files in the contracts directory
31
+ for file_path in contracts_dir.glob("*.gpy"):
32
+ try:
33
+ # Read the file content
34
+ with open(file_path, "r") as f:
35
+ content = f.read()
36
+
37
+ # Parse the content into an AST
38
+ tree = ast.parse(content)
39
+
40
+ # Search for class definitions
41
+ for node in ast.walk(tree):
42
+ if isinstance(node, ast.ClassDef) and node.name == contract_name:
43
+ # Found the contract class
44
+ return ContractDefinition(
45
+ contract_name=contract_name,
46
+ source_file=str(file_path),
47
+ contract_code=content,
48
+ ast_node=node,
49
+ )
50
+ except Exception as e:
51
+ raise ValueError(f"Error reading file {file_path}: {e}")
52
+ return None
gltest/assertions.py ADDED
@@ -0,0 +1,17 @@
1
+ from genlayer_py.types import GenLayerTransaction
2
+
3
+
4
+ def tx_execution_succeeded(result: GenLayerTransaction) -> bool:
5
+ if "consensus_data" not in result:
6
+ return False
7
+ if "leader_receipt" not in result["consensus_data"]:
8
+ return False
9
+ if "execution_result" not in result["consensus_data"]["leader_receipt"]:
10
+ return False
11
+ execution_result = result["consensus_data"]["leader_receipt"]["execution_result"]
12
+ return execution_result == "SUCCESS"
13
+
14
+
15
+ def tx_execution_failed(result: GenLayerTransaction) -> bool:
16
+ return not tx_execution_succeeded(result)
17
+
gltest/cli.py ADDED
@@ -0,0 +1,5 @@
1
+ import pytest
2
+
3
+
4
+ def main():
5
+ return pytest.main()
gltest/exceptions.py ADDED
@@ -0,0 +1,4 @@
1
+ class DeploymentError(Exception):
2
+ """Raised when a contract deployment fails."""
3
+
4
+ pass
@@ -0,0 +1,16 @@
1
+ from .contract import Contract, ContractFactory, get_contract_factory
2
+ from .client import gl_client, get_gl_client
3
+ from .account import create_accounts, create_account, accounts, default_account
4
+
5
+
6
+ __all__ = [
7
+ "Contract",
8
+ "ContractFactory",
9
+ "get_contract_factory",
10
+ "gl_client",
11
+ "create_account",
12
+ "default_account",
13
+ "accounts",
14
+ "create_accounts",
15
+ "get_gl_client",
16
+ ]
@@ -0,0 +1,17 @@
1
+ from genlayer_py import create_account
2
+
3
+
4
+ def create_accounts(n_accounts: int):
5
+ """
6
+ Create a list of accounts
7
+ """
8
+ accounts = []
9
+ for _ in range(n_accounts):
10
+ accounts.append(create_account())
11
+ return accounts
12
+
13
+ # Accounts for testing
14
+ accounts = create_accounts(n_accounts=10)
15
+
16
+ # Default account to use for transaction handling, if not specified
17
+ default_account = accounts[0]
@@ -0,0 +1,15 @@
1
+ from genlayer_py.chains import localnet
2
+ from genlayer_py import create_client
3
+ from .account import default_account
4
+ from functools import lru_cache
5
+
6
+ # Create the client
7
+ gl_client = create_client(chain=localnet, account=default_account)
8
+
9
+
10
+ @lru_cache(maxsize=1)
11
+ def get_gl_client():
12
+ """
13
+ Get the GenLayer client instance.
14
+ """
15
+ return gl_client
@@ -0,0 +1,192 @@
1
+ from dataclasses import dataclass
2
+ from gltest.artifacts import find_contract_definition
3
+ from gltest.assertions import tx_execution_failed
4
+ from gltest.exceptions import DeploymentError
5
+ from .client import get_gl_client
6
+ from genlayer_py.types import CalldataEncodable, GenLayerTransaction
7
+ from eth_account.signers.local import LocalAccount
8
+ from typing import List, Any, Type, Optional, Dict, Callable
9
+ import types
10
+
11
+
12
+ @dataclass
13
+ class Contract:
14
+ """
15
+ Class to interact with a contract, its methods
16
+ are implemented dynamically at build time.
17
+ """
18
+
19
+ address: str
20
+ account: Optional[LocalAccount] = None
21
+
22
+ @classmethod
23
+ def from_address_and_schema(
24
+ cls, address: str, schema: Dict[str, Any]
25
+ ) -> "Contract":
26
+ """
27
+ Build the methods from the schema.
28
+ """
29
+ if not isinstance(schema, dict) or "methods" not in schema:
30
+ raise ValueError("Invalid schema: must contain 'methods' field")
31
+
32
+ instance = cls(address=address)
33
+ for method_name, method_info in schema["methods"].items():
34
+ if not isinstance(method_info, dict) or "readonly" not in method_info:
35
+ raise ValueError(
36
+ f"Invalid method info for {method_name}: must contain 'readonly' field"
37
+ )
38
+ setattr(
39
+ instance,
40
+ method_name,
41
+ types.MethodType(
42
+ cls.contract_method_factory(method_name, method_info["readonly"]),
43
+ instance,
44
+ ),
45
+ )
46
+ return instance
47
+
48
+ def connect(self, account: LocalAccount) -> "Contract":
49
+ self.account = account
50
+ return self
51
+
52
+ @staticmethod
53
+ def contract_method_factory(method_name: str, read_only: bool) -> Callable:
54
+ """
55
+ Create a function that interacts with a specific contract method.
56
+ """
57
+
58
+ def read_contract_wrapper(
59
+ self,
60
+ args: Optional[List[CalldataEncodable]] = None,
61
+ ) -> Any:
62
+ """
63
+ Wrapper to the contract read method.
64
+ """
65
+ client = get_gl_client()
66
+ return client.read_contract(
67
+ address=self.address,
68
+ function_name=method_name,
69
+ account=self.account,
70
+ args=args,
71
+ )
72
+
73
+ def write_contract_wrapper(
74
+ self,
75
+ args: Optional[List[CalldataEncodable]] = None,
76
+ value: int = 0,
77
+ consensus_max_rotations: Optional[int] = None,
78
+ leader_only: bool = False,
79
+ wait_interval: Optional[int] = None,
80
+ wait_retries: Optional[int] = None,
81
+ ) -> GenLayerTransaction:
82
+ """
83
+ Wrapper to the contract write method.
84
+ """
85
+ client = get_gl_client()
86
+ tx_hash = client.write_contract(
87
+ address=self.address,
88
+ function_name=method_name,
89
+ account=self.account,
90
+ value=value,
91
+ consensus_max_rotations=consensus_max_rotations,
92
+ leader_only=leader_only,
93
+ args=args,
94
+ )
95
+ extra_args = {}
96
+ if wait_interval is not None:
97
+ extra_args["interval"] = wait_interval
98
+ if wait_retries is not None:
99
+ extra_args["retries"] = wait_retries
100
+ receipt = client.wait_for_transaction_receipt(
101
+ transaction_hash=tx_hash, status="FINALIZED", **extra_args
102
+ )
103
+ return receipt
104
+
105
+ return read_contract_wrapper if read_only else write_contract_wrapper
106
+
107
+
108
+ @dataclass
109
+ class ContractFactory:
110
+ """
111
+ A factory for deploying contracts.
112
+ """
113
+
114
+ contract_name: str
115
+ contract_code: str
116
+
117
+ @classmethod
118
+ def from_artifact(
119
+ cls: Type["ContractFactory"], contract_name: str
120
+ ) -> "ContractFactory":
121
+ """
122
+ Create a ContractFactory instance given the contract name.
123
+ """
124
+ contract_info = find_contract_definition(contract_name)
125
+ if contract_info is None:
126
+ raise ValueError(
127
+ f"Contract {contract_name} not found in the contracts directory"
128
+ )
129
+ return cls(
130
+ contract_name=contract_name, contract_code=contract_info.contract_code
131
+ )
132
+
133
+ def deploy(
134
+ self,
135
+ args: List[Any] = [],
136
+ account: Optional[LocalAccount] = None,
137
+ consensus_max_rotations: Optional[int] = None,
138
+ leader_only: bool = False,
139
+ wait_interval: Optional[int] = None,
140
+ wait_retries: Optional[int] = None,
141
+ ) -> Contract:
142
+ """
143
+ Deploy the contract
144
+ """
145
+ client = get_gl_client()
146
+ try:
147
+ tx_hash = client.deploy_contract(
148
+ code=self.contract_code,
149
+ args=args,
150
+ account=account,
151
+ consensus_max_rotations=consensus_max_rotations,
152
+ leader_only=leader_only,
153
+ )
154
+ extra_args = {}
155
+ if wait_interval is not None:
156
+ extra_args["interval"] = wait_interval
157
+ if wait_retries is not None:
158
+ extra_args["retries"] = wait_retries
159
+ tx_receipt = client.wait_for_transaction_receipt(
160
+ transaction_hash=tx_hash, status="FINALIZED", **extra_args
161
+ )
162
+
163
+ if (
164
+ not tx_receipt
165
+ or "data" not in tx_receipt
166
+ or "contract_address" not in tx_receipt["data"]
167
+ ):
168
+ raise ValueError(
169
+ "Invalid transaction receipt: missing contract address"
170
+ )
171
+
172
+ if tx_execution_failed(tx_receipt):
173
+ raise ValueError(
174
+ f"Deployment transaction finalized with error: {tx_receipt}"
175
+ )
176
+
177
+ contract_address = tx_receipt["data"]["contract_address"]
178
+ schema = client.get_contract_schema(address=contract_address)
179
+ return Contract.from_address_and_schema(
180
+ address=contract_address, schema=schema
181
+ )
182
+ except Exception as e:
183
+ raise DeploymentError(
184
+ f"Failed to deploy contract {self.contract_name}: {str(e)}"
185
+ ) from e
186
+
187
+
188
+ def get_contract_factory(contract_name: str) -> ContractFactory:
189
+ """
190
+ Get a ContractFactory instance for a contract.
191
+ """
192
+ return ContractFactory.from_artifact(contract_name)
@@ -1,17 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: genlayer-test
3
- Version: 0.0.1
4
- Summary: A minimal empty PyPI package for genlayer-test
5
- Author-email: GenLayer Labs Corp <tech@genlayer.com>
6
- License: MIT
7
- Requires-Python: >=3.6
8
- Description-Content-Type: text/markdown
9
-
10
- # GenLayer Test
11
-
12
- This is a minimal empty Python package for genlayer-test.
13
-
14
- ## Installation
15
- ```bash
16
- pip install genlayer-test
17
- ```
@@ -1,4 +0,0 @@
1
- genlayer_test-0.0.1.dist-info/METADATA,sha256=s6JVDtHiKhzy8c3UKBLYxGJLhzgitAsegT_XClxJVWM,373
2
- genlayer_test-0.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
3
- genlayer_test-0.0.1.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
- genlayer_test-0.0.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
-