From 473a211da7fa1923fe342b66cb663a9b3fd0eae3 Mon Sep 17 00:00:00 2001 From: MonaaEid Date: Fri, 2 Jan 2026 00:46:51 +0200 Subject: [PATCH 1/4] test: enhance NodeAddress tests with additional coverage for proto conversion Signed-off-by: MonaaEid --- CHANGELOG.md | 1 + .../address_book/node_address.py | 5 +- tests/unit/node_address_test.py | 145 +++++++++++++++++- 3 files changed, 145 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f1340f6..6c739acfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Updated `transfer_transaction_hbar.py` example to use `Hbar` objects instead of raw integers and added receipt checking with `ResponseCode` validation.([#1249](https://github.com/hiero-ledger/hiero-sdk-python/issues/1249)) - Renamed `pr-missing-linked-issue.yml` and `pr_missing_linked_issue.js` to `bot-pr-missing-linked-issue.yml` and `bot-pr-missing-linked-issue.js` respectively. Enhanced LinkBot PR comment with clickable hyperlinks to documentation for linking issues and creating issues. (#1264) - Enhance assignment bot to guard against users with spam PRs `.github/scripts/bot-assignment-check.sh` +- Enhance NodeAddress tests with additional coverage for proto conversion `tests/unit/node_address_test.py` ### Fixed - GFI workflow casing diff --git a/src/hiero_sdk_python/address_book/node_address.py b/src/hiero_sdk_python/address_book/node_address.py index 50660d1eb..da658d7f0 100644 --- a/src/hiero_sdk_python/address_book/node_address.py +++ b/src/hiero_sdk_python/address_book/node_address.py @@ -101,11 +101,8 @@ def _to_proto(self): if self._account_id: node_address_proto.nodeAccountId.CopyFrom(self._account_id._to_proto()) - service_endpoints: List[Endpoint] = [] for endpoint in self._addresses: - service_endpoints.append(endpoint._to_proto()) - - node_address_proto.serviceEndpoint = service_endpoints + node_address_proto.serviceEndpoint.append(endpoint._to_proto()) return node_address_proto diff --git a/tests/unit/node_address_test.py b/tests/unit/node_address_test.py index bd41ffdca..47f0200fc 100644 --- a/tests/unit/node_address_test.py +++ b/tests/unit/node_address_test.py @@ -1,8 +1,11 @@ +from platform import node +import pytest +import binascii import pytest from hiero_sdk_python.account.account_id import AccountId from hiero_sdk_python.address_book.endpoint import Endpoint from hiero_sdk_python.address_book.node_address import NodeAddress - +from hiero_sdk_python.hapi.services.basic_types_pb2 import NodeAddress as NodeAddressProto pytestmark = pytest.mark.unit def test_init(): @@ -55,4 +58,142 @@ def test_string_representation(): assert "NodeAccountId: 0.0.123" in result assert "CertHash: 73616d706c652d636572742d68617368" in result # hex representation of sample-cert-hash assert "NodeId: 1234" in result - assert "PubKey: sample-public-key" in result \ No newline at end of file + assert "PubKey: sample-public-key" in result + + +def test_to_proto(): + """Test conversion of NodeAddress to protobuf with endpoints.""" + account_id = AccountId(0, 0, 123) + endpoint = Endpoint( + address=bytes("192.168.1.1", "utf-8"), + port=8080, + domain_name="example.com" + ) + node_address = NodeAddress( + public_key="sample-public-key", + account_id=account_id, + node_id=1234, + cert_hash=b"sample-cert-hash", + addresses=[endpoint], + description="Sample Node" + ) + + node_address_proto = node_address._to_proto() + + # Scalars + assert node_address_proto.RSA_PubKey == "sample-public-key" + assert node_address_proto.nodeId == 1234 + assert node_address_proto.nodeCertHash == b"sample-cert-hash" + assert node_address_proto.description == "Sample Node" + + # AccountId + assert node_address_proto.nodeAccountId.shardNum == 0 + assert node_address_proto.nodeAccountId.realmNum == 0 + assert node_address_proto.nodeAccountId.accountNum == 123 + + # ServiceEndpoint + assert len(node_address_proto.serviceEndpoint) == 1 + ep = node_address_proto.serviceEndpoint[0] + assert ep.ipAddressV4 == bytes("192.168.1.1", "utf-8") + assert ep.port == 8080 + assert ep.domain_name == "example.com" + + +def test_from_dict(): + """Test creation of NodeAddress from a dictionary with hex cert hash.""" + node_dict = { + "public_key": "sample-public-key", + "node_account_id": "0.0.123", + "node_id": 1234, + "node_cert_hash": binascii.hexlify(b"sample-cert-hash").decode("utf-8"), + "description": "Sample Node", + "service_endpoints": [ + {"ip_address_v4": "192.168.1.1", "port": 8080, "domain_name": "example.com"} + ], + } + + # Create NodeAddress from dict + node_address = NodeAddress._from_dict(node_dict) + + assert node_address._public_key == "sample-public-key" + assert node_address._account_id == AccountId.from_string("0.0.123") + assert node_address._node_id == 1234 + assert node_address._cert_hash == b"sample-cert-hash" + assert node_address._description == "Sample Node" + assert len(node_address._addresses) == 1 + + +def test_from_proto(): + """Test creation of NodeAddress from protobuf with endpoint.""" + account_id_proto = AccountId(0, 0, 123)._to_proto() + endpoint_proto = Endpoint( + address=bytes("192.168.1.1", "utf-8"), + port=8080, + domain_name="example.com" + )._to_proto() + + # Create NodeAddressProto + node_address_proto = NodeAddressProto( + RSA_PubKey="sample-public-key", + nodeAccountId=account_id_proto, + nodeId=1234, + nodeCertHash=b"sample-cert-hash", + description="Sample Node", + ) + node_address_proto.serviceEndpoint.append(endpoint_proto) + + node_address = NodeAddress._from_proto(node_address_proto) + + assert node_address._public_key == "sample-public-key" + assert node_address._account_id == AccountId(0, 0, 123) + assert node_address._node_id == 1234 + assert node_address._cert_hash == b"sample-cert-hash" + assert node_address._description == "Sample Node" + assert len(node_address._addresses) == 1 + + +def test_round_trip(): + """Ensure NodeAddress → Proto → NodeAddress round trip works.""" + account_id = AccountId(0, 0, 123) + endpoint = Endpoint( + address=bytes("192.168.1.1", "utf-8"), + port=8080, + domain_name="example.com" + ) + + # Create NodeAddress + node_address = NodeAddress( + public_key="sample-public-key", + account_id=account_id, + node_id=1234, + cert_hash=b"sample-cert-hash", + addresses=[endpoint], + description="Sample Node" + ) + + # Convert to proto + proto = node_address._to_proto() + # Convert back from proto + node_address2 = NodeAddress._from_proto(proto) + + # Assert all fields are equal + assert node_address._public_key == node_address2._public_key + assert node_address._account_id == node_address2._account_id + assert node_address._node_id == node_address2._node_id + assert node_address._cert_hash == node_address2._cert_hash + assert node_address._description == node_address2._description + + +def test_empty_addresses(): + """Test NodeAddress with no endpoints produces empty serviceEndpoint.""" + node_address = NodeAddress( + public_key="sample-public-key", + account_id=AccountId(0, 0, 123), + node_id=1234, + cert_hash=b"sample-cert-hash", + addresses=[], + description="No endpoints" + ) + + proto = node_address._to_proto() + assert len(proto.serviceEndpoint) == 0 From bf86d58ac3d8f0b3b7c87584b56d79d51b3087b9 Mon Sep 17 00:00:00 2001 From: MonaaEid Date: Fri, 2 Jan 2026 01:18:50 +0200 Subject: [PATCH 2/4] test: enhance NodeAddress tests with additional coverage for proto conversion Signed-off-by: MonaaEid --- tests/unit/node_address_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit/node_address_test.py b/tests/unit/node_address_test.py index 47f0200fc..7d8fc786e 100644 --- a/tests/unit/node_address_test.py +++ b/tests/unit/node_address_test.py @@ -1,5 +1,3 @@ -from platform import node -import pytest import binascii import pytest from hiero_sdk_python.account.account_id import AccountId @@ -182,6 +180,12 @@ def test_round_trip(): assert node_address._node_id == node_address2._node_id assert node_address._cert_hash == node_address2._cert_hash assert node_address._description == node_address2._description + # Verify addresses match + assert len(node_address._addresses) == len(node_address2._addresses) + for i, endpoint in enumerate(node_address._addresses): + assert endpoint._address == node_address2._addresses[i]._address + assert endpoint._port == node_address2._addresses[i]._port + assert endpoint._domain_name == node_address2._addresses[i]._domain_name def test_empty_addresses(): From 33a2b2dcacd66d8955e9f657d09d0e43857da477 Mon Sep 17 00:00:00 2001 From: MonaaEid Date: Fri, 2 Jan 2026 14:25:10 +0200 Subject: [PATCH 3/4] test: enhance NodeAddress tests with additional coverage for proto conversion Signed-off-by: MonaaEid --- tests/unit/node_address_test.py | 58 ++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/tests/unit/node_address_test.py b/tests/unit/node_address_test.py index 7d8fc786e..1c349e9ff 100644 --- a/tests/unit/node_address_test.py +++ b/tests/unit/node_address_test.py @@ -62,22 +62,23 @@ def test_string_representation(): def test_to_proto(): """Test conversion of NodeAddress to protobuf with endpoints.""" account_id = AccountId(0, 0, 123) - endpoint = Endpoint( - address=bytes("192.168.1.1", "utf-8"), - port=8080, - domain_name="example.com" - ) + endpoints = [ + Endpoint(address=bytes("192.168.1.1", "utf-8"), port=8080, domain_name="example1.com"), + Endpoint(address=bytes("192.168.1.2", "utf-8"), port=8081, domain_name="example2.com"), + Endpoint(address=bytes("192.168.1.3", "utf-8"), port=8082, domain_name="example3.com"), + ] node_address = NodeAddress( public_key="sample-public-key", account_id=account_id, node_id=1234, cert_hash=b"sample-cert-hash", - addresses=[endpoint], + addresses=endpoints, description="Sample Node" ) node_address_proto = node_address._to_proto() - + # Protect against breaking changes - verify return type + assert isinstance(node_address_proto, NodeAddressProto) # Scalars assert node_address_proto.RSA_PubKey == "sample-public-key" assert node_address_proto.nodeId == 1234 @@ -90,11 +91,12 @@ def test_to_proto(): assert node_address_proto.nodeAccountId.accountNum == 123 # ServiceEndpoint - assert len(node_address_proto.serviceEndpoint) == 1 - ep = node_address_proto.serviceEndpoint[0] - assert ep.ipAddressV4 == bytes("192.168.1.1", "utf-8") - assert ep.port == 8080 - assert ep.domain_name == "example.com" + # Verify all endpoints are serialized + assert len(node_address_proto.serviceEndpoint) == 3 + for i, ep_proto in enumerate(node_address_proto.serviceEndpoint): + assert ep_proto.ipAddressV4 == endpoints[i]._address + assert ep_proto.port == endpoints[i]._port + assert ep_proto.domain_name == endpoints[i]._domain_name def test_from_dict(): @@ -121,6 +123,22 @@ def test_from_dict(): assert len(node_address._addresses) == 1 +def test_from_dict_with_0x_prefix(): + """Test _from_dict handles cert hash with 0x prefix.""" + node_dict = { + "public_key": "sample-public-key", + "node_account_id": "0.0.123", + "node_id": 1234, + "node_cert_hash": "0x" + binascii.hexlify(b"sample-cert-hash").decode("utf-8"), + "description": "Sample Node", + "service_endpoints": [], + } + + node_address = NodeAddress._from_dict(node_dict) + + assert node_address._cert_hash == b"sample-cert-hash" + + def test_from_proto(): """Test creation of NodeAddress from protobuf with endpoint.""" account_id_proto = AccountId(0, 0, 123)._to_proto() @@ -201,3 +219,19 @@ def test_empty_addresses(): proto = node_address._to_proto() assert len(proto.serviceEndpoint) == 0 + +def test_to_proto_none_account_id(): + """Test _to_proto handles None account_id gracefully.""" + node_address = NodeAddress( + public_key="sample-public-key", + account_id=None, + node_id=1234, + cert_hash=b"sample-cert-hash", + addresses=[], + description="No account" + ) + + proto = node_address._to_proto() + + # Should not have nodeAccountId set + assert not proto.HasField('nodeAccountId') From 35aef35487473ab88d262469018dde55b4659a85 Mon Sep 17 00:00:00 2001 From: MonaaEid Date: Fri, 2 Jan 2026 14:45:11 +0200 Subject: [PATCH 4/4] test: enhance NodeAddress tests with additional coverage for proto conversion Signed-off-by: MonaaEid --- tests/unit/node_address_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unit/node_address_test.py b/tests/unit/node_address_test.py index 1c349e9ff..9191bc9c2 100644 --- a/tests/unit/node_address_test.py +++ b/tests/unit/node_address_test.py @@ -115,6 +115,9 @@ def test_from_dict(): # Create NodeAddress from dict node_address = NodeAddress._from_dict(node_dict) + # Protect against breaking changes - verify return type + assert isinstance(node_address, NodeAddress) + assert node_address._public_key == "sample-public-key" assert node_address._account_id == AccountId.from_string("0.0.123") assert node_address._node_id == 1234 @@ -160,6 +163,9 @@ def test_from_proto(): node_address = NodeAddress._from_proto(node_address_proto) + # Protect against breaking changes - verify return type + assert isinstance(node_address, NodeAddress) + assert node_address._public_key == "sample-public-key" assert node_address._account_id == AccountId(0, 0, 123) assert node_address._node_id == 1234