Skip to content

Commit a436cb8

Browse files
committed
test(tck): add comprehensive tests covering params parsing, transaction-building helpers, full
end-to-end dispatch against a mocked network, and robustness checks for unknown sessionId / wrong-type params (confirmed against freezeToken as a control - pre-existing TCK-layer behavior) Signed-off-by: iron-prog <dt915725@gmail.com>
1 parent aa45186 commit a436cb8

1 file changed

Lines changed: 205 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
from __future__ import annotations
2+
3+
import importlib
4+
5+
import pytest
6+
7+
from hiero_sdk_python.account.account_id import AccountId
8+
from hiero_sdk_python.hapi.services import response_header_pb2, response_pb2, transaction_get_receipt_pb2
9+
from hiero_sdk_python.hapi.services.transaction_receipt_pb2 import TransactionReceipt as TransactionReceiptProto
10+
from hiero_sdk_python.hapi.services.transaction_response_pb2 import TransactionResponse as TransactionResponseProto
11+
from hiero_sdk_python.response_code import ResponseCode
12+
from hiero_sdk_python.tokens.token_id import TokenId
13+
from tck.errors import JsonRpcError
14+
from tck.handlers.registry import get_handler
15+
from tck.handlers.token import (
16+
_build_grant_token_kyc_transaction,
17+
_build_revoke_token_kyc_transaction,
18+
)
19+
from tck.param.token import FreezeTokenParams, GrantTokenKycParams, RevokeTokenKycParams
20+
from tck.util.client_utils import remove_client, store_client
21+
from tests.unit.mock_server import mock_hedera_servers
22+
23+
24+
pytestmark = pytest.mark.unit
25+
26+
27+
ACCOUNT_ID = AccountId(0, 0, 5555)
28+
TOKEN_ID = TokenId(0, 0, 9999)
29+
30+
31+
@pytest.fixture(autouse=True)
32+
def _ensure_handlers_registered():
33+
from tck.handlers import token as token_handlers
34+
35+
importlib.reload(token_handlers)
36+
yield
37+
38+
39+
def _success_response_sequence():
40+
"""Build a single-transaction response sequence that reports SUCCESS."""
41+
ok_response = TransactionResponseProto()
42+
ok_response.nodeTransactionPrecheckCode = ResponseCode.OK
43+
44+
receipt_proto = TransactionReceiptProto(status=ResponseCode.SUCCESS)
45+
receipt_query_response = response_pb2.Response(
46+
transactionGetReceipt=transaction_get_receipt_pb2.TransactionGetReceiptResponse(
47+
header=response_header_pb2.ResponseHeader(nodeTransactionPrecheckCode=ResponseCode.OK),
48+
receipt=receipt_proto,
49+
)
50+
)
51+
return [[ok_response, receipt_query_response]]
52+
53+
54+
class TestGrantTokenKycParamsParsing:
55+
def test_parses_full_valid_payload(self):
56+
params = GrantTokenKycParams.parse_json_params(
57+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "session-1"}
58+
)
59+
assert params.tokenId == "0.0.9999"
60+
assert params.accountId == "0.0.5555"
61+
assert params.sessionId == "session-1"
62+
63+
def test_missing_session_id_raises(self):
64+
with pytest.raises(ValueError, match="sessionId"):
65+
GrantTokenKycParams.parse_json_params({"tokenId": "0.0.9999", "accountId": "0.0.5555"})
66+
67+
def test_empty_session_id_raises(self):
68+
with pytest.raises(ValueError, match="sessionId"):
69+
GrantTokenKycParams.parse_json_params({"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": ""})
70+
71+
def test_missing_token_and_account_id_parse_as_none(self):
72+
"""tokenId/accountId are optional at parse time; the TCK spec uses
73+
their absence to test the endpoint's own validation errors."""
74+
params = GrantTokenKycParams.parse_json_params({"sessionId": "session-1"})
75+
assert params.tokenId is None
76+
assert params.accountId is None
77+
78+
79+
class TestRevokeTokenKycParamsParsing:
80+
def test_parses_full_valid_payload(self):
81+
params = RevokeTokenKycParams.parse_json_params(
82+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "session-1"}
83+
)
84+
assert params.tokenId == "0.0.9999"
85+
assert params.accountId == "0.0.5555"
86+
assert params.sessionId == "session-1"
87+
88+
def test_missing_session_id_raises(self):
89+
with pytest.raises(ValueError, match="sessionId"):
90+
RevokeTokenKycParams.parse_json_params({"tokenId": "0.0.9999", "accountId": "0.0.5555"})
91+
92+
93+
class TestBuildGrantTokenKycTransaction:
94+
def test_sets_both_fields_when_present(self):
95+
params = GrantTokenKycParams.parse_json_params(
96+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "s"}
97+
)
98+
tx = _build_grant_token_kyc_transaction(params)
99+
assert tx.token_id == TOKEN_ID
100+
assert tx.account_id == ACCOUNT_ID
101+
102+
def test_leaves_account_id_none_when_absent(self):
103+
params = GrantTokenKycParams.parse_json_params({"tokenId": "0.0.9999", "sessionId": "s"})
104+
tx = _build_grant_token_kyc_transaction(params)
105+
assert tx.token_id == TOKEN_ID
106+
assert tx.account_id is None
107+
108+
def test_missing_account_id_fails_at_proto_build_not_silently(self):
109+
"""Partial params build an executable-looking transaction object,
110+
but the SDK's own validation must still catch the missing field
111+
before anything reaches the network."""
112+
params = GrantTokenKycParams.parse_json_params({"tokenId": "0.0.9999", "sessionId": "s"})
113+
tx = _build_grant_token_kyc_transaction(params)
114+
with pytest.raises(ValueError, match="Missing account ID"):
115+
tx._build_proto_body()
116+
117+
118+
class TestBuildRevokeTokenKycTransaction:
119+
def test_sets_both_fields_when_present(self):
120+
params = RevokeTokenKycParams.parse_json_params(
121+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "s"}
122+
)
123+
tx = _build_revoke_token_kyc_transaction(params)
124+
assert tx.token_id == TOKEN_ID
125+
assert tx.account_id == ACCOUNT_ID
126+
127+
def test_missing_token_id_fails_at_proto_build_not_silently(self):
128+
params = RevokeTokenKycParams.parse_json_params({"accountId": "0.0.5555", "sessionId": "s"})
129+
tx = _build_revoke_token_kyc_transaction(params)
130+
with pytest.raises(ValueError, match="Missing token ID"):
131+
tx._build_proto_body()
132+
133+
134+
class TestEndToEndDispatch:
135+
def test_grant_token_kyc_success(self):
136+
session_id = "e2e-grant-session"
137+
with mock_hedera_servers(_success_response_sequence()) as client:
138+
store_client(session_id, client)
139+
try:
140+
handler = get_handler("grantTokenKyc")
141+
params = GrantTokenKycParams.parse_json_params(
142+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": session_id}
143+
)
144+
response = handler(params)
145+
assert response.status == "SUCCESS"
146+
finally:
147+
remove_client(session_id)
148+
149+
def test_revoke_token_kyc_success(self):
150+
session_id = "e2e-revoke-session"
151+
with mock_hedera_servers(_success_response_sequence()) as client:
152+
store_client(session_id, client)
153+
try:
154+
handler = get_handler("revokeTokenKyc")
155+
params = RevokeTokenKycParams.parse_json_params(
156+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": session_id}
157+
)
158+
response = handler(params)
159+
assert response.status == "SUCCESS"
160+
finally:
161+
remove_client(session_id)
162+
163+
164+
class TestErrorHandlingRobustness:
165+
def test_unknown_session_id_does_not_leak_raw_exception(self):
166+
handler = get_handler("revokeTokenKyc")
167+
params = RevokeTokenKycParams.parse_json_params(
168+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "never-registered"}
169+
)
170+
with pytest.raises(JsonRpcError) as exc_info:
171+
handler(params)
172+
assert exc_info.value.code == JsonRpcError.internal_error().code
173+
assert exc_info.value.message == "Internal error"
174+
assert exc_info.value.data is None
175+
176+
def test_unknown_session_id_same_behavior_as_freeze_token_control(self):
177+
handler = get_handler("freezeToken")
178+
params = FreezeTokenParams.parse_json_params(
179+
{"tokenId": "0.0.9999", "accountId": "0.0.5555", "sessionId": "never-registered"}
180+
)
181+
with pytest.raises(JsonRpcError) as exc_info:
182+
handler(params)
183+
assert exc_info.value.code == JsonRpcError.internal_error().code
184+
assert exc_info.value.message == "Internal error"
185+
186+
def test_wrong_type_token_id_does_not_leak_raw_exception(self):
187+
handler = get_handler("revokeTokenKyc")
188+
params = RevokeTokenKycParams.parse_json_params(
189+
{"tokenId": 9999, "accountId": "0.0.5555", "sessionId": "irrelevant"}
190+
)
191+
with pytest.raises(JsonRpcError) as exc_info:
192+
handler(params)
193+
assert exc_info.value.code == JsonRpcError.internal_error().code
194+
assert exc_info.value.message == "Internal error"
195+
assert exc_info.value.data is None
196+
197+
def test_wrong_type_account_id_does_not_leak_raw_exception(self):
198+
handler = get_handler("grantTokenKyc")
199+
params = GrantTokenKycParams.parse_json_params(
200+
{"tokenId": "0.0.9999", "accountId": ["not", "a", "string"], "sessionId": "irrelevant"}
201+
)
202+
with pytest.raises(JsonRpcError) as exc_info:
203+
handler(params)
204+
assert exc_info.value.code == JsonRpcError.internal_error().code
205+
assert exc_info.value.message == "Internal error"

0 commit comments

Comments
 (0)