1+ import binascii
12import pytest
23from hiero_sdk_python .account .account_id import AccountId
34from hiero_sdk_python .address_book .endpoint import Endpoint
45from hiero_sdk_python .address_book .node_address import NodeAddress
5-
6+ from hiero_sdk_python . hapi . services . basic_types_pb2 import NodeAddress as NodeAddressProto
67pytestmark = pytest .mark .unit
78
89def 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