Skip to content

Commit 6e752cf

Browse files
committed
feat: token_pause tests
Signed-off-by: exploreriii <[email protected]>
1 parent 0d2ef5e commit 6e752cf

File tree

2 files changed

+361
-0
lines changed

2 files changed

+361
-0
lines changed
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import pytest
2+
3+
from hiero_sdk_python.crypto.private_key import PrivateKey
4+
from hiero_sdk_python.exceptions import PrecheckError
5+
from hiero_sdk_python.hbar import Hbar
6+
from hiero_sdk_python.query.account_balance_query import CryptoGetAccountBalanceQuery
7+
from hiero_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
8+
from hiero_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
9+
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
10+
from hiero_sdk_python.response_code import ResponseCode
11+
from hiero_sdk_python.tokens.token_pause_transaction import TokenPauseTransaction
12+
from hiero_sdk_python.transaction.transfer_transaction import TransferTransaction
13+
from tests.integration.utils_for_test import IntegrationTestEnv, create_fungible_token
14+
from hiero_sdk_python.tokens.token_id import TokenId
15+
from hiero_sdk_python.tokens.token_type import TokenType
16+
from hiero_sdk_python.tokens.supply_type import SupplyType
17+
from hiero_sdk_python.tokens.token_info_query import TokenInfoQuery
18+
19+
@pytest.mark.integration
20+
def test_pause_without_setting_token_id_raises_client_error():
21+
"""
22+
Building a TokenPauseTransaction without ever
23+
calling set_token_id(), should error.
24+
"""
25+
env = IntegrationTestEnv()
26+
try:
27+
tx = TokenPauseTransaction() # no set_token_id()
28+
with pytest.raises(ValueError, match="token_id must be set"):
29+
tx.freeze_with(env.client)
30+
finally:
31+
env.close()
32+
33+
@pytest.mark.integration
34+
def test_pause_nonexistent_token_raises_precheck():
35+
"""
36+
Trying to pause a token that doesn’t exist should fail fast
37+
with INVALID_TOKEN_ID.
38+
"""
39+
env = IntegrationTestEnv()
40+
try:
41+
bogus = TokenPauseTransaction().set_token_id(TokenId(0, 0, 99999999))
42+
bogus.freeze_with(env.client)
43+
44+
with pytest.raises(
45+
PrecheckError,
46+
match="failed precheck with status: INVALID_TOKEN_ID"
47+
) as excinfo:
48+
bogus.execute(env.client)
49+
50+
assert "INVALID_TOKEN_ID" in str(excinfo.value)
51+
finally:
52+
env.close()
53+
54+
@pytest.mark.integration
55+
def test_pause_without_pause_key_fails():
56+
"""
57+
A token WITHOUT a pause key, attempting to pause it
58+
should hit TOKEN_HAS_NO_PAUSE_KEY error.
59+
"""
60+
env = IntegrationTestEnv()
61+
try:
62+
# 1) Create a token with no pause_key
63+
token = (
64+
TokenCreateTransaction()
65+
.set_token_name("NoPause")
66+
.set_token_symbol("NOP")
67+
.set_decimals(0)
68+
.set_initial_supply(1)
69+
.set_treasury_account_id(env.operator_id)
70+
.set_token_type(TokenType.FUNGIBLE_COMMON)
71+
.set_supply_type(SupplyType.FINITE)
72+
.set_max_supply(1)
73+
.freeze_with(env.client)
74+
).execute(env.client).tokenId
75+
76+
# 2) Attempt to pause it
77+
tx = TokenPauseTransaction().set_token_id(token)
78+
tx.freeze_with(env.client)
79+
80+
with pytest.raises(
81+
PrecheckError,
82+
match="failed precheck with status: TOKEN_HAS_NO_PAUSE_KEY"
83+
):
84+
tx.execute(env.client)
85+
finally:
86+
env.close()
87+
88+
89+
@pytest.mark.integration
90+
def test_pause_prevents_transfers_and_reflects_in_info():
91+
"""
92+
Create a token with a pause key, make a transfer,
93+
pause the token, verify transfers now fail,
94+
and confirm TokenInfoQuery reports PAUSED status.
95+
"""
96+
env = IntegrationTestEnv()
97+
try:
98+
# 1) create a test account to receive tokens
99+
recv_key = PrivateKey.generate()
100+
recv_tx = (
101+
AccountCreateTransaction()
102+
.set_key(recv_key.public_key())
103+
.set_initial_balance(Hbar(1))
104+
.freeze_with(env.client)
105+
).execute(env.client)
106+
recv = recv_tx.accountId
107+
108+
# 2) create a token WITH a pause key
109+
token = env.create_fungible_token(pause_key=env.operator_key)
110+
111+
# 3) associate and transfer some
112+
(
113+
TokenAssociateTransaction()
114+
.set_account_id(recv)
115+
.add_token_id(token)
116+
.freeze_with(env.client)
117+
.sign(recv_key)
118+
).execute(env.client)
119+
(
120+
TransferTransaction()
121+
.add_token_transfer(token, env.operator_id, -10)
122+
.add_token_transfer(token, recv, 10)
123+
.freeze_with(env.client)
124+
).execute(env.client)
125+
126+
# balance check before pause
127+
bal = CryptoGetAccountBalanceQuery(recv).execute(env.client)
128+
assert bal.token_balances[token] == 10
129+
130+
# 4) pause the token
131+
(
132+
TokenPauseTransaction()
133+
.set_token_id(token)
134+
.freeze_with(env.client)
135+
.sign(env.operator_key)
136+
).execute(env.client)
137+
138+
# info query should show PAUSED
139+
status = TokenInfoQuery().set_token_id(token).execute(env.client).token_status
140+
assert status.name == "PAUSED"
141+
142+
# 5) any further transfer should now throw ReceiptStatusException TOKEN_IS_PAUSED
143+
with pytest.raises(ReceiptStatusException) as exc:
144+
(
145+
TransferTransaction()
146+
.add_token_transfer(token, env.operator_id, -1)
147+
.add_token_transfer(token, recv, 1)
148+
.freeze_with(env.client)
149+
.sign(env.operator_key)
150+
.execute(env.client)
151+
)
152+
assert "TOKEN_IS_PAUSED" in str(exc.value)
153+
154+
finally:
155+
env.close()
156+
157+
@pytest.mark.integration
158+
def test_double_pause_raises_token_already_paused():
159+
"""
160+
Once a token is already paused, attempting to pause it again
161+
should hit the TOKEN_ALREADY_PAUSED precheck error.
162+
"""
163+
env = IntegrationTestEnv()
164+
try:
165+
# Create a token with a pause key
166+
token = env.create_fungible_token(pause_key=env.operator_key)
167+
168+
# First pause should succeed
169+
(
170+
TokenPauseTransaction()
171+
.set_token_id(token)
172+
.freeze_with(env.client)
173+
.sign(env.operator_key)
174+
.execute(env.client)
175+
)
176+
177+
# Confirm status is PAUSED
178+
status = (
179+
TokenInfoQuery()
180+
.set_token_id(token)
181+
.execute(env.client)
182+
.token_status
183+
)
184+
assert status.name == "PAUSED"
185+
186+
# Second pause should fail with TOKEN_ALREADY_PAUSED
187+
tx = TokenPauseTransaction().set_token_id(token)
188+
tx.freeze_with(env.client)
189+
tx.sign(env.operator_key)
190+
with pytest.raises(
191+
PrecheckError,
192+
match="failed precheck with status: TOKEN_ALREADY_PAUSED"
193+
):
194+
tx.execute(env.client)
195+
196+
finally:
197+
env.close()
198+
199+
200+
@pytest.mark.integration
201+
def test_pause_with_wrong_key_raises_sig_mismatch():
202+
"""
203+
Signing a pause transaction with a key that is NOT the token's pause key
204+
should fail the precheck with SIG_MISMATCH.
205+
"""
206+
env = IntegrationTestEnv()
207+
try:
208+
# Create a token whose pause key is the operator
209+
token = env.create_fungible_token(pause_key=env.operator_key)
210+
211+
# Build & freeze the pause transaction
212+
tx = TokenPauseTransaction().set_token_id(token)
213+
tx.freeze_with(env.client)
214+
215+
# Sign with a completely unrelated key
216+
wrong_key = PrivateKey.generate()
217+
tx.sign(wrong_key)
218+
219+
# Expect signature mismatch at precheck
220+
with pytest.raises(
221+
PrecheckError,
222+
match="failed precheck with status: SIG_MISMATCH"
223+
):
224+
tx.execute(env.client)
225+
226+
finally:
227+
env.close()
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import pytest
2+
from unittest.mock import MagicMock
3+
4+
from hiero_sdk_python.hapi.services.transaction_receipt_pb2 import TransactionReceipt as TransactionReceiptProto
5+
from hiero_sdk_python.hapi.services.transaction_response_pb2 import TransactionResponse as TransactionResponseProto
6+
from hiero_sdk_python.response_code import ResponseCode
7+
from hiero_sdk_python.tokens.token_pause_transaction import TokenPauseTransaction
8+
from hiero_sdk_python.hapi.services import response_header_pb2, response_pb2, timestamp_pb2, transaction_get_receipt_pb2
9+
from hiero_sdk_python.transaction.transaction_id import TransactionId
10+
from hiero_sdk_python.tokens.token_id import TokenId
11+
12+
from tests.unit.mock_server import mock_hedera_servers
13+
14+
pytestmark = pytest.mark.unit
15+
16+
def generate_transaction_id(account_id_proto):
17+
"""Generate a unique transaction ID based on the account ID and the current timestamp."""
18+
import time
19+
current_time = time.time()
20+
timestamp_seconds = int(current_time)
21+
timestamp_nanos = int((current_time - timestamp_seconds) * 1e9)
22+
23+
tx_timestamp = timestamp_pb2.Timestamp(seconds=timestamp_seconds, nanos=timestamp_nanos)
24+
25+
tx_id = TransactionId(
26+
valid_start=tx_timestamp,
27+
account_id=account_id_proto
28+
)
29+
return tx_id
30+
31+
def test_builds_token_pause_body_with_correct_token_id(mock_account_ids):
32+
"""
33+
Given a TokenPauseTransaction with a valid token_id set, when building the
34+
transaction body, then the inner `tokenPause.token` fields should match.
35+
"""
36+
account_id, node_account_id, token_id, _ = mock_account_ids
37+
38+
pause_tx = (
39+
TokenPauseTransaction()
40+
.set_token_id(token_id)
41+
)
42+
43+
pause_tx.transaction_id = generate_transaction_id(account_id)
44+
pause_tx.node_account_id = node_account_id
45+
46+
transaction_body = pause_tx.build_transaction_body()
47+
48+
expected = token_id.to_proto()
49+
assert transaction_body.tokenPause.token == expected
50+
51+
assert transaction_body.transactionID == pause_tx.transaction_id.to_proto()
52+
assert transaction_body.nodeAccountID == pause_tx.node_account_id.to_proto()
53+
54+
@pytest.mark.parametrize("bad_token", [None, TokenId(0,0,0)])
55+
def test_build_transaction_body_without_valid_token_id_raises(bad_token):
56+
"""Building a transaction body without a valid token_id must raise ValueError."""
57+
tx = TokenPauseTransaction()
58+
if bad_token is not None:
59+
tx.token_id = bad_token
60+
with pytest.raises(ValueError):
61+
tx.build_transaction_body()
62+
63+
# This test uses fixture (mock_account_ids, mock_client) as parameter
64+
def test_signed_bytes_include_token_pause_transaction(mock_account_ids, mock_client):
65+
"""Test converting the token pause transaction to protobuf format after signing."""
66+
account_id, _, token_id, _ = mock_account_ids
67+
68+
pause_tx = (
69+
TokenPauseTransaction()
70+
.set_token_id(token_id)
71+
)
72+
73+
pause_tx.transaction_id = generate_transaction_id(account_id)
74+
75+
pause_key = MagicMock()
76+
pause_key.sign.return_value = b'signature'
77+
pause_key.public_key().to_bytes_raw.return_value = b'public_key'
78+
79+
pause_tx.freeze_with(mock_client)
80+
81+
pause_tx.sign(pause_key)
82+
proto = pause_tx.to_proto()
83+
84+
assert proto.signedTransactionBytes
85+
assert len(proto.signedTransactionBytes) > 0
86+
87+
# This test uses fixture mock_account_ids as parameter
88+
def test_pause_transaction_can_execute(mock_account_ids):
89+
"""Test that a pause transaction can be executed successfully."""
90+
account_id, node_account_id, token_id, _ = mock_account_ids
91+
92+
# Create test transaction responses
93+
ok_response = TransactionResponseProto()
94+
ok_response.nodeTransactionPrecheckCode = ResponseCode.OK
95+
96+
# Create a mock receipt for a successful token pause
97+
mock_receipt_proto = TransactionReceiptProto(
98+
status=ResponseCode.SUCCESS
99+
)
100+
101+
# Create a response for the receipt query
102+
receipt_query_response = response_pb2.Response(
103+
transactionGetReceipt=transaction_get_receipt_pb2.TransactionGetReceiptResponse(
104+
header=response_header_pb2.ResponseHeader(
105+
nodeTransactionPrecheckCode=ResponseCode.OK
106+
),
107+
receipt=mock_receipt_proto
108+
)
109+
)
110+
111+
response_sequences = [
112+
[ok_response, receipt_query_response],
113+
]
114+
115+
with mock_hedera_servers(response_sequences) as client:
116+
# Build the transaction
117+
transaction = (
118+
TokenPauseTransaction()
119+
.set_token_id(token_id)
120+
)
121+
# Set identifiers so freeze/sign can populate the payload correctly
122+
transaction.transaction_id = generate_transaction_id(account_id)
123+
transaction.node_account_id = node_account_id
124+
125+
# Freeze and sign before execute
126+
transaction.freeze_with(client)
127+
dummy_key = MagicMock()
128+
dummy_key.sign.return_value = b'__SIG__'
129+
dummy_key.public_key().to_bytes_raw.return_value = b'PUB'
130+
transaction.sign(dummy_key)
131+
132+
# Execute and assert
133+
receipt = transaction.execute(client)
134+
assert receipt.status == ResponseCode.SUCCESS, "Transaction should have succeeded"

0 commit comments

Comments
 (0)