From c4bdc4211e40e16421e22ac74b5279c59ea141c3 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Tue, 31 Mar 2026 16:34:02 +0530 Subject: [PATCH 01/10] Feat: implemented GetAccInfo, TCK endpt. Signed-off-by: Adityarya11 --- tck/handlers/account.py | 73 +++++++++++++++++++++++++++++++++++++++-- tck/param/account.py | 11 ++++++- tck/response/account.py | 40 ++++++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/tck/handlers/account.py b/tck/handlers/account.py index 1b11a0372..b8f835d3f 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -2,12 +2,18 @@ from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction from hiero_sdk_python.account.account_id import AccountId +from hiero_sdk_python.query.account_info_query import AccountInfoQuery from hiero_sdk_python.hbar import Hbar from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt from tck.handlers.registry import rpc_method -from tck.param.account import CreateAccountParams -from tck.response.account import CreateAccountResponse +from tck.param.account import CreateAccountParams, GetAccountInfoParams +from tck.response.account import ( + CreateAccountResponse, + GetAccountInfoResponse, + StakingInfoResponse, + TokenRelationshipResponse, +) from tck.util.client_utils import get_client from tck.util.constants import DEFAULT_GRPC_TIMEOUT from tck.util.key_utils import get_key_from_string @@ -66,3 +72,66 @@ def create_account(params: CreateAccountParams) -> CreateAccountResponse: account_id = str(receipt.account_id) return CreateAccountResponse(account_id, ResponseCode(receipt.status).name) + + +def _build_account_info_response(info) -> GetAccountInfoResponse: + staking_info_response = None + if info.staking_info: + staking_info_response = StakingInfoResponse( + declineStakingReward=info.staking_info.decline_staking_reward, + stakePeriodStart=str(info.staking_info.stake_period_start) + if info.staking_info.stake_period_start + else None, + pendingReward=str(info.staking_info.pending_reward.to_tinybars()) + if info.staking_info.pending_reward + else None, + stakedToMe=str(info.staking_info.staked_to_me.to_tinybars()) if info.staking_info.staked_to_me else None, + stakedAccountId=str(info.staking_info.staked_account_id) if info.staking_info.staked_account_id else None, + stakedNodeId=str(info.staking_info.staked_node_id) if info.staking_info.staked_node_id else None, + ) + + token_relationships_response = [] + if info.token_relationships: + for rel in info.token_relationships: + token_relationships_response.append( + TokenRelationshipResponse( + tokenId=str(rel.token_id) if rel.token_id else None, + symbol=rel.symbol, + balance=str(rel.balance) if rel.balance is not None else None, + kycStatus=str(rel.kyc_status) if rel.kyc_status is not None else None, + freezeStatus=str(rel.freeze_status) if rel.freeze_status is not None else None, + decimals=str(rel.decimals) if rel.decimals is not None else None, + automaticAssociation=rel.automatic_association, + ) + ) + + return GetAccountInfoResponse( + accountId=str(info.account_id) if info.account_id else None, + contractAccountId=info.contract_account_id, + isDeleted=info.is_deleted, + proxyReceived=str(info.proxy_received.to_tinybars()) if info.proxy_received else None, + key=info.key.to_bytes().hex() if info.key else None, + balance=str(info.balance.to_tinybars()) if info.balance else None, + isReceiverSignatureRequired=info.receiver_signature_required, + expirationTime=str(info.expiration_time) if info.expiration_time else None, + autoRenewPeriod=str(info.auto_renew_period.seconds) if info.auto_renew_period else None, + tokenRelationships=token_relationships_response if token_relationships_response else None, + accountMemo=info.account_memo, + ownedNfts=str(info.owned_nfts) if info.owned_nfts is not None else None, + maxAutomaticTokenAssociations=str(info.max_automatic_token_associations) + if info.max_automatic_token_associations is not None + else None, + stakingInfo=staking_info_response, + ) + + +@rpc_method("getAccountInfo") +def get_account_info(params: GetAccountInfoParams) -> GetAccountInfoResponse: + client = get_client(params.sessionId) + + query = AccountInfoQuery() + if params.accountId: + query.set_account_id(AccountId.from_string(params.accountId)) + + info = query.execute(client) + return _build_account_info_response(info) diff --git a/tck/param/account.py b/tck/param/account.py index 58087aece..14b7e07d9 100644 --- a/tck/param/account.py +++ b/tck/param/account.py @@ -2,7 +2,7 @@ from dataclasses import dataclass -from tck.param.base import BaseTransactionParams +from tck.param.base import BaseParams, BaseTransactionParams from tck.util.param_utils import ( parse_common_transaction_params, parse_session_id, @@ -40,3 +40,12 @@ def parse_json_params(cls, params: dict) -> CreateAccountParams: sessionId=parse_session_id(params), commonTransactionParams=parse_common_transaction_params(params), ) + + +@dataclass +class GetAccountInfoParams(BaseParams): + accountId: str | None = None + + @classmethod + def parse_json_params(cls, params: dict) -> "GetAccountInfoParams": + return cls(accountId=params.get("accountId"), sessionId=parse_session_id(params)) diff --git a/tck/response/account.py b/tck/response/account.py index 73d4c72b3..495313952 100644 --- a/tck/response/account.py +++ b/tck/response/account.py @@ -1,9 +1,49 @@ from __future__ import annotations from dataclasses import dataclass +from typing import List, Optional @dataclass class CreateAccountResponse: accountId: str | None = None status: str | None = None + + +@dataclass +class StakingInfoResponse: + declineStakingReward: bool | None = None + stakePeriodStart: str | None = None + pendingReward: str | None = None + stakedToMe: str | None = None + stakedAccountId: str | None = None + stakedNodeId: str | None = None + + +@dataclass +class TokenRelationshipResponse: + tokenId: str | None = None + symbol: str | None = None + balance: str | None = None + kycStatus: str | None = None + freezeStatus: str | None = None + decimals: str | None = None + automaticAssociation: bool | None = None + + +@dataclass +class GetAccountInfoResponse: + accountId: str | None = None + contractAccountId: str | None = None + isDeleted: bool | None = None + proxyReceived: str | None = None + key: str | None = None + balance: str | None = None + isReceiverSignatureRequired: bool | None = None + expirationTime: str | None = None + autoRenewPeriod: str | None = None + tokenRelationships: List[TokenRelationshipResponse] | None = None + accountMemo: str | None = None + ownedNfts: str | None = None + maxAutomaticTokenAssociations: str | None = None + stakingInfo: StakingInfoResponse | None = None From 453327698310fb258eeafcf98a43629b843701c9 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Tue, 7 Apr 2026 00:30:22 +0530 Subject: [PATCH 02/10] feat: implemented getAccountInfo Tck endpt Signed-off-by: Adityarya11 --- tck/handlers/account.py | 147 +++++++++++++++++++--------- tck/response/account.py | 14 ++- tests/tck/test_account_info.py | 170 +++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 47 deletions(-) create mode 100644 tests/tck/test_account_info.py diff --git a/tck/handlers/account.py b/tck/handlers/account.py index b8f835d3f..f67f601f5 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -2,10 +2,12 @@ from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction from hiero_sdk_python.account.account_id import AccountId +from hiero_sdk_python.account.account_info import AccountInfo from hiero_sdk_python.query.account_info_query import AccountInfoQuery from hiero_sdk_python.hbar import Hbar from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt +from tck.errors import JsonRpcError from tck.handlers.registry import rpc_method from tck.param.account import CreateAccountParams, GetAccountInfoParams from tck.response.account import ( @@ -74,54 +76,101 @@ def create_account(params: CreateAccountParams) -> CreateAccountResponse: return CreateAccountResponse(account_id, ResponseCode(receipt.status).name) -def _build_account_info_response(info) -> GetAccountInfoResponse: - staking_info_response = None - if info.staking_info: - staking_info_response = StakingInfoResponse( - declineStakingReward=info.staking_info.decline_staking_reward, - stakePeriodStart=str(info.staking_info.stake_period_start) - if info.staking_info.stake_period_start - else None, - pendingReward=str(info.staking_info.pending_reward.to_tinybars()) - if info.staking_info.pending_reward - else None, - stakedToMe=str(info.staking_info.staked_to_me.to_tinybars()) if info.staking_info.staked_to_me else None, - stakedAccountId=str(info.staking_info.staked_account_id) if info.staking_info.staked_account_id else None, - stakedNodeId=str(info.staking_info.staked_node_id) if info.staking_info.staked_node_id else None, +def _enum_name(value) -> str | None: + if value is None: + return None + return getattr(value, "name", str(value)) + + +def _serialize_key(key) -> str | None: + if key is None: + return None + + to_string_der = getattr(key, "to_string_der", None) + if callable(to_string_der): + return to_string_der() + + return key.to_bytes().hex() + + +def _to_staking_info_response(info: AccountInfo) -> StakingInfoResponse: + if info.staking_info is None: + return StakingInfoResponse( + declineStakingReward=False, + pendingReward="0", + stakedToMe="0", + ) + + staking_info = info.staking_info + return StakingInfoResponse( + declineStakingReward=staking_info.decline_reward, + stakePeriodStart=str(staking_info.stake_period_start) + if staking_info.stake_period_start is not None + else None, + pendingReward=str(staking_info.pending_reward.to_tinybars()) + if staking_info.pending_reward is not None + else "0", + stakedToMe=str(staking_info.staked_to_me.to_tinybars()) + if staking_info.staked_to_me is not None + else "0", + stakedAccountId=str(staking_info.staked_account_id) + if staking_info.staked_account_id is not None + else None, + stakedNodeId=str(staking_info.staked_node_id) + if staking_info.staked_node_id is not None + else None, + ) + + +def _to_token_relationships_response(info: AccountInfo) -> dict[str, TokenRelationshipResponse]: + token_relationships_response: dict[str, TokenRelationshipResponse] = {} + + for relationship in info.token_relationships: + token_id = str(relationship.token_id) if relationship.token_id is not None else None + if token_id is None: + continue + + token_relationships_response[token_id] = TokenRelationshipResponse( + tokenId=token_id, + symbol=relationship.symbol, + balance=str(relationship.balance) if relationship.balance is not None else "0", + kycStatus=_enum_name(relationship.kyc_status), + freezeStatus=_enum_name(relationship.freeze_status), + decimals=str(relationship.decimals) if relationship.decimals is not None else None, + automaticAssociation=relationship.automatic_association, ) - token_relationships_response = [] - if info.token_relationships: - for rel in info.token_relationships: - token_relationships_response.append( - TokenRelationshipResponse( - tokenId=str(rel.token_id) if rel.token_id else None, - symbol=rel.symbol, - balance=str(rel.balance) if rel.balance is not None else None, - kycStatus=str(rel.kyc_status) if rel.kyc_status is not None else None, - freezeStatus=str(rel.freeze_status) if rel.freeze_status is not None else None, - decimals=str(rel.decimals) if rel.decimals is not None else None, - automaticAssociation=rel.automatic_association, - ) - ) + return token_relationships_response + +def _build_account_info_response(info: AccountInfo) -> GetAccountInfoResponse: return GetAccountInfoResponse( - accountId=str(info.account_id) if info.account_id else None, - contractAccountId=info.contract_account_id, - isDeleted=info.is_deleted, - proxyReceived=str(info.proxy_received.to_tinybars()) if info.proxy_received else None, - key=info.key.to_bytes().hex() if info.key else None, - balance=str(info.balance.to_tinybars()) if info.balance else None, - isReceiverSignatureRequired=info.receiver_signature_required, - expirationTime=str(info.expiration_time) if info.expiration_time else None, - autoRenewPeriod=str(info.auto_renew_period.seconds) if info.auto_renew_period else None, - tokenRelationships=token_relationships_response if token_relationships_response else None, - accountMemo=info.account_memo, - ownedNfts=str(info.owned_nfts) if info.owned_nfts is not None else None, + accountId=str(info.account_id) if info.account_id is not None else None, + contractAccountId=info.contract_account_id or "", + isDeleted=bool(info.is_deleted), + proxyAccountId="", + proxyReceived=str(info.proxy_received.to_tinybars()) + if info.proxy_received is not None + else "0", + key=_serialize_key(info.key), + balance=str(info.balance.to_tinybars()) if info.balance is not None else "0", + sendRecordThreshold="0", + receiveRecordThreshold="0", + isReceiverSignatureRequired=bool(info.receiver_signature_required), + expirationTime=str(info.expiration_time) if info.expiration_time is not None else None, + autoRenewPeriod=str(info.auto_renew_period.seconds) + if info.auto_renew_period is not None + else "0", + tokenRelationships=_to_token_relationships_response(info), + accountMemo=info.account_memo or "", + ownedNfts=str(info.owned_nfts) if info.owned_nfts is not None else "0", maxAutomaticTokenAssociations=str(info.max_automatic_token_associations) if info.max_automatic_token_associations is not None - else None, - stakingInfo=staking_info_response, + else "0", + aliasKey="", + ledgerId="", + ethereumNonce="0", + stakingInfo=_to_staking_info_response(info), ) @@ -129,9 +178,17 @@ def _build_account_info_response(info) -> GetAccountInfoResponse: def get_account_info(params: GetAccountInfoParams) -> GetAccountInfoResponse: client = get_client(params.sessionId) - query = AccountInfoQuery() - if params.accountId: - query.set_account_id(AccountId.from_string(params.accountId)) + if not params.accountId: + raise JsonRpcError.hiero_error({"status": ResponseCode.INVALID_ACCOUNT_ID.name}) + + try: + account_id = AccountId.from_string(params.accountId) + except (TypeError, ValueError) as error: + raise JsonRpcError.hiero_error( + {"status": ResponseCode.INVALID_ACCOUNT_ID.name} + ) from error + + query = AccountInfoQuery().set_account_id(account_id) info = query.execute(client) return _build_account_info_response(info) diff --git a/tck/response/account.py b/tck/response/account.py index 495313952..6f9da6ae9 100644 --- a/tck/response/account.py +++ b/tck/response/account.py @@ -1,7 +1,7 @@ from __future__ import annotations -from dataclasses import dataclass from typing import List, Optional +from dataclasses import dataclass, field @dataclass @@ -36,14 +36,24 @@ class GetAccountInfoResponse: accountId: str | None = None contractAccountId: str | None = None isDeleted: bool | None = None + proxyAccountId: str | None = None proxyReceived: str | None = None key: str | None = None balance: str | None = None + sendRecordThreshold: str | None = None + receiveRecordThreshold: str | None = None isReceiverSignatureRequired: bool | None = None expirationTime: str | None = None autoRenewPeriod: str | None = None - tokenRelationships: List[TokenRelationshipResponse] | None = None + liveHashes: list[dict] = field(default_factory=list) + tokenRelationships: dict[str, TokenRelationshipResponse] = field(default_factory=dict) accountMemo: str | None = None ownedNfts: str | None = None maxAutomaticTokenAssociations: str | None = None + aliasKey: str | None = None + ledgerId: str | None = None + ethereumNonce: str | None = None + hbarAllowances: list[dict] = field(default_factory=list) + tokenAllowances: list[dict] = field(default_factory=list) + nftAllowances: list[dict] = field(default_factory=list) stakingInfo: StakingInfoResponse | None = None diff --git a/tests/tck/test_account_info.py b/tests/tck/test_account_info.py new file mode 100644 index 000000000..6a31877cc --- /dev/null +++ b/tests/tck/test_account_info.py @@ -0,0 +1,170 @@ +import importlib +from unittest.mock import MagicMock, patch + +import pytest + +from hiero_sdk_python.Duration import Duration +from hiero_sdk_python.account.account_id import AccountId +from hiero_sdk_python.account.account_info import AccountInfo +from hiero_sdk_python.crypto.private_key import PrivateKey +from hiero_sdk_python.exceptions import PrecheckError +from hiero_sdk_python.hbar import Hbar +from hiero_sdk_python.response_code import ResponseCode +from hiero_sdk_python.staking_info import StakingInfo +from hiero_sdk_python.timestamp import Timestamp +from hiero_sdk_python.tokens.token_freeze_status import TokenFreezeStatus +from hiero_sdk_python.tokens.token_id import TokenId +from hiero_sdk_python.tokens.token_kyc_status import TokenKycStatus +from hiero_sdk_python.tokens.token_relationship import TokenRelationship +from tck.errors import HIERO_ERROR, JsonRpcError +from tck.handlers import registry +from tck.handlers.registry import dispatch +from tck.param.account import GetAccountInfoParams +from tck.util import client_utils + +pytestmark = pytest.mark.unit + + +@pytest.fixture(autouse=True) +def clear_registry_and_clients(): + registry._HANDLERS.clear() + client_utils._CLIENTS.clear() + + import tck.handlers.account as account_handlers + + importlib.reload(account_handlers) + yield + + registry._HANDLERS.clear() + client_utils._CLIENTS.clear() + + +@pytest.fixture +def params_dict(): + return {"accountId": "0.0.123", "sessionId": "sess1"} + + +def test_parse_json_params_success(params_dict): + params = GetAccountInfoParams.parse_json_params(params_dict) + + assert params.accountId == "0.0.123" + assert params.sessionId == "sess1" + + +def test_parse_json_params_missing_account_id_defaults_to_none(): + params = GetAccountInfoParams.parse_json_params({"sessionId": "sess1"}) + + assert params.accountId is None + assert params.sessionId == "sess1" + + +@patch("tck.handlers.account.get_client") +@patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") +def test_get_account_info_success_mapping(mock_execute, mock_get_client, params_dict): + mock_get_client.return_value = MagicMock() + + key = PrivateKey.generate_ed25519().public_key() + expected_key = key.to_string_der() + + mock_execute.return_value = AccountInfo( + account_id=AccountId.from_string("0.0.123"), + contract_account_id="000000000000000000000000000000000000007b", + is_deleted=False, + proxy_received=Hbar.from_tinybars(10), + key=key, + balance=Hbar.from_tinybars(1000), + receiver_signature_required=True, + expiration_time=Timestamp(1700000000, 123), + auto_renew_period=Duration(7776000), + token_relationships=[ + TokenRelationship( + token_id=TokenId(0, 0, 456), + symbol="TOK", + balance=12, + kyc_status=TokenKycStatus.GRANTED, + freeze_status=TokenFreezeStatus.UNFROZEN, + decimals=2, + automatic_association=True, + ) + ], + account_memo="memo", + owned_nfts=2, + max_automatic_token_associations=11, + staking_info=StakingInfo( + decline_reward=False, + stake_period_start=Timestamp(1700000001, 0), + pending_reward=Hbar.from_tinybars(3), + staked_to_me=Hbar.from_tinybars(4), + staked_account_id=AccountId.from_string("0.0.7"), + ), + ) + + response = dispatch("getAccountInfo", params_dict) + + assert response["accountId"] == "0.0.123" + assert response["contractAccountId"] == "000000000000000000000000000000000000007b" + assert response["isDeleted"] is False + assert response["proxyReceived"] == "10" + assert response["key"] == expected_key + assert response["balance"] == "1000" + assert response["sendRecordThreshold"] == "0" + assert response["receiveRecordThreshold"] == "0" + assert response["isReceiverSignatureRequired"] is True + assert response["expirationTime"] == "1700000000.000000123" + assert response["autoRenewPeriod"] == "7776000" + assert response["tokenRelationships"]["0.0.456"] == { + "tokenId": "0.0.456", + "symbol": "TOK", + "balance": "12", + "kycStatus": "GRANTED", + "freezeStatus": "UNFROZEN", + "decimals": "2", + "automaticAssociation": True, + } + assert response["accountMemo"] == "memo" + assert response["ownedNfts"] == "2" + assert response["maxAutomaticTokenAssociations"] == "11" + assert response["aliasKey"] == "" + assert response["ledgerId"] == "" + assert response["ethereumNonce"] == "0" + assert response["liveHashes"] == [] + assert response["hbarAllowances"] == [] + assert response["tokenAllowances"] == [] + assert response["nftAllowances"] == [] + assert response["stakingInfo"] == { + "declineStakingReward": False, + "stakePeriodStart": "1700000001.000000000", + "pendingReward": "3", + "stakedToMe": "4", + "stakedAccountId": "0.0.7", + "stakedNodeId": None, + } + + +def test_get_account_info_missing_account_id_maps_to_hiero_error(): + with pytest.raises(JsonRpcError) as exception: + dispatch("getAccountInfo", {"sessionId": "sess1"}) + + assert exception.value.code == HIERO_ERROR + assert exception.value.data == {"status": ResponseCode.INVALID_ACCOUNT_ID.name} + + +def test_get_account_info_invalid_account_id_maps_to_hiero_error(): + with pytest.raises(JsonRpcError) as exception: + dispatch("getAccountInfo", {"sessionId": "sess1", "accountId": "invalid-id"}) + + assert exception.value.code == HIERO_ERROR + assert exception.value.data == {"status": ResponseCode.INVALID_ACCOUNT_ID.name} + + +@patch("tck.handlers.account.get_client") +@patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") +def test_get_account_info_precheck_error_maps_to_hiero_error(mock_execute, mock_get_client): + mock_get_client.return_value = MagicMock() + mock_execute.side_effect = PrecheckError(status=ResponseCode.ACCOUNT_DELETED) + + with pytest.raises(JsonRpcError) as exception: + dispatch("getAccountInfo", {"sessionId": "sess1", "accountId": "0.0.123"}) + + assert exception.value.code == HIERO_ERROR + assert exception.value.data == {"status": ResponseCode.ACCOUNT_DELETED.name} From bc794052194128aef8b6f23da10099b7e7119137 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Tue, 7 Apr 2026 01:05:09 +0530 Subject: [PATCH 03/10] fixed codacy's review and suggestions. Signed-off-by: Adityarya11 --- tests/tck/test_account_info.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/tck/test_account_info.py b/tests/tck/test_account_info.py index 6a31877cc..c0ceec085 100644 --- a/tests/tck/test_account_info.py +++ b/tests/tck/test_account_info.py @@ -1,12 +1,13 @@ -import importlib +"""Unit tests for the GetAccountInfo TCK endpoint.""" + from unittest.mock import MagicMock, patch import pytest -from hiero_sdk_python.Duration import Duration from hiero_sdk_python.account.account_id import AccountId from hiero_sdk_python.account.account_info import AccountInfo from hiero_sdk_python.crypto.private_key import PrivateKey +from hiero_sdk_python.Duration import Duration from hiero_sdk_python.exceptions import PrecheckError from hiero_sdk_python.hbar import Hbar from hiero_sdk_python.response_code import ResponseCode @@ -18,6 +19,7 @@ from hiero_sdk_python.tokens.token_relationship import TokenRelationship from tck.errors import HIERO_ERROR, JsonRpcError from tck.handlers import registry +from tck.handlers.account import get_account_info from tck.handlers.registry import dispatch from tck.param.account import GetAccountInfoParams from tck.util import client_utils @@ -26,13 +28,13 @@ @pytest.fixture(autouse=True) -def clear_registry_and_clients(): +def setup_registry_and_clients(): + """Reset handler and client registries for isolated test execution.""" registry._HANDLERS.clear() client_utils._CLIENTS.clear() - import tck.handlers.account as account_handlers + registry.rpc_method("getAccountInfo")(get_account_info) - importlib.reload(account_handlers) yield registry._HANDLERS.clear() @@ -41,10 +43,12 @@ def clear_registry_and_clients(): @pytest.fixture def params_dict(): + """Provide a valid getAccountInfo request payload.""" return {"accountId": "0.0.123", "sessionId": "sess1"} def test_parse_json_params_success(params_dict): + """parse_json_params should parse both accountId and sessionId.""" params = GetAccountInfoParams.parse_json_params(params_dict) assert params.accountId == "0.0.123" @@ -52,6 +56,7 @@ def test_parse_json_params_success(params_dict): def test_parse_json_params_missing_account_id_defaults_to_none(): + """parse_json_params should allow missing accountId and keep it None.""" params = GetAccountInfoParams.parse_json_params({"sessionId": "sess1"}) assert params.accountId is None @@ -61,6 +66,7 @@ def test_parse_json_params_missing_account_id_defaults_to_none(): @patch("tck.handlers.account.get_client") @patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") def test_get_account_info_success_mapping(mock_execute, mock_get_client, params_dict): + """Endpoint should map AccountInfo response fields to TCK response shape.""" mock_get_client.return_value = MagicMock() key = PrivateKey.generate_ed25519().public_key() @@ -142,6 +148,7 @@ def test_get_account_info_success_mapping(mock_execute, mock_get_client, params_ def test_get_account_info_missing_account_id_maps_to_hiero_error(): + """Missing accountId should map to HIERO_ERROR with INVALID_ACCOUNT_ID.""" with pytest.raises(JsonRpcError) as exception: dispatch("getAccountInfo", {"sessionId": "sess1"}) @@ -150,6 +157,7 @@ def test_get_account_info_missing_account_id_maps_to_hiero_error(): def test_get_account_info_invalid_account_id_maps_to_hiero_error(): + """Malformed accountId should map to HIERO_ERROR with INVALID_ACCOUNT_ID.""" with pytest.raises(JsonRpcError) as exception: dispatch("getAccountInfo", {"sessionId": "sess1", "accountId": "invalid-id"}) @@ -160,6 +168,7 @@ def test_get_account_info_invalid_account_id_maps_to_hiero_error(): @patch("tck.handlers.account.get_client") @patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") def test_get_account_info_precheck_error_maps_to_hiero_error(mock_execute, mock_get_client): + """SDK PrecheckError should map to HIERO_ERROR preserving response status.""" mock_get_client.return_value = MagicMock() mock_execute.side_effect = PrecheckError(status=ResponseCode.ACCOUNT_DELETED) From 5fec559145ecd711f33bfc601c8a988a8d03dc22 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Mon, 13 Apr 2026 21:57:29 +0530 Subject: [PATCH 04/10] removed test and added ruff lints Signed-off-by: Adityarya11 --- tck/handlers/account.py | 21 ++-- tck/param/account.py | 10 +- tck/response/account.py | 11 +- tests/tck/test_account_info.py | 179 --------------------------------- 4 files changed, 27 insertions(+), 194 deletions(-) delete mode 100644 tests/tck/test_account_info.py diff --git a/tck/handlers/account.py b/tck/handlers/account.py index f67f601f5..6d43c443b 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -1,13 +1,14 @@ +"""TCK RPC handlers for account-related endpoints.""" + from __future__ import annotations from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction from hiero_sdk_python.account.account_id import AccountId from hiero_sdk_python.account.account_info import AccountInfo -from hiero_sdk_python.query.account_info_query import AccountInfoQuery from hiero_sdk_python.hbar import Hbar +from hiero_sdk_python.query.account_info_query import AccountInfoQuery from hiero_sdk_python.response_code import ResponseCode from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt -from tck.errors import JsonRpcError from tck.handlers.registry import rpc_method from tck.param.account import CreateAccountParams, GetAccountInfoParams from tck.response.account import ( @@ -59,6 +60,7 @@ def _build_create_account_transaction(params: CreateAccountParams) -> AccountCre @rpc_method("createAccount") def create_account(params: CreateAccountParams) -> CreateAccountResponse: + """Create a new account using TCK createAccount parameters.""" client = get_client(params.sessionId) transaction = _build_create_account_transaction(params) @@ -176,19 +178,12 @@ def _build_account_info_response(info: AccountInfo) -> GetAccountInfoResponse: @rpc_method("getAccountInfo") def get_account_info(params: GetAccountInfoParams) -> GetAccountInfoResponse: + """Query account info and map SDK fields to the TCK response contract.""" client = get_client(params.sessionId) - if not params.accountId: - raise JsonRpcError.hiero_error({"status": ResponseCode.INVALID_ACCOUNT_ID.name}) - - try: - account_id = AccountId.from_string(params.accountId) - except (TypeError, ValueError) as error: - raise JsonRpcError.hiero_error( - {"status": ResponseCode.INVALID_ACCOUNT_ID.name} - ) from error - - query = AccountInfoQuery().set_account_id(account_id) + query = AccountInfoQuery().set_grpc_deadline(DEFAULT_GRPC_TIMEOUT) + if params.accountId is not None: + query.set_account_id(AccountId.from_string(params.accountId)) info = query.execute(client) return _build_account_info_response(info) diff --git a/tck/param/account.py b/tck/param/account.py index 14b7e07d9..de8047b23 100644 --- a/tck/param/account.py +++ b/tck/param/account.py @@ -1,3 +1,5 @@ +"""TCK request parameter models for account endpoints.""" + from __future__ import annotations from dataclasses import dataclass @@ -13,6 +15,8 @@ @dataclass class CreateAccountParams(BaseTransactionParams): + """Request parameters for the createAccount endpoint.""" + key: str | None = None initialBalance: int | None = None receiverSignatureRequired: bool | None = None @@ -26,6 +30,7 @@ class CreateAccountParams(BaseTransactionParams): @classmethod def parse_json_params(cls, params: dict) -> CreateAccountParams: + """Parse JSON-RPC params into a CreateAccountParams instance.""" return cls( key=params.get("key"), initialBalance=to_int(params.get("initialBalance")), @@ -44,8 +49,11 @@ def parse_json_params(cls, params: dict) -> CreateAccountParams: @dataclass class GetAccountInfoParams(BaseParams): + """Request parameters for the getAccountInfo endpoint.""" + accountId: str | None = None @classmethod - def parse_json_params(cls, params: dict) -> "GetAccountInfoParams": + def parse_json_params(cls, params: dict) -> GetAccountInfoParams: + """Parse JSON-RPC params into a GetAccountInfoParams instance.""" return cls(accountId=params.get("accountId"), sessionId=parse_session_id(params)) diff --git a/tck/response/account.py b/tck/response/account.py index 6f9da6ae9..f316ab1c5 100644 --- a/tck/response/account.py +++ b/tck/response/account.py @@ -1,17 +1,22 @@ +"""TCK response models for account endpoints.""" + from __future__ import annotations -from typing import List, Optional from dataclasses import dataclass, field @dataclass class CreateAccountResponse: + """Response payload for createAccount.""" + accountId: str | None = None status: str | None = None @dataclass class StakingInfoResponse: + """Nested staking fields in the getAccountInfo response.""" + declineStakingReward: bool | None = None stakePeriodStart: str | None = None pendingReward: str | None = None @@ -22,6 +27,8 @@ class StakingInfoResponse: @dataclass class TokenRelationshipResponse: + """Nested token relationship details for getAccountInfo.""" + tokenId: str | None = None symbol: str | None = None balance: str | None = None @@ -33,6 +40,8 @@ class TokenRelationshipResponse: @dataclass class GetAccountInfoResponse: + """Response payload for getAccountInfo.""" + accountId: str | None = None contractAccountId: str | None = None isDeleted: bool | None = None diff --git a/tests/tck/test_account_info.py b/tests/tck/test_account_info.py deleted file mode 100644 index c0ceec085..000000000 --- a/tests/tck/test_account_info.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Unit tests for the GetAccountInfo TCK endpoint.""" - -from unittest.mock import MagicMock, patch - -import pytest - -from hiero_sdk_python.account.account_id import AccountId -from hiero_sdk_python.account.account_info import AccountInfo -from hiero_sdk_python.crypto.private_key import PrivateKey -from hiero_sdk_python.Duration import Duration -from hiero_sdk_python.exceptions import PrecheckError -from hiero_sdk_python.hbar import Hbar -from hiero_sdk_python.response_code import ResponseCode -from hiero_sdk_python.staking_info import StakingInfo -from hiero_sdk_python.timestamp import Timestamp -from hiero_sdk_python.tokens.token_freeze_status import TokenFreezeStatus -from hiero_sdk_python.tokens.token_id import TokenId -from hiero_sdk_python.tokens.token_kyc_status import TokenKycStatus -from hiero_sdk_python.tokens.token_relationship import TokenRelationship -from tck.errors import HIERO_ERROR, JsonRpcError -from tck.handlers import registry -from tck.handlers.account import get_account_info -from tck.handlers.registry import dispatch -from tck.param.account import GetAccountInfoParams -from tck.util import client_utils - -pytestmark = pytest.mark.unit - - -@pytest.fixture(autouse=True) -def setup_registry_and_clients(): - """Reset handler and client registries for isolated test execution.""" - registry._HANDLERS.clear() - client_utils._CLIENTS.clear() - - registry.rpc_method("getAccountInfo")(get_account_info) - - yield - - registry._HANDLERS.clear() - client_utils._CLIENTS.clear() - - -@pytest.fixture -def params_dict(): - """Provide a valid getAccountInfo request payload.""" - return {"accountId": "0.0.123", "sessionId": "sess1"} - - -def test_parse_json_params_success(params_dict): - """parse_json_params should parse both accountId and sessionId.""" - params = GetAccountInfoParams.parse_json_params(params_dict) - - assert params.accountId == "0.0.123" - assert params.sessionId == "sess1" - - -def test_parse_json_params_missing_account_id_defaults_to_none(): - """parse_json_params should allow missing accountId and keep it None.""" - params = GetAccountInfoParams.parse_json_params({"sessionId": "sess1"}) - - assert params.accountId is None - assert params.sessionId == "sess1" - - -@patch("tck.handlers.account.get_client") -@patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") -def test_get_account_info_success_mapping(mock_execute, mock_get_client, params_dict): - """Endpoint should map AccountInfo response fields to TCK response shape.""" - mock_get_client.return_value = MagicMock() - - key = PrivateKey.generate_ed25519().public_key() - expected_key = key.to_string_der() - - mock_execute.return_value = AccountInfo( - account_id=AccountId.from_string("0.0.123"), - contract_account_id="000000000000000000000000000000000000007b", - is_deleted=False, - proxy_received=Hbar.from_tinybars(10), - key=key, - balance=Hbar.from_tinybars(1000), - receiver_signature_required=True, - expiration_time=Timestamp(1700000000, 123), - auto_renew_period=Duration(7776000), - token_relationships=[ - TokenRelationship( - token_id=TokenId(0, 0, 456), - symbol="TOK", - balance=12, - kyc_status=TokenKycStatus.GRANTED, - freeze_status=TokenFreezeStatus.UNFROZEN, - decimals=2, - automatic_association=True, - ) - ], - account_memo="memo", - owned_nfts=2, - max_automatic_token_associations=11, - staking_info=StakingInfo( - decline_reward=False, - stake_period_start=Timestamp(1700000001, 0), - pending_reward=Hbar.from_tinybars(3), - staked_to_me=Hbar.from_tinybars(4), - staked_account_id=AccountId.from_string("0.0.7"), - ), - ) - - response = dispatch("getAccountInfo", params_dict) - - assert response["accountId"] == "0.0.123" - assert response["contractAccountId"] == "000000000000000000000000000000000000007b" - assert response["isDeleted"] is False - assert response["proxyReceived"] == "10" - assert response["key"] == expected_key - assert response["balance"] == "1000" - assert response["sendRecordThreshold"] == "0" - assert response["receiveRecordThreshold"] == "0" - assert response["isReceiverSignatureRequired"] is True - assert response["expirationTime"] == "1700000000.000000123" - assert response["autoRenewPeriod"] == "7776000" - assert response["tokenRelationships"]["0.0.456"] == { - "tokenId": "0.0.456", - "symbol": "TOK", - "balance": "12", - "kycStatus": "GRANTED", - "freezeStatus": "UNFROZEN", - "decimals": "2", - "automaticAssociation": True, - } - assert response["accountMemo"] == "memo" - assert response["ownedNfts"] == "2" - assert response["maxAutomaticTokenAssociations"] == "11" - assert response["aliasKey"] == "" - assert response["ledgerId"] == "" - assert response["ethereumNonce"] == "0" - assert response["liveHashes"] == [] - assert response["hbarAllowances"] == [] - assert response["tokenAllowances"] == [] - assert response["nftAllowances"] == [] - assert response["stakingInfo"] == { - "declineStakingReward": False, - "stakePeriodStart": "1700000001.000000000", - "pendingReward": "3", - "stakedToMe": "4", - "stakedAccountId": "0.0.7", - "stakedNodeId": None, - } - - -def test_get_account_info_missing_account_id_maps_to_hiero_error(): - """Missing accountId should map to HIERO_ERROR with INVALID_ACCOUNT_ID.""" - with pytest.raises(JsonRpcError) as exception: - dispatch("getAccountInfo", {"sessionId": "sess1"}) - - assert exception.value.code == HIERO_ERROR - assert exception.value.data == {"status": ResponseCode.INVALID_ACCOUNT_ID.name} - - -def test_get_account_info_invalid_account_id_maps_to_hiero_error(): - """Malformed accountId should map to HIERO_ERROR with INVALID_ACCOUNT_ID.""" - with pytest.raises(JsonRpcError) as exception: - dispatch("getAccountInfo", {"sessionId": "sess1", "accountId": "invalid-id"}) - - assert exception.value.code == HIERO_ERROR - assert exception.value.data == {"status": ResponseCode.INVALID_ACCOUNT_ID.name} - - -@patch("tck.handlers.account.get_client") -@patch("hiero_sdk_python.query.account_info_query.AccountInfoQuery.execute") -def test_get_account_info_precheck_error_maps_to_hiero_error(mock_execute, mock_get_client): - """SDK PrecheckError should map to HIERO_ERROR preserving response status.""" - mock_get_client.return_value = MagicMock() - mock_execute.side_effect = PrecheckError(status=ResponseCode.ACCOUNT_DELETED) - - with pytest.raises(JsonRpcError) as exception: - dispatch("getAccountInfo", {"sessionId": "sess1", "accountId": "0.0.123"}) - - assert exception.value.code == HIERO_ERROR - assert exception.value.data == {"status": ResponseCode.ACCOUNT_DELETED.name} From f6d3f728e5f5b3c2e2fda35dc762c394dceb7144 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Thu, 16 Apr 2026 10:30:35 +0530 Subject: [PATCH 05/10] fixed pre-commit check Signed-off-by: Adityarya11 --- tck/handlers/account.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tck/handlers/account.py b/tck/handlers/account.py index 6d43c443b..74ab1c8e4 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -146,6 +146,10 @@ def _to_token_relationships_response(info: AccountInfo) -> dict[str, TokenRelati def _build_account_info_response(info: AccountInfo) -> GetAccountInfoResponse: + auto_renew_period_seconds = ( + str(info.auto_renew_period.seconds) if info.auto_renew_period is not None else "0" + ) + return GetAccountInfoResponse( accountId=str(info.account_id) if info.account_id is not None else None, contractAccountId=info.contract_account_id or "", @@ -160,9 +164,7 @@ def _build_account_info_response(info: AccountInfo) -> GetAccountInfoResponse: receiveRecordThreshold="0", isReceiverSignatureRequired=bool(info.receiver_signature_required), expirationTime=str(info.expiration_time) if info.expiration_time is not None else None, - autoRenewPeriod=str(info.auto_renew_period.seconds) - if info.auto_renew_period is not None - else "0", + autoRenewPeriod=auto_renew_period_seconds, tokenRelationships=_to_token_relationships_response(info), accountMemo=info.account_memo or "", ownedNfts=str(info.owned_nfts) if info.owned_nfts is not None else "0", From e6794d72a35c940ba5fdf72ab1bf8ffcaa2cdc02 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Thu, 16 Apr 2026 13:02:22 +0530 Subject: [PATCH 06/10] ruff formatted. Signed-off-by: Adityarya11 --- pyproject.toml | 1 + tck/handlers/account.py | 24 ++++++------------------ 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 83b5161f3..aa9fe8724 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ dev = [ "pytest-cov>=7.0.0,<8", "hypothesis>=6.137.2", "pre-commit>=4.0.0", + "ruff>=0.14.10", ] lint = [ diff --git a/tck/handlers/account.py b/tck/handlers/account.py index 74ab1c8e4..6781fe98c 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -106,21 +106,13 @@ def _to_staking_info_response(info: AccountInfo) -> StakingInfoResponse: staking_info = info.staking_info return StakingInfoResponse( declineStakingReward=staking_info.decline_reward, - stakePeriodStart=str(staking_info.stake_period_start) - if staking_info.stake_period_start is not None - else None, + stakePeriodStart=str(staking_info.stake_period_start) if staking_info.stake_period_start is not None else None, pendingReward=str(staking_info.pending_reward.to_tinybars()) if staking_info.pending_reward is not None else "0", - stakedToMe=str(staking_info.staked_to_me.to_tinybars()) - if staking_info.staked_to_me is not None - else "0", - stakedAccountId=str(staking_info.staked_account_id) - if staking_info.staked_account_id is not None - else None, - stakedNodeId=str(staking_info.staked_node_id) - if staking_info.staked_node_id is not None - else None, + stakedToMe=str(staking_info.staked_to_me.to_tinybars()) if staking_info.staked_to_me is not None else "0", + stakedAccountId=str(staking_info.staked_account_id) if staking_info.staked_account_id is not None else None, + stakedNodeId=str(staking_info.staked_node_id) if staking_info.staked_node_id is not None else None, ) @@ -146,18 +138,14 @@ def _to_token_relationships_response(info: AccountInfo) -> dict[str, TokenRelati def _build_account_info_response(info: AccountInfo) -> GetAccountInfoResponse: - auto_renew_period_seconds = ( - str(info.auto_renew_period.seconds) if info.auto_renew_period is not None else "0" - ) + auto_renew_period_seconds = str(info.auto_renew_period.seconds) if info.auto_renew_period is not None else "0" return GetAccountInfoResponse( accountId=str(info.account_id) if info.account_id is not None else None, contractAccountId=info.contract_account_id or "", isDeleted=bool(info.is_deleted), proxyAccountId="", - proxyReceived=str(info.proxy_received.to_tinybars()) - if info.proxy_received is not None - else "0", + proxyReceived=str(info.proxy_received.to_tinybars()) if info.proxy_received is not None else "0", key=_serialize_key(info.key), balance=str(info.balance.to_tinybars()) if info.balance is not None else "0", sendRecordThreshold="0", From 98430212b43fbb011b4683cb9545fe73f0bb0ba7 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Thu, 16 Apr 2026 15:13:05 +0530 Subject: [PATCH 07/10] ruff removed from pyproject Signed-off-by: Adityarya11 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index aa9fe8724..83b5161f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,6 @@ dev = [ "pytest-cov>=7.0.0,<8", "hypothesis>=6.137.2", "pre-commit>=4.0.0", - "ruff>=0.14.10", ] lint = [ From e1563adb19563b875d44d99b940a865cf1f94106 Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Thu, 16 Apr 2026 23:34:07 +0530 Subject: [PATCH 08/10] removed valueError from accInfoQuery. Signed-off-by: Adityarya11 --- src/hiero_sdk_python/query/account_info_query.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hiero_sdk_python/query/account_info_query.py b/src/hiero_sdk_python/query/account_info_query.py index a1198f99e..141c66638 100644 --- a/src/hiero_sdk_python/query/account_info_query.py +++ b/src/hiero_sdk_python/query/account_info_query.py @@ -52,13 +52,9 @@ def _make_request(self): Query: The protobuf query message. Raises: - ValueError: If the account ID is not set. Exception: If any other error occurs during request construction. """ try: - if not self.account_id: - raise ValueError("Account ID must be set before making the request.") - query_header = self._make_request_header() crypto_info_query = crypto_get_info_pb2.CryptoGetInfoQuery() From 6b0f6147c7c5a8e1648f4d9b754cd5c407b7e61e Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Sat, 18 Apr 2026 10:06:54 +0530 Subject: [PATCH 09/10] fixed test for removing valueError. Signed-off-by: Adityarya11 --- src/hiero_sdk_python/query/account_info_query.py | 3 ++- tests/unit/account_info_query_test.py | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/hiero_sdk_python/query/account_info_query.py b/src/hiero_sdk_python/query/account_info_query.py index 141c66638..9731282d9 100644 --- a/src/hiero_sdk_python/query/account_info_query.py +++ b/src/hiero_sdk_python/query/account_info_query.py @@ -59,7 +59,8 @@ def _make_request(self): crypto_info_query = crypto_get_info_pb2.CryptoGetInfoQuery() crypto_info_query.header.CopyFrom(query_header) - crypto_info_query.accountID.CopyFrom(self.account_id._to_proto()) + if self.account_id is not None: + crypto_info_query.accountID.CopyFrom(self.account_id._to_proto()) query = query_pb2.Query() query.cryptoGetInfo.CopyFrom(crypto_info_query) diff --git a/tests/unit/account_info_query_test.py b/tests/unit/account_info_query_test.py index 204733083..6aa66a89d 100644 --- a/tests/unit/account_info_query_test.py +++ b/tests/unit/account_info_query_test.py @@ -34,14 +34,6 @@ def test_constructor(): assert query.account_id == account_id -def test_execute_fails_with_missing_account_id(mock_client): - """Test request creation with missing Account ID.""" - query = AccountInfoQuery() - - with pytest.raises(ValueError, match=r"Account ID must be set before making the request\."): - query.execute(mock_client) - - def test_get_method(): """Test retrieving the gRPC method for the query.""" query = AccountInfoQuery() From 5d8f6d71035118c17b4605291c3025107524dfaf Mon Sep 17 00:00:00 2001 From: Adityarya11 Date: Tue, 5 May 2026 18:34:27 +0530 Subject: [PATCH 10/10] missing account throws precheckerror, instead of removal Signed-off-by: Adityarya11 --- tck/handlers/account.py | 8 ++------ tests/unit/account_info_query_test.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tck/handlers/account.py b/tck/handlers/account.py index 6781fe98c..f20be2b67 100644 --- a/tck/handlers/account.py +++ b/tck/handlers/account.py @@ -95,13 +95,9 @@ def _serialize_key(key) -> str | None: return key.to_bytes().hex() -def _to_staking_info_response(info: AccountInfo) -> StakingInfoResponse: +def _to_staking_info_response(info: AccountInfo) -> StakingInfoResponse | None: if info.staking_info is None: - return StakingInfoResponse( - declineStakingReward=False, - pendingReward="0", - stakedToMe="0", - ) + return None staking_info = info.staking_info return StakingInfoResponse( diff --git a/tests/unit/account_info_query_test.py b/tests/unit/account_info_query_test.py index 6aa66a89d..a23d984a6 100644 --- a/tests/unit/account_info_query_test.py +++ b/tests/unit/account_info_query_test.py @@ -34,6 +34,31 @@ def test_constructor(): assert query.account_id == account_id +def test_execute_fails_with_missing_account_id(): + """Test request execution with missing Account ID throws PrecheckError.""" + from hiero_sdk_python.exceptions import PrecheckError + + query = AccountInfoQuery() + + response_sequences = [ + [ + response_pb2.Response( + cryptoGetInfo=crypto_get_info_pb2.CryptoGetInfoResponse( + header=response_header_pb2.ResponseHeader( + nodeTransactionPrecheckCode=ResponseCode.INVALID_ACCOUNT_ID, + responseType=ResponseType.ANSWER_ONLY, + cost=0, + ) + ) + ) + ] + ] + with mock_hedera_servers(response_sequences) as client: + with pytest.raises(PrecheckError) as exc_info: + query.execute(client) + assert exc_info.value.status == ResponseCode.INVALID_ACCOUNT_ID + + def test_get_method(): """Test retrieving the gRPC method for the query.""" query = AccountInfoQuery()