From 50899f7481d14fd29120c2b61e532c9d42d1a2bb Mon Sep 17 00:00:00 2001 From: Manish Dait Date: Sun, 19 Jul 2026 01:01:40 +0530 Subject: [PATCH 1/3] chore: fix the build_proto to rely on precheck error instead of value error Signed-off-by: Manish Dait --- .../tokens/token_dissociate_transaction.py | 12 +-- .../unit/token_dissociate_transaction_test.py | 76 +++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/hiero_sdk_python/tokens/token_dissociate_transaction.py b/src/hiero_sdk_python/tokens/token_dissociate_transaction.py index 1957de2c2..c528a442f 100644 --- a/src/hiero_sdk_python/tokens/token_dissociate_transaction.py +++ b/src/hiero_sdk_python/tokens/token_dissociate_transaction.py @@ -102,12 +102,14 @@ def _build_proto_body(self) -> token_dissociate_pb2.TokenDissociateTransactionBo Raises: ValueError: If account ID or token IDs are not set. """ - if not self.account_id or not self.token_ids: - raise ValueError("Account ID and token IDs must be set.") + transaction_body = token_dissociate_pb2.TokenDissociateTransactionBody() - return token_dissociate_pb2.TokenDissociateTransactionBody( - account=self.account_id._to_proto(), tokens=[token_id._to_proto() for token_id in self.token_ids] - ) + if self.account_id is not None: + transaction_body.account.CopyFrom(self.account_id._to_proto()) + if self.token_ids is not None: + transaction_body.tokens.extend(token_id._to_proto() for token_id in self.token_ids if token_id is not None) + + return transaction_body def build_transaction_body(self) -> transaction_pb2.TransactionBody: """ diff --git a/tests/unit/token_dissociate_transaction_test.py b/tests/unit/token_dissociate_transaction_test.py index ea354bad0..5d920b26d 100644 --- a/tests/unit/token_dissociate_transaction_test.py +++ b/tests/unit/token_dissociate_transaction_test.py @@ -191,3 +191,79 @@ def test_build_scheduled_body(mock_account_ids): assert len(schedulable_body.tokenDissociate.tokens) == len(token_ids) for i, token_id in enumerate(token_ids): assert schedulable_body.tokenDissociate.tokens[i] == token_id._to_proto() + + +def test_build_protobuf_body(mock_account_ids): + """Test build protobuf body for a token dissociate transaction.""" + account_id, _, _, token_id_1, token_id_2 = mock_account_ids + token_ids = [token_id_1, token_id_2] + + tx = TokenDissociateTransaction().set_account_id(account_id).set_token_ids(token_ids) + + body = tx._build_proto_body() + + assert body is not None + assert body.HasField("account") + assert body.account == account_id._to_proto() + + assert len(body.tokens) == len(token_ids) + assert list(body.tokens) == [token._to_proto() for token in token_ids] + + +def test_build_protobuf_body_ignore_none_tokens(mock_account_ids): + """Test build protobuf body for a token dissociate transaction ignores none tokenId in list.""" + account_id, _, _, token_id, _ = mock_account_ids + token_ids = [token_id, None] + + tx = TokenDissociateTransaction().set_account_id(account_id).set_token_ids(token_ids) + + body = tx._build_proto_body() + + assert body is not None + assert body.HasField("account") + assert body.account == account_id._to_proto() + + assert len(body.tokens) == 1 + assert list(body.tokens) == [token_id._to_proto()] + + +def test_build_protobuf_body_without_account_id(mock_account_ids): + """Test build protobuf body for a token dissociate transaction without accountId.""" + _, _, _, token_id_1, token_id_2 = mock_account_ids + token_ids = [token_id_1, token_id_2] + + tx = TokenDissociateTransaction().set_token_ids(token_ids) + + body = tx._build_proto_body() + + assert body is not None + assert not body.HasField("account") + + assert len(body.tokens) == len(token_ids) + assert list(body.tokens) == [token._to_proto() for token in token_ids] + + +def test_build_protobuf_body_without_token_ids(mock_account_ids): + """Test build protobuf body for a token dissociate transaction without tokenIds.""" + account_id, _, _, _, _ = mock_account_ids + + tx = TokenDissociateTransaction().set_account_id(account_id) + + body = tx._build_proto_body() + + assert body is not None + assert body.HasField("account") + assert body.account == account_id._to_proto() + + assert len(body.tokens) == 0 + + +def test_build_protobuf_body_missing_both_account_and_token_ids(): + """Test build protobuf body for a token dissociate transaction without accountId and tokenIds.""" + tx = TokenDissociateTransaction() + + body = tx._build_proto_body() + + assert body is not None + assert not body.HasField("account") + assert len(body.tokens) == 0 From 877db028a33d07738531ffcda695b08e38f9e05a Mon Sep 17 00:00:00 2001 From: Manish Dait Date: Sun, 19 Jul 2026 01:33:03 +0530 Subject: [PATCH 2/3] chore: update docsting Signed-off-by: Manish Dait --- src/hiero_sdk_python/tokens/token_dissociate_transaction.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hiero_sdk_python/tokens/token_dissociate_transaction.py b/src/hiero_sdk_python/tokens/token_dissociate_transaction.py index c528a442f..77c1f4571 100644 --- a/src/hiero_sdk_python/tokens/token_dissociate_transaction.py +++ b/src/hiero_sdk_python/tokens/token_dissociate_transaction.py @@ -98,9 +98,6 @@ def _build_proto_body(self) -> token_dissociate_pb2.TokenDissociateTransactionBo Returns: TokenDissociateTransactionBody: The protobuf body for this transaction. - - Raises: - ValueError: If account ID or token IDs are not set. """ transaction_body = token_dissociate_pb2.TokenDissociateTransactionBody() From bbdea50aa77d1ae1fc5af4e7e541e2e9c1477373 Mon Sep 17 00:00:00 2001 From: Manish Dait Date: Sun, 19 Jul 2026 02:01:19 +0530 Subject: [PATCH 3/3] chore: added e2e test Signed-off-by: Manish Dait --- .../token_dissociate_transaction_e2e_test.py | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/tests/integration/token_dissociate_transaction_e2e_test.py b/tests/integration/token_dissociate_transaction_e2e_test.py index 894b371db..28ce5eac8 100644 --- a/tests/integration/token_dissociate_transaction_e2e_test.py +++ b/tests/integration/token_dissociate_transaction_e2e_test.py @@ -4,53 +4,81 @@ from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction 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.tokens.token_associate_transaction import TokenAssociateTransaction from hiero_sdk_python.tokens.token_dissociate_transaction import TokenDissociateTransaction -from tests.integration.utils import IntegrationTestEnv, create_fungible_token +from tests.integration.utils import create_fungible_token + + +def _create_associated_account(env): + """Creates an account and associates it with a fungible token.""" + private_key = PrivateKey.generate() + + receipt = ( + AccountCreateTransaction() + .set_key_without_alias(private_key) + .set_initial_balance(Hbar(2)) + .set_account_memo("Recipient Account") + .freeze_with(env.client) + .execute(env.client) + ) + + account_id = receipt.account_id + token_id = create_fungible_token(env) + + associate_tx = TokenAssociateTransaction().set_account_id(account_id).set_token_ids([token_id]) + associate_tx.freeze_with(env.client) + associate_tx.sign(private_key) + + receipt = associate_tx.execute(env.client) + + assert receipt.status == ResponseCode.SUCCESS, ( + f"Token association failed with status: {ResponseCode(receipt.status).name}" + ) + + return account_id, private_key, token_id @pytest.mark.integration -def test_integration_token_dissociate_transaction_can_execute(): - env = IntegrationTestEnv() +def test_integration_token_dissociate_transaction_can_execute(env): + """Test token dissociate transaction can be executed successfully.""" + account_id, account_private_key, token_id = _create_associated_account(env) - try: - new_account_private_key = PrivateKey.generate() - new_account_public_key = new_account_private_key.public_key() + dissociate_transaction = TokenDissociateTransaction(account_id=account_id, token_ids=[token_id]) + dissociate_transaction.freeze_with(env.client) + dissociate_transaction.sign(account_private_key) - initial_balance = Hbar(2) + receipt = dissociate_transaction.execute(env.client) - assert initial_balance.to_tinybars() == 200000000 + assert receipt.status == ResponseCode.SUCCESS, ( + f"Token dissociation failed with status: {ResponseCode(receipt.status).name}" + ) - account_transaction = AccountCreateTransaction( - key=new_account_public_key, initial_balance=initial_balance, memo="Recipient Account" - ) - account_transaction.freeze_with(env.client) - account_receipt = account_transaction.execute(env.client) - new_account_id = account_receipt.account_id +def test_token_dissociate_transaction_can_execute_with_no_tokensId(env): + """Test token dissociate transaction can be executed without setting the tokenIds.""" + account_id, account_private_key, _ = _create_associated_account(env) - token_id = create_fungible_token(env) + dissociate_transaction = TokenDissociateTransaction(account_id=account_id) + dissociate_transaction.freeze_with(env.client) + dissociate_transaction.sign(account_private_key) - associate_transaction = TokenAssociateTransaction(account_id=new_account_id, token_ids=[token_id]) - associate_transaction.freeze_with(env.client) - associate_transaction.sign(new_account_private_key) + receipt = dissociate_transaction.execute(env.client) - receipt = associate_transaction.execute(env.client) + assert receipt.status == ResponseCode.SUCCESS, ( + f"Token dissociation failed with status: {ResponseCode(receipt.status).name}" + ) - assert receipt.status == ResponseCode.SUCCESS, ( - f"Token association failed with status: {ResponseCode(receipt.status).name}" - ) - dissociate_transaction = TokenDissociateTransaction(account_id=new_account_id, token_ids=[token_id]) - dissociate_transaction.freeze_with(env.client) - dissociate_transaction.sign(new_account_private_key) +def test_token_dissociate_transaction_raise_error_if_account_id_not_set(env): + """Test token dissociate transaction raises PrecheckError if accountId not set.""" + a_, account_private_key, token_id = _create_associated_account(env) - receipt = dissociate_transaction.execute(env.client) + dissociate_transaction = TokenDissociateTransaction(token_ids=[token_id]) + dissociate_transaction.freeze_with(env.client) + dissociate_transaction.sign(account_private_key) - assert receipt.status == ResponseCode.SUCCESS, ( - f"Token dissociation failed with status: {ResponseCode(receipt.status).name}" - ) - finally: - env.close() + with pytest.raises(PrecheckError): + dissociate_transaction.execute(env.client)