genlayer-test 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/METADATA +1 -1
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/RECORD +12 -10
- gltest/artifacts/__init__.py +5 -2
- gltest/artifacts/contract.py +87 -12
- gltest/glchain/contract.py +41 -5
- tests/artifact/contracts/duplicate_ic_contract_1.py +22 -0
- tests/artifact/contracts/duplicate_ic_contract_2.py +22 -0
- tests/artifact/test_contract_definition.py +261 -6
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/WHEEL +0 -0
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/entry_points.txt +0 -0
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {genlayer_test-0.1.3.dist-info → genlayer_test-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,23 @@
|
|
1
|
-
genlayer_test-0.
|
1
|
+
genlayer_test-0.2.0.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
|
2
2
|
gltest/__init__.py,sha256=AK_YfRvwlhrOheOelUG8qIRG17on0-nFCF747dopg2w,332
|
3
3
|
gltest/assertions.py,sha256=0dEk0VxcHK4I7GZPHxJmz-2jaA60V499gOSR74rZbfM,1748
|
4
4
|
gltest/exceptions.py,sha256=deJPmrTe5gF33qkkKF2IVJY7lc_knI7Ql3N7jZ8aLZs,510
|
5
5
|
gltest/plugin_config.py,sha256=8Z97RtEJ89OcRbki_oRuBBVct_q56BFmKvthan1y9Y4,840
|
6
6
|
gltest/plugin_hooks.py,sha256=py1rzIR9QSsFOt8SEePPL96e-8DeiPFxvcPZurRlExM,1436
|
7
7
|
gltest/types.py,sha256=BODmwTr2gAUEiO9FjiuTiWwuKvXgo4xZWstQWNUfnlw,156
|
8
|
-
gltest/artifacts/__init__.py,sha256=
|
9
|
-
gltest/artifacts/contract.py,sha256=
|
8
|
+
gltest/artifacts/__init__.py,sha256=qTt3TE19gVNWnQLUlt5aDe4nNvJ2YJ1jzDkMmYIsCG0,194
|
9
|
+
gltest/artifacts/contract.py,sha256=jqdsJD9B4aUTpR4EODHaag5j-IvdWm3Ac4ektbpTxFo,6152
|
10
10
|
gltest/glchain/__init__.py,sha256=X-mEbREoAOe9K4n74C55gCiXH4wItzY5HTJcg3_F3mI,412
|
11
11
|
gltest/glchain/account.py,sha256=ZxYsfbtBXKVC5vV4pko3yyL6lhPljqIb68NgIgvInSc,403
|
12
12
|
gltest/glchain/client.py,sha256=q04LIQy5SCIrYZflZiTapfeQ-AaSTa0w369ehnVbJLM,532
|
13
|
-
gltest/glchain/contract.py,sha256=
|
13
|
+
gltest/glchain/contract.py,sha256=aNS2Wm93BG4kug4iyWembBDaNb2mYCTJOTfDzMPoiRE,10042
|
14
14
|
gltest/helpers/__init__.py,sha256=I7HiTu_H7_hP65zY6Wl02r-5eAMr2eZvqBVmusuQLX4,180
|
15
15
|
gltest/helpers/fixture_snapshot.py,sha256=DWLTsMbTnfhpv0_7_gkJpDKX4hJx-tlruX7x3FWL6UI,2073
|
16
16
|
gltest/helpers/take_snapshot.py,sha256=eXqEKXM2hcox3pLGIcNddobU8zXPQvD-Iwf87eHqW2s,1276
|
17
17
|
gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
|
18
|
-
tests/artifact/test_contract_definition.py,sha256=
|
18
|
+
tests/artifact/test_contract_definition.py,sha256=vr3guuJOvsQorUyv_ahbNvUwfDi6cq_NKAbQMATMp1g,13803
|
19
|
+
tests/artifact/contracts/duplicate_ic_contract_1.py,sha256=bSWsUVjBy5cGtI72cjnkstsMzuUJbB3IG5JjTxOF-dc,500
|
20
|
+
tests/artifact/contracts/duplicate_ic_contract_2.py,sha256=bSWsUVjBy5cGtI72cjnkstsMzuUJbB3IG5JjTxOF-dc,500
|
19
21
|
tests/artifact/contracts/not_ic_contract.py,sha256=hQyGnYiiVceYdLI2WrvcFgPqzy1S4-YMb9FPhiHEGSA,510
|
20
22
|
tests/assertions/test_assertions.py,sha256=qzVrOdOM4xYtIy1sFHVAD_-naDHOequ23tEN0MELh0k,10781
|
21
23
|
tests/examples/contracts/football_prediction_market.py,sha256=kdouFijjeCdIJyaVJlgXcqbBAXecA9_YdhklSsIW-QM,3219
|
@@ -46,8 +48,8 @@ tests/examples/tests/test_user_storage.py,sha256=QEgt2p22LAyzBnBb0YW4BWa_Jasrt15
|
|
46
48
|
tests/examples/tests/test_wizard_of_coin.py,sha256=aUDeV5w0XONMMS71Vzw80lHfcSM0z8RKPJSXAuDwRto,392
|
47
49
|
tests/plugin/conftest.py,sha256=RKdoE5_zcMimeojAoA_GSFI9du4pMzMi1vZ1njtfoAs,28
|
48
50
|
tests/plugin/test_plugin_hooks.py,sha256=FQOrkhoXLinq0sjvoYjr63Oqg-ZVPcNFeUrK4bqrn4E,2020
|
49
|
-
genlayer_test-0.
|
50
|
-
genlayer_test-0.
|
51
|
-
genlayer_test-0.
|
52
|
-
genlayer_test-0.
|
53
|
-
genlayer_test-0.
|
51
|
+
genlayer_test-0.2.0.dist-info/METADATA,sha256=_okLWSmaUrqZVrIZ5fLCJvXFBlhmUoItqEoioj2JV2U,14814
|
52
|
+
genlayer_test-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
53
|
+
genlayer_test-0.2.0.dist-info/entry_points.txt,sha256=rXhrPVq2IhVsd4uWzxzwCTx7jA1KcQIVNxDCUuxq4f8,89
|
54
|
+
genlayer_test-0.2.0.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
|
55
|
+
genlayer_test-0.2.0.dist-info/RECORD,,
|
gltest/artifacts/__init__.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
-
from .contract import
|
1
|
+
from .contract import (
|
2
|
+
find_contract_definition_from_name,
|
3
|
+
find_contract_definition_from_path,
|
4
|
+
)
|
2
5
|
|
3
|
-
__all__ = ["
|
6
|
+
__all__ = ["find_contract_definition_from_name", "find_contract_definition_from_path"]
|
gltest/artifacts/contract.py
CHANGED
@@ -20,6 +20,8 @@ class ContractDefinition:
|
|
20
20
|
|
21
21
|
def search_path_by_class_name(contracts_dir: Path, contract_name: str) -> Path:
|
22
22
|
"""Search for a file by class name in the contracts directory."""
|
23
|
+
matching_files = []
|
24
|
+
|
23
25
|
for file_path in contracts_dir.rglob("*"):
|
24
26
|
if not file_path.suffix in [".gpy", ".py"]:
|
25
27
|
continue
|
@@ -40,10 +42,24 @@ def search_path_by_class_name(contracts_dir: Path, contract_name: str) -> Path:
|
|
40
42
|
and base.value.id == "gl"
|
41
43
|
and base.attr == "Contract"
|
42
44
|
):
|
43
|
-
|
45
|
+
matching_files.append(file_path)
|
46
|
+
break
|
47
|
+
break
|
44
48
|
except Exception as e:
|
45
|
-
raise ValueError(f"Error reading file {file_path}: {e}")
|
46
|
-
|
49
|
+
raise ValueError(f"Error reading file {file_path}: {e}") from e
|
50
|
+
|
51
|
+
if len(matching_files) == 0:
|
52
|
+
raise FileNotFoundError(
|
53
|
+
f"Contract {contract_name} not found at: {contracts_dir}"
|
54
|
+
)
|
55
|
+
if len(matching_files) > 1:
|
56
|
+
file_paths_str = ", ".join(str(f) for f in matching_files)
|
57
|
+
raise ValueError(
|
58
|
+
f"Multiple contracts named '{contract_name}' found in contracts directory. "
|
59
|
+
f"Found in files: {file_paths_str}. Please ensure contract names are unique."
|
60
|
+
) from None
|
61
|
+
|
62
|
+
return matching_files[0]
|
47
63
|
|
48
64
|
|
49
65
|
def compute_contract_code(
|
@@ -71,14 +87,35 @@ def compute_contract_code(
|
|
71
87
|
return buffer.getvalue()
|
72
88
|
|
73
89
|
|
74
|
-
def
|
75
|
-
"""
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
90
|
+
def _extract_contract_name_from_file(file_path: Path) -> str:
|
91
|
+
"""Extract contract name from a file by parsing the AST."""
|
92
|
+
try:
|
93
|
+
with open(file_path, "r") as f:
|
94
|
+
content = f.read()
|
95
|
+
tree = ast.parse(content)
|
96
|
+
|
97
|
+
# Search for class definitions that inherit from gl.Contract
|
98
|
+
for node in ast.walk(tree):
|
99
|
+
if isinstance(node, ast.ClassDef):
|
100
|
+
for base in node.bases:
|
101
|
+
if (
|
102
|
+
isinstance(base, ast.Attribute)
|
103
|
+
and isinstance(base.value, ast.Name)
|
104
|
+
and base.value.id == "gl"
|
105
|
+
and base.attr == "Contract"
|
106
|
+
):
|
107
|
+
return node.name
|
108
|
+
except Exception as e:
|
109
|
+
raise ValueError(f"Error parsing contract file {file_path}: {e}") from e
|
110
|
+
|
111
|
+
raise ValueError(f"No valid contract class found in {file_path}")
|
112
|
+
|
113
|
+
|
114
|
+
def _create_contract_definition(
|
115
|
+
main_file_path: Path, contract_name: str
|
116
|
+
) -> ContractDefinition:
|
117
|
+
"""Create a ContractDefinition from a main file path and contract name."""
|
118
|
+
# Determine if it's a multifile contract
|
82
119
|
main_file_dir = main_file_path.parent
|
83
120
|
runner_file_path = None
|
84
121
|
if main_file_path.name in ["__init__.py", "__init__.gpy"]:
|
@@ -87,9 +124,47 @@ def find_contract_definition(contract_name: str) -> Optional[ContractDefinition]
|
|
87
124
|
if not runner_file_path.exists():
|
88
125
|
# No runner file, so it's a single file contract
|
89
126
|
runner_file_path = None
|
127
|
+
|
128
|
+
# Compute contract code
|
129
|
+
contract_code = compute_contract_code(main_file_path, runner_file_path)
|
130
|
+
|
90
131
|
return ContractDefinition(
|
91
132
|
contract_name=contract_name,
|
92
|
-
contract_code=
|
133
|
+
contract_code=contract_code,
|
93
134
|
main_file_path=main_file_path,
|
94
135
|
runner_file_path=runner_file_path,
|
95
136
|
)
|
137
|
+
|
138
|
+
|
139
|
+
def find_contract_definition_from_name(
|
140
|
+
contract_name: str,
|
141
|
+
) -> Optional[ContractDefinition]:
|
142
|
+
"""
|
143
|
+
Search in the contracts directory for a contract definition.
|
144
|
+
"""
|
145
|
+
contracts_dir = get_contracts_dir()
|
146
|
+
if not contracts_dir.exists():
|
147
|
+
raise FileNotFoundError(f"Contracts directory not found at: {contracts_dir}")
|
148
|
+
|
149
|
+
main_file_path = search_path_by_class_name(contracts_dir, contract_name)
|
150
|
+
return _create_contract_definition(main_file_path, contract_name)
|
151
|
+
|
152
|
+
|
153
|
+
def find_contract_definition_from_path(
|
154
|
+
contract_file_path: Union[str, Path],
|
155
|
+
) -> ContractDefinition:
|
156
|
+
"""
|
157
|
+
Create a ContractDefinition from a given file path relative to the contracts directory.
|
158
|
+
"""
|
159
|
+
contracts_dir = get_contracts_dir()
|
160
|
+
if not contracts_dir.exists():
|
161
|
+
raise FileNotFoundError(f"Contracts directory not found at: {contracts_dir}")
|
162
|
+
|
163
|
+
# Resolve the file path relative to contracts directory
|
164
|
+
main_file_path = contracts_dir / contract_file_path
|
165
|
+
if not main_file_path.exists():
|
166
|
+
raise FileNotFoundError(f"Contract file not found at: {main_file_path}")
|
167
|
+
|
168
|
+
contract_name = _extract_contract_name_from_file(main_file_path)
|
169
|
+
|
170
|
+
return _create_contract_definition(main_file_path, contract_name)
|
gltest/glchain/contract.py
CHANGED
@@ -4,8 +4,12 @@ from eth_typing import (
|
|
4
4
|
)
|
5
5
|
from eth_account.signers.local import LocalAccount
|
6
6
|
from typing import Union
|
7
|
+
from pathlib import Path
|
7
8
|
from dataclasses import dataclass
|
8
|
-
from gltest.artifacts import
|
9
|
+
from gltest.artifacts import (
|
10
|
+
find_contract_definition_from_name,
|
11
|
+
find_contract_definition_from_path,
|
12
|
+
)
|
9
13
|
from gltest.assertions import tx_execution_failed
|
10
14
|
from gltest.exceptions import DeploymentError
|
11
15
|
from .client import get_gl_client
|
@@ -146,13 +150,13 @@ class ContractFactory:
|
|
146
150
|
contract_code: str
|
147
151
|
|
148
152
|
@classmethod
|
149
|
-
def
|
153
|
+
def from_name(
|
150
154
|
cls: Type["ContractFactory"], contract_name: str
|
151
155
|
) -> "ContractFactory":
|
152
156
|
"""
|
153
157
|
Create a ContractFactory instance given the contract name.
|
154
158
|
"""
|
155
|
-
contract_info =
|
159
|
+
contract_info = find_contract_definition_from_name(contract_name)
|
156
160
|
if contract_info is None:
|
157
161
|
raise ValueError(
|
158
162
|
f"Contract {contract_name} not found in the contracts directory"
|
@@ -161,6 +165,19 @@ class ContractFactory:
|
|
161
165
|
contract_name=contract_name, contract_code=contract_info.contract_code
|
162
166
|
)
|
163
167
|
|
168
|
+
@classmethod
|
169
|
+
def from_file_path(
|
170
|
+
cls: Type["ContractFactory"], contract_file_path: Union[str, Path]
|
171
|
+
) -> "ContractFactory":
|
172
|
+
"""
|
173
|
+
Create a ContractFactory instance given the contract file path.
|
174
|
+
"""
|
175
|
+
contract_info = find_contract_definition_from_path(contract_file_path)
|
176
|
+
return cls(
|
177
|
+
contract_name=contract_info.contract_name,
|
178
|
+
contract_code=contract_info.contract_code,
|
179
|
+
)
|
180
|
+
|
164
181
|
def build_contract(
|
165
182
|
self,
|
166
183
|
contract_address: Union[Address, ChecksumAddress],
|
@@ -237,8 +254,27 @@ class ContractFactory:
|
|
237
254
|
) from e
|
238
255
|
|
239
256
|
|
240
|
-
def get_contract_factory(
|
257
|
+
def get_contract_factory(
|
258
|
+
contract_name: Optional[str] = None,
|
259
|
+
contract_file_path: Optional[Union[str, Path]] = None,
|
260
|
+
) -> ContractFactory:
|
241
261
|
"""
|
242
262
|
Get a ContractFactory instance for a contract.
|
263
|
+
|
264
|
+
Args:
|
265
|
+
contract_name: Name of the contract to load from artifacts
|
266
|
+
contract_file_path: Path to the contract file to load directly
|
267
|
+
|
268
|
+
Note: Exactly one of contract_name or contract_file_path must be provided.
|
243
269
|
"""
|
244
|
-
|
270
|
+
if contract_name is not None and contract_file_path is not None:
|
271
|
+
raise ValueError(
|
272
|
+
"Only one of contract_name or contract_file_path should be provided"
|
273
|
+
)
|
274
|
+
|
275
|
+
if contract_name is None and contract_file_path is None:
|
276
|
+
raise ValueError("Either contract_name or contract_file_path must be provided")
|
277
|
+
|
278
|
+
if contract_name is not None:
|
279
|
+
return ContractFactory.from_name(contract_name)
|
280
|
+
return ContractFactory.from_file_path(contract_file_path)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
# contract class
|
7
|
+
class DuplicateContract(gl.Contract):
|
8
|
+
storage: str
|
9
|
+
|
10
|
+
# constructor
|
11
|
+
def __init__(self, initial_storage: str):
|
12
|
+
self.storage = initial_storage
|
13
|
+
|
14
|
+
# read methods must be annotated with view
|
15
|
+
@gl.public.view
|
16
|
+
def get_storage(self) -> str:
|
17
|
+
return self.storage
|
18
|
+
|
19
|
+
# write method
|
20
|
+
@gl.public.write
|
21
|
+
def update_storage(self, new_storage: str) -> None:
|
22
|
+
self.storage = new_storage
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
# contract class
|
7
|
+
class DuplicateContract(gl.Contract):
|
8
|
+
storage: str
|
9
|
+
|
10
|
+
# constructor
|
11
|
+
def __init__(self, initial_storage: str):
|
12
|
+
self.storage = initial_storage
|
13
|
+
|
14
|
+
# read methods must be annotated with view
|
15
|
+
@gl.public.view
|
16
|
+
def get_storage(self) -> str:
|
17
|
+
return self.storage
|
18
|
+
|
19
|
+
# write method
|
20
|
+
@gl.public.write
|
21
|
+
def update_storage(self, new_storage: str) -> None:
|
22
|
+
self.storage = new_storage
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import pytest
|
2
2
|
from gltest.artifacts.contract import (
|
3
|
-
|
3
|
+
find_contract_definition_from_name,
|
4
|
+
find_contract_definition_from_path,
|
4
5
|
compute_contract_code,
|
5
6
|
)
|
6
7
|
from gltest.plugin_config import set_contracts_dir
|
@@ -8,8 +9,15 @@ from pathlib import Path
|
|
8
9
|
|
9
10
|
|
10
11
|
def test_single_file():
|
12
|
+
"""
|
13
|
+
Test finding a contract definition by name for a single-file contract.
|
14
|
+
|
15
|
+
Verifies that the function correctly identifies and loads a contract
|
16
|
+
from a single Python file, extracting the contract name and computing
|
17
|
+
the contract code without any additional runner files.
|
18
|
+
"""
|
11
19
|
set_contracts_dir(".")
|
12
|
-
contract_definition =
|
20
|
+
contract_definition = find_contract_definition_from_name("PredictionMarket")
|
13
21
|
|
14
22
|
assert contract_definition.contract_name == "PredictionMarket"
|
15
23
|
|
@@ -28,8 +36,15 @@ def test_single_file():
|
|
28
36
|
|
29
37
|
|
30
38
|
def test_multiple_files():
|
39
|
+
"""
|
40
|
+
Test finding a contract definition by name for a multi-file contract.
|
41
|
+
|
42
|
+
Verifies that the function correctly identifies and loads a contract
|
43
|
+
from a multi-file structure with __init__.py and runner.json,
|
44
|
+
properly packaging all files into a ZIP archive for deployment.
|
45
|
+
"""
|
31
46
|
set_contracts_dir(".")
|
32
|
-
contract_definition =
|
47
|
+
contract_definition = find_contract_definition_from_name("MultiFileContract")
|
33
48
|
|
34
49
|
assert contract_definition.contract_name == "MultiFileContract"
|
35
50
|
|
@@ -47,8 +62,15 @@ def test_multiple_files():
|
|
47
62
|
|
48
63
|
|
49
64
|
def test_single_file_legacy():
|
65
|
+
"""
|
66
|
+
Test finding a contract definition by name for a legacy single-file contract.
|
67
|
+
|
68
|
+
Verifies that the function correctly handles legacy .gpy files,
|
69
|
+
maintaining backward compatibility with older contract formats
|
70
|
+
while extracting contract name and computing contract code.
|
71
|
+
"""
|
50
72
|
set_contracts_dir(".")
|
51
|
-
contract_definition =
|
73
|
+
contract_definition = find_contract_definition_from_name("StorageLegacy")
|
52
74
|
|
53
75
|
# Assert complete contract definition
|
54
76
|
assert contract_definition.contract_name == "StorageLegacy"
|
@@ -66,8 +88,15 @@ def test_single_file_legacy():
|
|
66
88
|
|
67
89
|
|
68
90
|
def test_multiple_files_legacy():
|
91
|
+
"""
|
92
|
+
Test finding a contract definition by name for a legacy multi-file contract.
|
93
|
+
|
94
|
+
Verifies that the function correctly handles legacy multi-file contracts
|
95
|
+
with .gpy extension and runner.json, ensuring proper packaging and
|
96
|
+
backward compatibility with older contract structures.
|
97
|
+
"""
|
69
98
|
set_contracts_dir(".")
|
70
|
-
contract_definition =
|
99
|
+
contract_definition = find_contract_definition_from_name("MultiFileContractLegacy")
|
71
100
|
|
72
101
|
# Assert complete contract definition
|
73
102
|
assert contract_definition.contract_name == "MultiFileContractLegacy"
|
@@ -86,7 +115,233 @@ def test_multiple_files_legacy():
|
|
86
115
|
|
87
116
|
|
88
117
|
def test_class_is_not_intelligent_contract():
|
118
|
+
"""
|
119
|
+
Test error handling when searching for a non-existent contract by name.
|
120
|
+
|
121
|
+
Verifies that the function raises FileNotFoundError when attempting
|
122
|
+
to find a contract that doesn't exist in the contracts directory,
|
123
|
+
ensuring proper error handling for invalid contract names.
|
124
|
+
"""
|
89
125
|
set_contracts_dir(".")
|
90
126
|
|
91
127
|
with pytest.raises(FileNotFoundError):
|
92
|
-
_ =
|
128
|
+
_ = find_contract_definition_from_name("NotICContract")
|
129
|
+
|
130
|
+
|
131
|
+
def test_find_from_path_single_file():
|
132
|
+
"""
|
133
|
+
Test finding a contract definition by file path for a single-file contract.
|
134
|
+
|
135
|
+
Verifies that the function correctly loads a contract when given a relative
|
136
|
+
path to a single Python file, extracting the contract name via AST parsing
|
137
|
+
and computing the contract code without additional runner files.
|
138
|
+
"""
|
139
|
+
set_contracts_dir(".")
|
140
|
+
contract_definition = find_contract_definition_from_path(
|
141
|
+
"examples/contracts/football_prediction_market.py"
|
142
|
+
)
|
143
|
+
|
144
|
+
assert contract_definition.contract_name == "PredictionMarket"
|
145
|
+
|
146
|
+
# Assert complete contract definition
|
147
|
+
expected_main_file_path = Path("examples/contracts/football_prediction_market.py")
|
148
|
+
expected_runner_file_path = None
|
149
|
+
contract_code = compute_contract_code(
|
150
|
+
expected_main_file_path, expected_runner_file_path
|
151
|
+
)
|
152
|
+
assert contract_definition.contract_code == contract_code
|
153
|
+
assert (
|
154
|
+
str(contract_definition.main_file_path)
|
155
|
+
== "examples/contracts/football_prediction_market.py"
|
156
|
+
)
|
157
|
+
assert contract_definition.runner_file_path is None
|
158
|
+
|
159
|
+
|
160
|
+
def test_find_from_path_multiple_files():
|
161
|
+
"""
|
162
|
+
Test finding a contract definition by file path for a multi-file contract.
|
163
|
+
|
164
|
+
Verifies that the function correctly loads a contract when given a relative
|
165
|
+
path to __init__.py in a multi-file structure, automatically detecting
|
166
|
+
the associated runner.json and packaging all files appropriately.
|
167
|
+
"""
|
168
|
+
set_contracts_dir(".")
|
169
|
+
contract_definition = find_contract_definition_from_path(
|
170
|
+
"examples/contracts/multi_file_contract/__init__.py"
|
171
|
+
)
|
172
|
+
|
173
|
+
assert contract_definition.contract_name == "MultiFileContract"
|
174
|
+
|
175
|
+
# Assert complete contract definition
|
176
|
+
expected_main_file_path = Path("examples/contracts/multi_file_contract/__init__.py")
|
177
|
+
expected_runner_file_path = Path(
|
178
|
+
"examples/contracts/multi_file_contract/runner.json"
|
179
|
+
)
|
180
|
+
assert contract_definition.main_file_path == expected_main_file_path
|
181
|
+
assert contract_definition.runner_file_path == expected_runner_file_path
|
182
|
+
contract_code = compute_contract_code(
|
183
|
+
expected_main_file_path, expected_runner_file_path
|
184
|
+
)
|
185
|
+
assert contract_definition.contract_code == contract_code
|
186
|
+
|
187
|
+
|
188
|
+
def test_find_from_path_single_file_legacy():
|
189
|
+
"""
|
190
|
+
Test finding a contract definition by file path for a legacy single-file contract.
|
191
|
+
|
192
|
+
Verifies that the function correctly handles legacy .gpy files when accessed
|
193
|
+
by file path, maintaining backward compatibility while extracting contract
|
194
|
+
name via AST parsing and computing appropriate contract code.
|
195
|
+
"""
|
196
|
+
set_contracts_dir(".")
|
197
|
+
contract_definition = find_contract_definition_from_path(
|
198
|
+
"examples/contracts/storage_legacy.gpy"
|
199
|
+
)
|
200
|
+
|
201
|
+
# Assert complete contract definition
|
202
|
+
assert contract_definition.contract_name == "StorageLegacy"
|
203
|
+
expected_main_file_path = Path("examples/contracts/storage_legacy.gpy")
|
204
|
+
expected_runner_file_path = None
|
205
|
+
contract_code = compute_contract_code(
|
206
|
+
expected_main_file_path, expected_runner_file_path
|
207
|
+
)
|
208
|
+
assert contract_definition.contract_code == contract_code
|
209
|
+
assert (
|
210
|
+
str(contract_definition.main_file_path)
|
211
|
+
== "examples/contracts/storage_legacy.gpy"
|
212
|
+
)
|
213
|
+
assert contract_definition.runner_file_path is None
|
214
|
+
|
215
|
+
|
216
|
+
def test_find_from_path_multiple_files_legacy():
|
217
|
+
"""
|
218
|
+
Test finding a contract definition by file path for a legacy multi-file contract.
|
219
|
+
|
220
|
+
Verifies that the function correctly handles legacy multi-file contracts
|
221
|
+
with .gpy extension when accessed by file path, properly detecting
|
222
|
+
runner.json and maintaining backward compatibility with older structures.
|
223
|
+
"""
|
224
|
+
set_contracts_dir(".")
|
225
|
+
contract_definition = find_contract_definition_from_path(
|
226
|
+
"examples/contracts/multi_file_contract_legacy/__init__.gpy"
|
227
|
+
)
|
228
|
+
|
229
|
+
# Assert complete contract definition
|
230
|
+
assert contract_definition.contract_name == "MultiFileContractLegacy"
|
231
|
+
expected_main_file_path = Path(
|
232
|
+
"examples/contracts/multi_file_contract_legacy/__init__.gpy"
|
233
|
+
)
|
234
|
+
expected_runner_file_path = Path(
|
235
|
+
"examples/contracts/multi_file_contract_legacy/runner.json"
|
236
|
+
)
|
237
|
+
assert contract_definition.main_file_path == expected_main_file_path
|
238
|
+
assert contract_definition.runner_file_path == expected_runner_file_path
|
239
|
+
contract_code = compute_contract_code(
|
240
|
+
expected_main_file_path, expected_runner_file_path
|
241
|
+
)
|
242
|
+
assert contract_definition.contract_code == contract_code
|
243
|
+
|
244
|
+
|
245
|
+
def test_find_from_path_file_not_found():
|
246
|
+
"""
|
247
|
+
Test error handling when the specified contract file doesn't exist.
|
248
|
+
|
249
|
+
Verifies that the function raises FileNotFoundError with appropriate
|
250
|
+
error message when attempting to load a contract from a non-existent
|
251
|
+
file path relative to the contracts directory.
|
252
|
+
"""
|
253
|
+
set_contracts_dir(".")
|
254
|
+
|
255
|
+
with pytest.raises(FileNotFoundError, match="Contract file not found at:"):
|
256
|
+
_ = find_contract_definition_from_path("nonexistent/contract.py")
|
257
|
+
|
258
|
+
|
259
|
+
def test_find_from_path_contracts_dir_not_found():
|
260
|
+
"""
|
261
|
+
Test error handling when the contracts directory doesn't exist.
|
262
|
+
|
263
|
+
Verifies that the function raises FileNotFoundError with appropriate
|
264
|
+
error message when the configured contracts directory is invalid,
|
265
|
+
ensuring proper validation before attempting file operations.
|
266
|
+
"""
|
267
|
+
set_contracts_dir("nonexistent_directory")
|
268
|
+
|
269
|
+
with pytest.raises(FileNotFoundError, match="Contracts directory not found at:"):
|
270
|
+
_ = find_contract_definition_from_path("some/contract.py")
|
271
|
+
|
272
|
+
|
273
|
+
def test_find_from_path_no_valid_contract_class():
|
274
|
+
"""
|
275
|
+
Test error handling when a file exists but contains no valid contract class.
|
276
|
+
|
277
|
+
Verifies that the function raises ValueError with appropriate error message
|
278
|
+
when attempting to load a file that exists but doesn't contain a class
|
279
|
+
that inherits from gl.Contract, ensuring proper AST parsing validation.
|
280
|
+
"""
|
281
|
+
set_contracts_dir(".")
|
282
|
+
|
283
|
+
with pytest.raises(ValueError, match="No valid contract class found in"):
|
284
|
+
_ = find_contract_definition_from_path("artifact/contracts/not_ic_contract.py")
|
285
|
+
|
286
|
+
|
287
|
+
def test_multiple_contracts_same_name():
|
288
|
+
"""
|
289
|
+
Test error handling when multiple contracts with the same name exist.
|
290
|
+
|
291
|
+
Verifies that the function raises ValueError with appropriate error message
|
292
|
+
when multiple files contain contracts with the same name, listing all
|
293
|
+
duplicate file locations and providing guidance for resolution.
|
294
|
+
"""
|
295
|
+
set_contracts_dir(".")
|
296
|
+
|
297
|
+
with pytest.raises(
|
298
|
+
ValueError,
|
299
|
+
match=r"Multiple contracts named 'DuplicateContract' found in contracts directory\. Found in files: .+\. Please ensure contract names are unique\.",
|
300
|
+
):
|
301
|
+
_ = find_contract_definition_from_name("DuplicateContract")
|
302
|
+
|
303
|
+
|
304
|
+
def test_duplicate_contract_error_message_format():
|
305
|
+
"""
|
306
|
+
Test that the duplicate contract error message contains all expected elements.
|
307
|
+
|
308
|
+
Verifies that when multiple contracts with the same name are found, the error
|
309
|
+
message includes the contract name, mentions "contracts directory", lists
|
310
|
+
file paths, and provides clear guidance about ensuring uniqueness.
|
311
|
+
"""
|
312
|
+
set_contracts_dir(".")
|
313
|
+
|
314
|
+
try:
|
315
|
+
_ = find_contract_definition_from_name("DuplicateContract")
|
316
|
+
pytest.fail("Expected ValueError for duplicate contracts")
|
317
|
+
except ValueError as e:
|
318
|
+
error_message = str(e)
|
319
|
+
# Verify error message contains key components
|
320
|
+
assert "Multiple contracts named 'DuplicateContract' found" in error_message
|
321
|
+
assert "contracts directory" in error_message
|
322
|
+
assert "Found in files:" in error_message
|
323
|
+
assert "Please ensure contract names are unique" in error_message
|
324
|
+
# Verify that multiple file paths are mentioned (comma-separated)
|
325
|
+
assert (
|
326
|
+
"," in error_message
|
327
|
+
or len(error_message.split("Found in files: ")[1].split(".")[0]) > 0
|
328
|
+
)
|
329
|
+
except Exception as e:
|
330
|
+
pytest.fail(f"Expected ValueError but got {type(e).__name__}: {e}")
|
331
|
+
|
332
|
+
|
333
|
+
def test_single_contract_still_works_with_duplicate_detection():
|
334
|
+
"""
|
335
|
+
Test that normal single contract loading still works after duplicate detection changes.
|
336
|
+
|
337
|
+
Verifies that the enhanced search_path_by_class_name function doesn't break
|
338
|
+
the normal case where only one contract with a given name exists, ensuring
|
339
|
+
backward compatibility with existing functionality.
|
340
|
+
"""
|
341
|
+
set_contracts_dir(".")
|
342
|
+
|
343
|
+
# This should work normally - no duplicates expected for PredictionMarket
|
344
|
+
contract_definition = find_contract_definition_from_name("PredictionMarket")
|
345
|
+
assert contract_definition.contract_name == "PredictionMarket"
|
346
|
+
assert contract_definition.main_file_path is not None
|
347
|
+
assert "football_prediction_market.py" in str(contract_definition.main_file_path)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|