genlayer-test 0.10.1__py3-none-any.whl → 0.11.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genlayer-test
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: GenLayer Testing Suite
5
5
  Author: GenLayer
6
6
  License-Expression: MIT
@@ -24,8 +24,8 @@ Dynamic: license-file
24
24
  # GenLayer Testing Suite
25
25
 
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/license/mit/)
27
- [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/VpfmXEMN66)
28
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/yeagerai.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
27
+ [![Discord](https://dcbadge.vercel.app/api/server/8Jm4v89VAu?compact=true&style=flat)](https://discord.gg/qjCU4AWnKE)
28
+ [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/genlayerlabs.svg?style=social&label=Follow%20%40GenLayer)](https://x.com/GenLayer)
29
29
  [![PyPI version](https://badge.fury.io/py/genlayer-test.svg)](https://badge.fury.io/py/genlayer-test)
30
30
  [![Documentation](https://img.shields.io/badge/docs-genlayer-blue)](https://docs.genlayer.com/api-references/genlayer-test)
31
31
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -107,7 +107,7 @@ $ pip install genlayer-test
107
107
 
108
108
  2. Install from source:
109
109
  ```bash
110
- $ git clone https://github.com/yeagerai/genlayer-testing-suite
110
+ $ git clone https://github.com/genlayerlabs/genlayer-testing-suite
111
111
  $ cd genlayer-testing-suite
112
112
  $ pip install -e .
113
113
  ```
@@ -622,6 +622,205 @@ The `.analyze()` method helps you:
622
622
  - Benchmark performance across multiple runs
623
623
 
624
624
 
625
+ ### Mock Web Responses
626
+
627
+ The Mock Web Response system allows you to simulate HTTP responses for web requests made by intelligent contracts using GenLayer's web methods (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.). This feature enables deterministic testing of contracts that interact with external web services without making actual HTTP calls.
628
+
629
+ #### Basic Example
630
+
631
+ Here's a simple example of mocking a web API response:
632
+
633
+ ```python
634
+ from gltest import get_contract_factory, get_validator_factory
635
+ from gltest.types import MockedWebResponse
636
+ import json
637
+
638
+ def test_simple_web_mock():
639
+ # Define mock web responses
640
+ mock_web_response: MockedWebResponse = {
641
+ "nondet_web_request": {
642
+ "https://api.example.com/price": {
643
+ "method": "GET",
644
+ "status": 200,
645
+ "body": json.dumps({"price": 100.50})
646
+ }
647
+ }
648
+ }
649
+
650
+ # Create validators with mock web responses
651
+ validator_factory = get_validator_factory()
652
+ validators = validator_factory.batch_create_mock_validators(
653
+ count=5,
654
+ mock_web_response=mock_web_response
655
+ )
656
+
657
+ # Use validators in transaction context
658
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
659
+
660
+ # Deploy and test contract
661
+ factory = get_contract_factory("PriceOracle")
662
+ contract = factory.deploy(transaction_context=transaction_context)
663
+
664
+ # Contract's web requests will receive the mocked response
665
+ result = contract.update_price().transact(transaction_context=transaction_context)
666
+ ```
667
+
668
+ #### Supported HTTP Methods
669
+
670
+ Mock web responses support all HTTP methods including GET, POST, PUT, DELETE, PATCH, etc.:
671
+
672
+ ```python
673
+ mock_web_response: MockedWebResponse = {
674
+ "nondet_web_request": {
675
+ # GET request
676
+ "https://api.example.com/users/123": {
677
+ "method": "GET",
678
+ "status": 200,
679
+ "body": '{"id": 123, "name": "Alice"}'
680
+ },
681
+ # POST request
682
+ "https://api.example.com/users": {
683
+ "method": "POST",
684
+ "status": 201,
685
+ "body": '{"id": 124, "name": "Bob", "created": true}'
686
+ },
687
+ # DELETE request
688
+ "https://api.example.com/users/123": {
689
+ "method": "DELETE",
690
+ "status": 204,
691
+ "body": ""
692
+ },
693
+ # PUT request
694
+ "https://api.example.com/users/123": {
695
+ "method": "PUT",
696
+ "status": 200,
697
+ "body": '{"id": 123, "name": "Alice Updated"}'
698
+ },
699
+ # Error response
700
+ "https://api.example.com/error": {
701
+ "method": "GET",
702
+ "status": 500,
703
+ "body": "Internal Server Error"
704
+ }
705
+ }
706
+ }
707
+ ```
708
+
709
+ #### How It Works
710
+
711
+ When a contract calls any web method (`gl.nondet.web.get()`, `gl.nondet.web.post()`, etc.):
712
+ 1. The mock system checks if the URL exists in the mock configuration
713
+ 2. If found, it returns the mocked response with the specified status and body
714
+ 3. If not found, the actual web request would be made (or fail if network access is disabled)
715
+
716
+ #### Complete Example: Twitter/X Username Storage
717
+
718
+ Here's a real-world example showing how to mock Twitter/X API responses:
719
+
720
+ ```python
721
+ # test_x_username_storage.py
722
+ from gltest import get_contract_factory, get_validator_factory
723
+ from gltest.assertions import tx_execution_succeeded
724
+ from gltest.types import MockedWebResponse
725
+ import json
726
+ import urllib.parse
727
+
728
+ def test_x_username_storage():
729
+ # Helper to build URL with query parameters
730
+ def get_username_url(username: str) -> str:
731
+ params = {"user.fields": "public_metrics,verified"}
732
+ return f"https://domain.com/api/twitter/users/by/username/{username}?{urllib.parse.urlencode(params)}"
733
+
734
+ # Define mock responses for different usernames
735
+ mock_web_response: MockedWebResponse = {
736
+ "nondet_web_request": {
737
+ get_username_url("user_a"): {
738
+ "method": "GET",
739
+ "status": 200,
740
+ "body": json.dumps({"username": "user_a", "verified": True})
741
+ },
742
+ get_username_url("user_b"): {
743
+ "method": "GET",
744
+ "status": 200,
745
+ "body": json.dumps({"username": "user_b", "verified": False})
746
+ }
747
+ }
748
+ }
749
+
750
+ # Create validators with mock web responses
751
+ validator_factory = get_validator_factory()
752
+ validators = validator_factory.batch_create_mock_validators(
753
+ count=5,
754
+ mock_web_response=mock_web_response
755
+ )
756
+ transaction_context = {"validators": [v.to_dict() for v in validators]}
757
+
758
+ # Deploy and test contract
759
+ factory = get_contract_factory("XUsernameStorage")
760
+ contract = factory.deploy(transaction_context=transaction_context)
761
+
762
+ # Test updating username - will use mocked response
763
+ tx_receipt = contract.update_username(args=["user_a"]).transact(
764
+ transaction_context=transaction_context
765
+ )
766
+ assert tx_execution_succeeded(tx_receipt)
767
+
768
+ # Verify the username was stored
769
+ username = contract.get_username().call(transaction_context=transaction_context)
770
+ assert username == "user_a"
771
+ ```
772
+
773
+ #### Combining Mock LLM and Web Responses
774
+
775
+ You can combine both mock LLM responses and mock web responses in the same test:
776
+
777
+ ```python
778
+ def test_combined_mocks():
779
+ # Define both mock types
780
+ mock_llm_response = {
781
+ "eq_principle_prompt_comparative": {
782
+ "values match": True
783
+ }
784
+ }
785
+
786
+ mock_web_response: MockedWebResponse = {
787
+ "nondet_web_request": {
788
+ "https://api.example.com/data": {
789
+ "method": "GET",
790
+ "status": 200,
791
+ "body": '{"value": 42}'
792
+ }
793
+ }
794
+ }
795
+
796
+ # Create validators with both mock types
797
+ validator_factory = get_validator_factory()
798
+ validators = validator_factory.batch_create_mock_validators(
799
+ count=5,
800
+ mock_llm_response=mock_llm_response,
801
+ mock_web_response=mock_web_response
802
+ )
803
+
804
+ # Use in your tests...
805
+ ```
806
+
807
+ #### Best Practices
808
+
809
+ 1. **URL Matching**: URLs must match exactly, including query parameters
810
+ 2. **Response Body**: Always provide the body as a string (use `json.dumps()` for JSON data)
811
+ 3. **Status Codes**: Use realistic HTTP status codes (200, 404, 500, etc.)
812
+ 4. **Method Matching**: Specify the correct HTTP method that your contract uses
813
+ 5. **Error Testing**: Mock error responses to test error handling paths
814
+ 6. **Deterministic Tests**: Mock web responses ensure tests don't depend on external services
815
+
816
+ #### Notes
817
+
818
+ - Mock web responses are only available when using mock validators
819
+ - URL matching is exact - the full URL including query parameters must match
820
+ - The method field should match the HTTP method used by the contract
821
+ - Useful for testing contracts that interact with external APIs without network dependencies
822
+ - All standard HTTP methods are supported (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
823
+
625
824
  ### Custom Transaction Context
626
825
 
627
826
  The GenLayer Testing Suite allows you to customize the transaction execution environment by providing a `transaction_context` parameter with custom validators and GenVM datetime settings.
@@ -1001,8 +1200,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
1001
1200
  ## 💬 Support
1002
1201
 
1003
1202
  - [Documentation](https://docs.genlayer.com/api-references/genlayer-test)
1004
- - [Discord Community](https://discord.gg/VpfmXEMN66)
1005
- - [GitHub Issues](https://github.com/yeagerai/genlayer-testing-suite/issues)
1203
+ - [Discord Community](https://discord.gg/qjCU4AWnKE)
1204
+ - [GitHub Issues](https://github.com/genlayerlabs/genlayer-testing-suite/issues)
1006
1205
  - [Twitter](https://x.com/GenLayer)
1007
1206
 
1008
1207
 
@@ -1,4 +1,4 @@
1
- genlayer_test-0.10.1.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
1
+ genlayer_test-0.11.0.dist-info/licenses/LICENSE,sha256=qeyU7v-TUbXruF0KGiAPMvgxsqxMq1ObA47C0gR5kWI,1070
2
2
  gltest/__init__.py,sha256=49112x2CLdYwvCbBZ1laJmMk0NQ7S3u5YUbxPefqhrk,454
3
3
  gltest/accounts.py,sha256=HUmWguJMolggQaZNRPw-LGlRlQCjLLdUanKRowMv6pI,812
4
4
  gltest/assertions.py,sha256=0dEk0VxcHK4I7GZPHxJmz-2jaA60V499gOSR74rZbfM,1748
@@ -6,7 +6,7 @@ gltest/clients.py,sha256=1dX6wmG3QCevQRLbSaFlHymZSb-sJ5aYwet1IoX2nbA,1554
6
6
  gltest/exceptions.py,sha256=deJPmrTe5gF33qkkKF2IVJY7lc_knI7Ql3N7jZ8aLZs,510
7
7
  gltest/fixtures.py,sha256=omVjLh1kXXUyL7Oo8zzdooJBbSX-Qk-Aa-prp5MqvFc,833
8
8
  gltest/logging.py,sha256=jAkHsuMm-AEx1Xu1srU6W-0YzTwXJB48mCK-OVzAiN4,342
9
- gltest/types.py,sha256=H32fHrU5aFMaPHXgEWcHAmLWOZ9pBFVp8PK_ncpVOgM,940
9
+ gltest/types.py,sha256=LLkKjkFsKG0AXpqt-qk70YhOm18AGkqmihamG_9kjEc,1305
10
10
  gltest/utils.py,sha256=-gHhjrS7i_GhDG3sKOq2qsTtYBt4HHgXHEXh-3RB_rI,573
11
11
  gltest/artifacts/__init__.py,sha256=qTt3TE19gVNWnQLUlt5aDe4nNvJ2YJ1jzDkMmYIsCG0,194
12
12
  gltest/artifacts/contract.py,sha256=KChpmfjZod_0dVB8y-dvWz6IVm7QlIJsgG2ArtvVDaU,6457
@@ -21,18 +21,18 @@ gltest/helpers/__init__.py,sha256=I7HiTu_H7_hP65zY6Wl02r-5eAMr2eZvqBVmusuQLX4,18
21
21
  gltest/helpers/fixture_snapshot.py,sha256=bMgvlEVQBGIQzj7NOyosXWlphI1H2C1o75Zo0C-kGfQ,1931
22
22
  gltest/helpers/take_snapshot.py,sha256=-QkaBvFG4ZsNKv_nCSEsy5Ze1pICOHxVhReSeQmZUlY,1276
23
23
  gltest/validators/__init__.py,sha256=AXboXORF5a8MVtG7jWMT1fJcwGXNzcX6txXQstwX2EU,152
24
- gltest/validators/validator_factory.py,sha256=fpb-YyAKuWo4-pXBjrZ_TApYLsm6HHa6kGpbFByRucs,3886
24
+ gltest/validators/validator_factory.py,sha256=Pu7c5XMc7HgPFIiWet0ybcgtW4T29d0lsgPrpj4tHtk,4957
25
25
  gltest_cli/logging.py,sha256=WXVhfq9vT6FtV_jxDqGEGia1ZWSIUKAfmWRnZd_gWQk,1266
26
26
  gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
27
27
  gltest_cli/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  gltest_cli/config/constants.py,sha256=z7njbU8WXYhTnp1NYrW4YI2jN-p114BP2UDSYF0qAJE,571
29
29
  gltest_cli/config/general.py,sha256=ezpoGsT8grO9zQH6RugV14b1GzeFt-htYToHQBJhNvY,186
30
- gltest_cli/config/plugin.py,sha256=rySUyo7OSG1SOiUx2Xrxiv-SfjaxJSWpT5KKKNRk4Mk,6719
30
+ gltest_cli/config/plugin.py,sha256=jxmdSBq7A1UPpxL4zolhDX53T8Eo4M8xluAsKx_6MTY,6774
31
31
  gltest_cli/config/pytest_context.py,sha256=Ze8JSkrwMTCE8jIhpzU_71CEXg92SiEPvSgNTp-gbS4,243
32
32
  gltest_cli/config/types.py,sha256=nGKm2qTwo99M8DbGchj14IdIQ4lcNTDODZFHXdsi5rY,9506
33
33
  gltest_cli/config/user.py,sha256=JeclpIVv4BT5COMW2xrEbTspI6e1RAYIOvd8ruPc3gM,12887
34
- genlayer_test-0.10.1.dist-info/METADATA,sha256=TAZaEuDPIKWu3kDlIfASJ_M0BGPsFFV1TLMKPivnm68,36341
35
- genlayer_test-0.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- genlayer_test-0.10.1.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
37
- genlayer_test-0.10.1.dist-info/top_level.txt,sha256=GSdrnQbiLcZssmtCpbDgBTygsc8Bt_TPeYjwm0FmpdA,18
38
- genlayer_test-0.10.1.dist-info/RECORD,,
34
+ genlayer_test-0.11.0.dist-info/METADATA,sha256=UugYMl8Xl5qnR6yhNAhR2ZhkpnAfGzeskxnDldfr7Io,43279
35
+ genlayer_test-0.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ genlayer_test-0.11.0.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
37
+ genlayer_test-0.11.0.dist-info/top_level.txt,sha256=GSdrnQbiLcZssmtCpbDgBTygsc8Bt_TPeYjwm0FmpdA,18
38
+ genlayer_test-0.11.0.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 YeagerAI
3
+ Copyright (c) 2024 GenLayer Labs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
gltest/types.py CHANGED
@@ -20,6 +20,20 @@ class MockedLLMResponse(TypedDict):
20
20
  eq_principle_prompt_non_comparative: Dict[str, bool]
21
21
 
22
22
 
23
+ class MockedWebResponseData(TypedDict):
24
+ """Mocked web response data with method for matching"""
25
+
26
+ method: str # GET, POST, PUT, DELETE, etc.
27
+ status: int # status code of the response
28
+ body: str # body of the response
29
+
30
+
31
+ class MockedWebResponse(TypedDict):
32
+ """Maps urls to responses"""
33
+
34
+ nondet_web_request: Dict[str, MockedWebResponseData]
35
+
36
+
23
37
  class ValidatorConfig(TypedDict):
24
38
  """Validator information."""
25
39
 
@@ -1,4 +1,4 @@
1
- from gltest.types import MockedLLMResponse
1
+ from gltest.types import MockedLLMResponse, MockedWebResponse
2
2
  from dataclasses import dataclass
3
3
  from typing import Dict, Any, Optional, List
4
4
  from copy import deepcopy
@@ -16,6 +16,7 @@ class Validator:
16
16
  # Mock configuration
17
17
  mock_enabled: bool
18
18
  mock_llm_response: Optional[MockedLLMResponse]
19
+ mock_web_response: Optional[MockedWebResponse]
19
20
 
20
21
  def to_dict(self) -> Dict[str, Any]:
21
22
  normal_config = {
@@ -29,21 +30,27 @@ class Validator:
29
30
  if not self.mock_enabled:
30
31
  return normal_config
31
32
 
32
- mock = self.mock_llm_response or {}
33
- mock_config = {
34
- "response": mock.get("nondet_exec_prompt", {}),
35
- "eq_principle_prompt_comparative": mock.get(
33
+ # Mock llm response
34
+ mock_llm = self.mock_llm_response or {}
35
+ mock_llm_config = {
36
+ "response": mock_llm.get("nondet_exec_prompt", {}),
37
+ "eq_principle_prompt_comparative": mock_llm.get(
36
38
  "eq_principle_prompt_comparative", {}
37
39
  ),
38
- "eq_principle_prompt_non_comparative": mock.get(
40
+ "eq_principle_prompt_non_comparative": mock_llm.get(
39
41
  "eq_principle_prompt_non_comparative", {}
40
42
  ),
41
43
  }
44
+ mock_web = self.mock_web_response or {}
45
+ mock_web_config = {
46
+ "nondet_web_request": mock_web.get("nondet_web_request", {}),
47
+ }
42
48
  return {
43
49
  **normal_config,
44
50
  "plugin_config": {
45
51
  **self.plugin_config,
46
- "mock_response": mock_config,
52
+ "mock_response": mock_llm_config,
53
+ "mock_web_response": mock_web_config,
47
54
  },
48
55
  }
49
56
 
@@ -60,6 +67,7 @@ class Validator:
60
67
  plugin_config=deepcopy(self.plugin_config),
61
68
  mock_enabled=self.mock_enabled,
62
69
  mock_llm_response=deepcopy(self.mock_llm_response),
70
+ mock_web_response=deepcopy(self.mock_web_response),
63
71
  )
64
72
 
65
73
 
@@ -85,6 +93,7 @@ class ValidatorFactory:
85
93
  plugin_config=deepcopy(plugin_config),
86
94
  mock_enabled=False,
87
95
  mock_llm_response=None,
96
+ mock_web_response=None,
88
97
  )
89
98
 
90
99
  def batch_create_validators(
@@ -109,7 +118,15 @@ class ValidatorFactory:
109
118
  for _ in range(count)
110
119
  ]
111
120
 
112
- def create_mock_validator(self, mock_llm_response: MockedLLMResponse) -> Validator:
121
+ def create_mock_validator(
122
+ self,
123
+ mock_llm_response: Optional[MockedLLMResponse] = None,
124
+ mock_web_response: Optional[MockedWebResponse] = None,
125
+ ) -> Validator:
126
+ if mock_llm_response is None and mock_web_response is None:
127
+ raise ValueError(
128
+ "mock_llm_response and mock_web_response cannot both be None"
129
+ )
113
130
  return Validator(
114
131
  stake=8,
115
132
  provider="openai",
@@ -121,15 +138,24 @@ class ValidatorFactory:
121
138
  "api_url": "https://api.openai.com",
122
139
  },
123
140
  mock_enabled=True,
124
- mock_llm_response=deepcopy(mock_llm_response),
141
+ mock_llm_response=(
142
+ deepcopy(mock_llm_response) if mock_llm_response is not None else None
143
+ ),
144
+ mock_web_response=(
145
+ deepcopy(mock_web_response) if mock_web_response is not None else None
146
+ ),
125
147
  )
126
148
 
127
149
  def batch_create_mock_validators(
128
150
  self,
129
151
  count: int,
130
- mock_llm_response: MockedLLMResponse,
152
+ mock_llm_response: Optional[MockedLLMResponse] = None,
153
+ mock_web_response: Optional[MockedWebResponse] = None,
131
154
  ) -> List[Validator]:
132
- return [self.create_mock_validator(mock_llm_response) for _ in range(count)]
155
+ return [
156
+ self.create_mock_validator(mock_llm_response, mock_web_response)
157
+ for _ in range(count)
158
+ ]
133
159
 
134
160
 
135
161
  def get_validator_factory() -> ValidatorFactory:
@@ -13,8 +13,6 @@ from gltest_cli.config.general import (
13
13
  from gltest_cli.config.types import PluginConfig
14
14
  from gltest_cli.config.pytest_context import _pytest_context
15
15
  from gltest_cli.config.constants import (
16
- DEFAULT_WAIT_INTERVAL,
17
- DEFAULT_WAIT_RETRIES,
18
16
  DEFAULT_LEADER_ONLY,
19
17
  CHAINS,
20
18
  )
@@ -39,14 +37,14 @@ def pytest_addoption(parser):
39
37
  group.addoption(
40
38
  "--default-wait-interval",
41
39
  action="store",
42
- default=DEFAULT_WAIT_INTERVAL,
40
+ default=None,
43
41
  help="Default interval (ms) between transaction receipt checks",
44
42
  )
45
43
 
46
44
  group.addoption(
47
45
  "--default-wait-retries",
48
46
  action="store",
49
- default=DEFAULT_WAIT_RETRIES,
47
+ default=None,
50
48
  help="Default number of retries for transaction receipt checks",
51
49
  )
52
50
 
@@ -122,8 +120,12 @@ def pytest_configure(config):
122
120
  plugin_config.artifacts_dir = (
123
121
  Path(artifacts_dir) if artifacts_dir is not None else None
124
122
  )
125
- plugin_config.default_wait_interval = int(default_wait_interval)
126
- plugin_config.default_wait_retries = int(default_wait_retries)
123
+ plugin_config.default_wait_interval = (
124
+ int(default_wait_interval) if default_wait_interval is not None else None
125
+ )
126
+ plugin_config.default_wait_retries = (
127
+ int(default_wait_retries) if default_wait_retries is not None else None
128
+ )
127
129
  plugin_config.rpc_url = rpc_url
128
130
  plugin_config.network_name = network
129
131
  plugin_config.leader_only = leader_only