Skip to content

Commit 4755c7b

Browse files
committed
chore: added wait_for_mirro_node util to e2e test
Signed-off-by: Manish Dait <[email protected]>
1 parent 966b598 commit 4755c7b

File tree

3 files changed

+156
-92
lines changed

3 files changed

+156
-92
lines changed

tests/integration/account_id_population_e2e_test.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
TransactionGetReceiptQuery,
1313
)
1414
from hiero_sdk_python.transaction.transfer_transaction import TransferTransaction
15-
from tests.integration.utils import env
15+
from tests.integration.utils import env, wait_for_mirror_node
1616

1717

1818
@pytest.fixture
@@ -45,24 +45,26 @@ def test_populate_account_id_num(env, evm_address):
4545
)
4646

4747
assert transfer_receipt is not None
48-
assert len(transfer_receipt.children) > 0
48+
assert (
49+
len(transfer_receipt.children) > 0
50+
), "Expected child transaction for auto-account creation"
4951

50-
account_id = transfer_receipt.children[0].account_id
51-
52-
assert account_id is not None
53-
54-
# Wait for mirrornode to update
55-
time.sleep(5)
52+
created_account_id = transfer_receipt.children[0].account_id
53+
assert created_account_id is not None
5654

5755
mirror_account_id = AccountId.from_evm_address(evm_address, 0, 0)
58-
new_account_id = mirror_account_id.populate_account_num(env.client)
59-
6056
assert mirror_account_id.num == 0
6157

62-
assert new_account_id.evm_address == mirror_account_id.evm_address
63-
assert new_account_id.shard == account_id.shard
64-
assert new_account_id.realm == account_id.realm
65-
assert new_account_id.num == account_id.num
58+
# Wait for mirrornode to update
59+
resolved_account_id = wait_for_mirror_node(
60+
fn=lambda: mirror_account_id.populate_account_num(env.client),
61+
predicate=lambda acc: acc.num != 0,
62+
)
63+
64+
assert resolved_account_id.evm_address == mirror_account_id.evm_address
65+
assert resolved_account_id.shard == created_account_id.shard
66+
assert resolved_account_id.realm == created_account_id.realm
67+
assert resolved_account_id.num == created_account_id.num
6668

6769

6870
@pytest.mark.integration
@@ -86,17 +88,21 @@ def test_populate_account_id_evm_address(env, evm_address):
8688
)
8789

8890
assert transfer_receipt is not None
89-
assert len(transfer_receipt.children) > 0
91+
assert (
92+
len(transfer_receipt.children) > 0
93+
), "Expected child transaction for auto-account creation"
9094

91-
account_id = transfer_receipt.children[0].account_id
95+
created_account_id = transfer_receipt.children[0].account_id
96+
assert created_account_id is not None
97+
assert created_account_id.evm_address is None
9298

9399
# Wait for mirror_node to update
94-
time.sleep(5)
95-
96-
new_account_id = account_id.populate_evm_address(env.client)
97-
assert account_id.evm_address is None
100+
resolved_account_id = wait_for_mirror_node(
101+
fn=lambda: created_account_id.populate_evm_address(env.client),
102+
predicate=lambda acc: acc.evm_address is not None,
103+
)
98104

99-
assert new_account_id.evm_address == evm_address
100-
assert new_account_id.shard == account_id.shard
101-
assert new_account_id.realm == account_id.realm
102-
assert new_account_id.num == account_id.num
105+
assert resolved_account_id.shard == created_account_id.shard
106+
assert resolved_account_id.realm == created_account_id.realm
107+
assert resolved_account_id.num == created_account_id.num
108+
assert resolved_account_id.evm_address == evm_address

tests/integration/contract_id_population_e2e_test.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from hiero_sdk_python.contract.contract_info_query import ContractInfoQuery
2020
from hiero_sdk_python.file.file_create_transaction import FileCreateTransaction
2121
from hiero_sdk_python.response_code import ResponseCode
22-
from tests.integration.utils import env
22+
from tests.integration.utils import env, wait_for_mirror_node
2323

2424

2525
@pytest.mark.integration
@@ -49,25 +49,22 @@ def test_populate_contract_id_num(env):
4949
.execute(env.client)
5050
)
5151
assert contract_receipt.status == ResponseCode.SUCCESS
52-
contract_id_with_num = contract_receipt.contract_id
5352

54-
assert contract_id_with_num is not None
53+
created_contract_id = contract_receipt.contract_id
54+
assert created_contract_id is not None
5555

5656
# Query contract info to get evm_address
57-
info = ContractInfoQuery().set_contract_id(contract_id_with_num).execute(env.client)
58-
contract_id_with_evm_addr = ContractId.from_evm_address(
59-
0, 0, info.contract_account_id
60-
)
57+
info = ContractInfoQuery().set_contract_id(created_contract_id).execute(env.client)
58+
contract_with_evm = ContractId.from_evm_address(0, 0, info.contract_account_id)
59+
assert contract_with_evm.contract == 0
6160

6261
# Wait for mirror_node to update
63-
time.sleep(5)
64-
65-
final_contract_id = contract_id_with_evm_addr.populate_contract_num(env.client)
66-
67-
# should not update
68-
assert contract_id_with_evm_addr.contract == 0
62+
resolved_contract_id = wait_for_mirror_node(
63+
fn=lambda: contract_with_evm.populate_contract_num(env.client),
64+
predicate=lambda contract: contract.contract != 0,
65+
)
6966

70-
assert final_contract_id.shard == contract_id_with_num.shard
71-
assert final_contract_id.realm == contract_id_with_num.realm
72-
assert final_contract_id.contract == contract_id_with_num.contract
73-
assert final_contract_id.evm_address == contract_id_with_evm_addr.evm_address
67+
assert resolved_contract_id.shard == created_contract_id.shard
68+
assert resolved_contract_id.realm == created_contract_id.realm
69+
assert resolved_contract_id.contract == created_contract_id.contract
70+
assert resolved_contract_id.evm_address == contract_with_evm.evm_address

tests/integration/utils.py

Lines changed: 112 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import os
2+
import time
23
from pytest import fixture
34
from dotenv import load_dotenv
45
from dataclasses import dataclass
5-
from typing import Optional
6+
from typing import Callable, Optional, TypeVar
67
from hiero_sdk_python.account.account_id import AccountId
78
from hiero_sdk_python.client.client import Client
89
from hiero_sdk_python.client.network import Network
@@ -11,43 +12,54 @@
1112
from hiero_sdk_python.logger.log_level import LogLevel
1213
from hiero_sdk_python.response_code import ResponseCode
1314
from hiero_sdk_python.tokens.supply_type import SupplyType
14-
from hiero_sdk_python.tokens.token_create_transaction import TokenCreateTransaction, TokenKeys, TokenParams
15-
from hiero_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
15+
from hiero_sdk_python.tokens.token_create_transaction import (
16+
TokenCreateTransaction,
17+
TokenKeys,
18+
TokenParams,
19+
)
20+
from hiero_sdk_python.tokens.token_associate_transaction import (
21+
TokenAssociateTransaction,
22+
)
1623
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
1724
from hiero_sdk_python.transaction.transfer_transaction import TransferTransaction
18-
from hiero_sdk_python.hbar import Hbar
25+
from hiero_sdk_python.hbar import Hbar
26+
27+
T = TypeVar("T")
1928

2029
load_dotenv(override=True)
2130

31+
2232
@fixture
2333
def env():
2434
"""Integration test environment with client/operator set up."""
2535
e = IntegrationTestEnv()
2636
yield e
2737
e.close()
2838

39+
2940
@dataclass
3041
class Account:
31-
id: AccountId
32-
key: PrivateKey
42+
id: AccountId
43+
key: PrivateKey
44+
3345

3446
class IntegrationTestEnv:
3547

3648
def __init__(self) -> None:
37-
network = Network(os.getenv('NETWORK'))
49+
network = Network(os.getenv("NETWORK"))
3850
self.client = Client(network)
3951
self.operator_id: Optional[AccountId] = None
4052
self.operator_key: Optional[PrivateKey] = None
41-
operator_id = os.getenv('OPERATOR_ID')
42-
operator_key = os.getenv('OPERATOR_KEY')
53+
operator_id = os.getenv("OPERATOR_ID")
54+
operator_key = os.getenv("OPERATOR_KEY")
4355
if operator_id and operator_key:
4456
self.operator_id = AccountId.from_string(operator_id)
4557
self.operator_key = PrivateKey.from_string(operator_key)
4658
self.client.set_operator(self.operator_id, self.operator_key)
4759

4860
self.client.logger.set_level(LogLevel.ERROR)
4961
self.public_operator_key = self.operator_key.public_key()
50-
62+
5163
def close(self):
5264
self.client.close()
5365

@@ -56,8 +68,8 @@ def create_account(self, initial_hbar: float = 1.0) -> Account:
5668
key = PrivateKey.generate()
5769
tx = (
5870
AccountCreateTransaction()
59-
.set_key_without_alias(key.public_key())
60-
.set_initial_balance(Hbar(initial_hbar))
71+
.set_key_without_alias(key.public_key())
72+
.set_initial_balance(Hbar(initial_hbar))
6173
)
6274
receipt = tx.execute(self.client)
6375
if receipt.status != ResponseCode.SUCCESS:
@@ -66,18 +78,20 @@ def create_account(self, initial_hbar: float = 1.0) -> Account:
6678
)
6779
return Account(id=receipt.account_id, key=key)
6880

69-
def associate_and_transfer(self, receiver: AccountId, receiver_key: PrivateKey, token_id, amount: int):
81+
def associate_and_transfer(
82+
self, receiver: AccountId, receiver_key: PrivateKey, token_id, amount: int
83+
):
7084
"""
7185
Associate the token with `receiver`, then transfer `amount` of the token
7286
from the operator to that receiver.
7387
"""
7488
assoc_receipt = (
7589
TokenAssociateTransaction()
76-
.set_account_id(receiver)
77-
.add_token_id(token_id)
78-
.freeze_with(self.client)
79-
.sign(receiver_key)
80-
.execute(self.client)
90+
.set_account_id(receiver)
91+
.add_token_id(token_id)
92+
.freeze_with(self.client)
93+
.sign(receiver_key)
94+
.execute(self.client)
8195
)
8296
if assoc_receipt.status != ResponseCode.SUCCESS:
8397
raise AssertionError(
@@ -86,15 +100,16 @@ def associate_and_transfer(self, receiver: AccountId, receiver_key: PrivateKey,
86100

87101
transfer_receipt = (
88102
TransferTransaction()
89-
.add_token_transfer(token_id, self.operator_id, -amount)
90-
.add_token_transfer(token_id, receiver, amount)
91-
.execute(self.client) # auto-signs with operator’s key
103+
.add_token_transfer(token_id, self.operator_id, -amount)
104+
.add_token_transfer(token_id, receiver, amount)
105+
.execute(self.client) # auto-signs with operator’s key
92106
)
93107
if transfer_receipt.status != ResponseCode.SUCCESS:
94108
raise AssertionError(
95109
f"Transfer failed: {ResponseCode(transfer_receipt.status).name}"
96110
)
97111

112+
98113
def create_fungible_token(env, opts=[]):
99114
"""
100115
Create a fungible token with the given options.
@@ -106,36 +121,39 @@ def create_fungible_token(env, opts=[]):
106121
lambda tx: tx.set_treasury_account_id(custom_treasury_id).freeze_with(client)
107122
"""
108123
token_params = TokenParams(
109-
token_name="PTokenTest34",
110-
token_symbol="PTT34",
111-
decimals=2,
112-
initial_supply=1000,
113-
treasury_account_id=env.operator_id,
114-
token_type=TokenType.FUNGIBLE_COMMON,
115-
supply_type=SupplyType.FINITE,
116-
max_supply=10000
117-
)
118-
124+
token_name="PTokenTest34",
125+
token_symbol="PTT34",
126+
decimals=2,
127+
initial_supply=1000,
128+
treasury_account_id=env.operator_id,
129+
token_type=TokenType.FUNGIBLE_COMMON,
130+
supply_type=SupplyType.FINITE,
131+
max_supply=10000,
132+
)
133+
119134
token_keys = TokenKeys(
120-
admin_key=env.operator_key,
121-
supply_key=env.operator_key,
122-
freeze_key=env.operator_key,
123-
wipe_key=env.operator_key
124-
# pause_key= None # implicitly “no pause key” use opts to add one
125-
)
126-
135+
admin_key=env.operator_key,
136+
supply_key=env.operator_key,
137+
freeze_key=env.operator_key,
138+
wipe_key=env.operator_key,
139+
# pause_key= None # implicitly “no pause key” use opts to add one
140+
)
141+
127142
token_transaction = TokenCreateTransaction(token_params, token_keys)
128-
143+
129144
# Apply optional functions to the token creation transaction
130145
for opt in opts:
131146
opt(token_transaction)
132-
147+
133148
token_receipt = token_transaction.execute(env.client)
134-
135-
assert token_receipt.status == ResponseCode.SUCCESS, f"Token creation failed with status: {ResponseCode(token_receipt.status).name}"
136-
149+
150+
assert (
151+
token_receipt.status == ResponseCode.SUCCESS
152+
), f"Token creation failed with status: {ResponseCode(token_receipt.status).name}"
153+
137154
return token_receipt.token_id
138155

156+
139157
def create_nft_token(env, opts=[]):
140158
"""
141159
Create a non-fungible token (NFT) with the given options.
@@ -154,15 +172,14 @@ def create_nft_token(env, opts=[]):
154172
treasury_account_id=env.operator_id,
155173
token_type=TokenType.NON_FUNGIBLE_UNIQUE,
156174
supply_type=SupplyType.FINITE,
157-
max_supply=10000
175+
max_supply=10000,
158176
)
159-
177+
160178
token_keys = TokenKeys(
161179
admin_key=env.operator_key,
162180
supply_key=env.operator_key,
163-
freeze_key=env.operator_key
181+
freeze_key=env.operator_key,
164182
# pause_key= None # implicitly “no pause key” use opts to add one
165-
166183
)
167184

168185
transaction = TokenCreateTransaction(token_params, token_keys)
@@ -172,7 +189,51 @@ def create_nft_token(env, opts=[]):
172189
opt(transaction)
173190

174191
token_receipt = transaction.execute(env.client)
175-
176-
assert token_receipt.status == ResponseCode.SUCCESS, f"Token creation failed with status: {ResponseCode(token_receipt.status).name}"
177-
178-
return token_receipt.token_id
192+
193+
assert (
194+
token_receipt.status == ResponseCode.SUCCESS
195+
), f"Token creation failed with status: {ResponseCode(token_receipt.status).name}"
196+
197+
return token_receipt.token_id
198+
199+
200+
def wait_for_mirror_node(
201+
fn: Callable[[], T],
202+
predicate: Callable[[T], bool],
203+
timeout: float = 5,
204+
interval: float = 1,
205+
) -> T:
206+
"""
207+
Polls fn until predicate(result) returns True or timeout is reached
208+
209+
Args:
210+
fn: Function that fetches data from mirror node.
211+
predicate: Condition that determines success.
212+
timeout: Max time to wait (seconds).
213+
interval: Sleep interval between retries (seconds).
214+
215+
Returns:
216+
T: The successful result.
217+
"""
218+
deadline = time.monotonic() + timeout
219+
last_response = None
220+
last_exception = None
221+
222+
while time.monotonic() < deadline:
223+
try:
224+
last_response = fn()
225+
if predicate(last_response):
226+
return last_response
227+
except Exception as e:
228+
last_exception = e
229+
230+
time.sleep(interval)
231+
232+
if last_exception is not None:
233+
raise TimeoutError(
234+
"Timed out waiting for mirror node, Last call raised an exception"
235+
) from last_exception
236+
237+
raise TimeoutError(
238+
f"Timed out waiting for mirror node. Last response: {last_response}"
239+
)

0 commit comments

Comments
 (0)