Skip to content

Commit 08207a1

Browse files
authored
test: enhance NodeAddress tests with additional coverage (#1302)
Signed-off-by: MonaaEid <[email protected]> Signed-off-by: MontyPokemon <[email protected]>
1 parent c6bff5b commit 08207a1

File tree

3 files changed

+189
-6
lines changed

3 files changed

+189
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
127127
- 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)
128128
- Enhance assignment bot to guard against users with spam PRs `.github/scripts/bot-assignment-check.sh`
129129
- Add CodeRabbit documentation review prompts for docs, sdk_users, and sdk_developers with priorities, philosophy, and edge case checks. ([#1236](https://github.com/hiero-ledger/hiero-sdk-python/issues/1236))
130+
- Enhance NodeAddress tests with additional coverage for proto conversion `tests/unit/node_address_test.py`
130131

131132
### Fixed
132133
- GFI workflow casing

src/hiero_sdk_python/address_book/node_address.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,8 @@ def _to_proto(self):
101101
if self._account_id:
102102
node_address_proto.nodeAccountId.CopyFrom(self._account_id._to_proto())
103103

104-
service_endpoints: List[Endpoint] = []
105104
for endpoint in self._addresses:
106-
service_endpoints.append(endpoint._to_proto())
107-
108-
node_address_proto.serviceEndpoint = service_endpoints
105+
node_address_proto.serviceEndpoint.append(endpoint._to_proto())
109106

110107
return node_address_proto
111108

tests/unit/node_address_test.py

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import binascii
12
import pytest
23
from hiero_sdk_python.account.account_id import AccountId
34
from hiero_sdk_python.address_book.endpoint import Endpoint
45
from hiero_sdk_python.address_book.node_address import NodeAddress
5-
6+
from hiero_sdk_python.hapi.services.basic_types_pb2 import NodeAddress as NodeAddressProto
67
pytestmark = pytest.mark.unit
78

89
def test_init():
@@ -55,4 +56,188 @@ def test_string_representation():
5556
assert "NodeAccountId: 0.0.123" in result
5657
assert "CertHash: 73616d706c652d636572742d68617368" in result # hex representation of sample-cert-hash
5758
assert "NodeId: 1234" in result
58-
assert "PubKey: sample-public-key" in result
59+
assert "PubKey: sample-public-key" in result
60+
61+
62+
def test_to_proto():
63+
"""Test conversion of NodeAddress to protobuf with endpoints."""
64+
account_id = AccountId(0, 0, 123)
65+
endpoints = [
66+
Endpoint(address=bytes("192.168.1.1", "utf-8"), port=8080, domain_name="example1.com"),
67+
Endpoint(address=bytes("192.168.1.2", "utf-8"), port=8081, domain_name="example2.com"),
68+
Endpoint(address=bytes("192.168.1.3", "utf-8"), port=8082, domain_name="example3.com"),
69+
]
70+
node_address = NodeAddress(
71+
public_key="sample-public-key",
72+
account_id=account_id,
73+
node_id=1234,
74+
cert_hash=b"sample-cert-hash",
75+
addresses=endpoints,
76+
description="Sample Node"
77+
)
78+
79+
node_address_proto = node_address._to_proto()
80+
# Protect against breaking changes - verify return type
81+
assert isinstance(node_address_proto, NodeAddressProto)
82+
# Scalars
83+
assert node_address_proto.RSA_PubKey == "sample-public-key"
84+
assert node_address_proto.nodeId == 1234
85+
assert node_address_proto.nodeCertHash == b"sample-cert-hash"
86+
assert node_address_proto.description == "Sample Node"
87+
88+
# AccountId
89+
assert node_address_proto.nodeAccountId.shardNum == 0
90+
assert node_address_proto.nodeAccountId.realmNum == 0
91+
assert node_address_proto.nodeAccountId.accountNum == 123
92+
93+
# ServiceEndpoint
94+
# Verify all endpoints are serialized
95+
assert len(node_address_proto.serviceEndpoint) == 3
96+
for i, ep_proto in enumerate(node_address_proto.serviceEndpoint):
97+
assert ep_proto.ipAddressV4 == endpoints[i]._address
98+
assert ep_proto.port == endpoints[i]._port
99+
assert ep_proto.domain_name == endpoints[i]._domain_name
100+
101+
102+
def test_from_dict():
103+
"""Test creation of NodeAddress from a dictionary with hex cert hash."""
104+
node_dict = {
105+
"public_key": "sample-public-key",
106+
"node_account_id": "0.0.123",
107+
"node_id": 1234,
108+
"node_cert_hash": binascii.hexlify(b"sample-cert-hash").decode("utf-8"),
109+
"description": "Sample Node",
110+
"service_endpoints": [
111+
{"ip_address_v4": "192.168.1.1", "port": 8080, "domain_name": "example.com"}
112+
],
113+
}
114+
115+
# Create NodeAddress from dict
116+
node_address = NodeAddress._from_dict(node_dict)
117+
118+
# Protect against breaking changes - verify return type
119+
assert isinstance(node_address, NodeAddress)
120+
121+
assert node_address._public_key == "sample-public-key"
122+
assert node_address._account_id == AccountId.from_string("0.0.123")
123+
assert node_address._node_id == 1234
124+
assert node_address._cert_hash == b"sample-cert-hash"
125+
assert node_address._description == "Sample Node"
126+
assert len(node_address._addresses) == 1
127+
128+
129+
def test_from_dict_with_0x_prefix():
130+
"""Test _from_dict handles cert hash with 0x prefix."""
131+
node_dict = {
132+
"public_key": "sample-public-key",
133+
"node_account_id": "0.0.123",
134+
"node_id": 1234,
135+
"node_cert_hash": "0x" + binascii.hexlify(b"sample-cert-hash").decode("utf-8"),
136+
"description": "Sample Node",
137+
"service_endpoints": [],
138+
}
139+
140+
node_address = NodeAddress._from_dict(node_dict)
141+
142+
assert node_address._cert_hash == b"sample-cert-hash"
143+
144+
145+
def test_from_proto():
146+
"""Test creation of NodeAddress from protobuf with endpoint."""
147+
account_id_proto = AccountId(0, 0, 123)._to_proto()
148+
endpoint_proto = Endpoint(
149+
address=bytes("192.168.1.1", "utf-8"),
150+
port=8080,
151+
domain_name="example.com"
152+
)._to_proto()
153+
154+
# Create NodeAddressProto
155+
node_address_proto = NodeAddressProto(
156+
RSA_PubKey="sample-public-key",
157+
nodeAccountId=account_id_proto,
158+
nodeId=1234,
159+
nodeCertHash=b"sample-cert-hash",
160+
description="Sample Node",
161+
)
162+
node_address_proto.serviceEndpoint.append(endpoint_proto)
163+
164+
node_address = NodeAddress._from_proto(node_address_proto)
165+
166+
# Protect against breaking changes - verify return type
167+
assert isinstance(node_address, NodeAddress)
168+
169+
assert node_address._public_key == "sample-public-key"
170+
assert node_address._account_id == AccountId(0, 0, 123)
171+
assert node_address._node_id == 1234
172+
assert node_address._cert_hash == b"sample-cert-hash"
173+
assert node_address._description == "Sample Node"
174+
assert len(node_address._addresses) == 1
175+
176+
177+
def test_round_trip():
178+
"""Ensure NodeAddress → Proto → NodeAddress round trip works."""
179+
account_id = AccountId(0, 0, 123)
180+
endpoint = Endpoint(
181+
address=bytes("192.168.1.1", "utf-8"),
182+
port=8080,
183+
domain_name="example.com"
184+
)
185+
186+
# Create NodeAddress
187+
node_address = NodeAddress(
188+
public_key="sample-public-key",
189+
account_id=account_id,
190+
node_id=1234,
191+
cert_hash=b"sample-cert-hash",
192+
addresses=[endpoint],
193+
description="Sample Node"
194+
)
195+
196+
# Convert to proto
197+
proto = node_address._to_proto()
198+
# Convert back from proto
199+
node_address2 = NodeAddress._from_proto(proto)
200+
201+
# Assert all fields are equal
202+
assert node_address._public_key == node_address2._public_key
203+
assert node_address._account_id == node_address2._account_id
204+
assert node_address._node_id == node_address2._node_id
205+
assert node_address._cert_hash == node_address2._cert_hash
206+
assert node_address._description == node_address2._description
207+
# Verify addresses match
208+
assert len(node_address._addresses) == len(node_address2._addresses)
209+
for i, endpoint in enumerate(node_address._addresses):
210+
assert endpoint._address == node_address2._addresses[i]._address
211+
assert endpoint._port == node_address2._addresses[i]._port
212+
assert endpoint._domain_name == node_address2._addresses[i]._domain_name
213+
214+
215+
def test_empty_addresses():
216+
"""Test NodeAddress with no endpoints produces empty serviceEndpoint."""
217+
node_address = NodeAddress(
218+
public_key="sample-public-key",
219+
account_id=AccountId(0, 0, 123),
220+
node_id=1234,
221+
cert_hash=b"sample-cert-hash",
222+
addresses=[],
223+
description="No endpoints"
224+
)
225+
226+
proto = node_address._to_proto()
227+
assert len(proto.serviceEndpoint) == 0
228+
229+
def test_to_proto_none_account_id():
230+
"""Test _to_proto handles None account_id gracefully."""
231+
node_address = NodeAddress(
232+
public_key="sample-public-key",
233+
account_id=None,
234+
node_id=1234,
235+
cert_hash=b"sample-cert-hash",
236+
addresses=[],
237+
description="No account"
238+
)
239+
240+
proto = node_address._to_proto()
241+
242+
# Should not have nodeAccountId set
243+
assert not proto.HasField('nodeAccountId')

0 commit comments

Comments
 (0)