1+ import pytest
2+ from unittest .mock import MagicMock
3+ from src .hiero_sdk_python .address_book .endpoint import Endpoint
4+
5+ pytestmark = pytest .mark .unit
6+
7+ def test_getter_setter ():
8+
9+ """Test for Endpoint constructor, getters, and setters with fluent interface."""
10+
11+ endpoint = Endpoint (address = None , port = None , domain_name = None )
12+
13+ # Test fluent interface (method chaining)
14+ result = endpoint .set_address (b'127.0.1.1' )
15+ assert result is endpoint , "set_address should return self for method chaining"
16+
17+ result = endpoint .set_port (77777 )
18+ assert result is endpoint , "set_port should return self for method chaining"
19+
20+ result = endpoint .set_domain_name ("redpanda.com" )
21+ assert result is endpoint , "set_domain_name should return self for method chaining"
22+
23+ # Protect against breaking changes - verify attributes exist
24+ assert hasattr (endpoint , 'get_address' ), "Missing get_address method"
25+ assert hasattr (endpoint , 'get_port' ), "Missing get_port method"
26+ assert hasattr (endpoint , 'get_domain_name' ), "Missing get_domain_name method"
27+
28+ assert endpoint .get_address () == b'127.0.1.1'
29+ assert endpoint .get_port () == 77777
30+ assert endpoint .get_domain_name () == "redpanda.com"
31+
32+ def test_serialization_roundtrip ():
33+ """
34+ Verifies that all fields survive a full round-trip conversion:
35+ Endpoint -> Protobuf -> Endpoint.
36+ """
37+ original = Endpoint (address = b'192.168.1.1' , port = 8080 , domain_name = "example.com" )
38+
39+ # Perform round-trip
40+ proto = original ._to_proto ()
41+ roundtrip = Endpoint ._from_proto (proto )
42+
43+ assert roundtrip .get_address () == original .get_address ()
44+ assert roundtrip .get_port () == original .get_port ()
45+ assert roundtrip .get_domain_name () == original .get_domain_name ()
46+
47+ def test_constructor_with_values ():
48+ """Test Endpoint constructor with actual values."""
49+ endpoint = Endpoint (address = b'192.168.1.1' , port = 8080 , domain_name = "example.com" )
50+ # Protect against breaking changes
51+ assert isinstance (endpoint , Endpoint ), "Constructor must return Endpoint instance"
52+ assert endpoint .get_address () == b'192.168.1.1'
53+ assert endpoint .get_port () == 8080
54+ assert endpoint .get_domain_name () == "example.com"
55+
56+
57+ @pytest .mark .parametrize (
58+ ("input_port" , "expected_port" ),
59+ [
60+ (0 , 50211 ),
61+ (50111 , 50211 ),
62+ (80 , 80 ),
63+ ],
64+ )
65+ def test_from_proto_port_mapping (input_port , expected_port ):
66+ """Tests port mapping logic when converting Protobuf ServiceEndpoint to Endpoint.
67+
68+ Port mapping rules:
69+ - Port 0 or 50111 maps to 50211 (legacy/default behavior)
70+ - Other ports pass through unchanged
71+ """
72+
73+ mock_proto = MagicMock ()
74+ mock_proto .port = input_port
75+ mock_proto .ipAddressV4 = b"127.0.1.1"
76+ mock_proto .domain_name = "redpanda.com"
77+
78+ endpoint = Endpoint ._from_proto (mock_proto )
79+
80+ # Verify port mapping
81+ assert endpoint .get_port () == expected_port
82+
83+ # Verify all fields are mapped correctly (not just port)
84+ assert endpoint .get_address () == b"127.0.1.1" , "Address must be mapped from proto"
85+ assert endpoint .get_domain_name () == "redpanda.com" , "Domain name must be mapped from proto"
86+
87+ # Protect against breaking changes - PRIORITY 1
88+ assert isinstance (endpoint , Endpoint ), "Must return Endpoint instance"
89+
90+ @pytest .mark .parametrize (("field_to_none" , "attr_name" , "expected_default" ), [
91+ ("address" , "ipAddressV4" , b"" ),
92+ ("port" , "port" , 0 ),
93+ ("domain_name" , "domain_name" , "" )
94+ ])
95+ def test_to_proto_with_none_values (field_to_none , attr_name , expected_default ):
96+ """
97+ Ensures that when a field is None, _to_proto assigns the
98+ standard Protobuf default instead of crashing.
99+ """
100+ # Create endpoint with all values set
101+ params = {"address" : b'127.0.0.1' , "port" : 50211 , "domain_name" : "hiero.org" }
102+
103+ # Nullify one specific field
104+ params [field_to_none ] = None
105+ endpoint = Endpoint (** params )
106+
107+ # Act
108+ proto = endpoint ._to_proto ()
109+
110+ # Assert: Check that the specific attribute is the proto default
111+ assert getattr (proto , attr_name ) == expected_default
112+
113+ def test_to_proto ():
114+
115+ """Verifies that an Endpoint instance can be correctly serialized back into
116+ a Protobuf ServiceEndpoint object with all fields intact."""
117+
118+ endpoint = Endpoint (address = b'127.0.1.1' , port = 77777 , domain_name = "redpanda.com" )
119+ proto = endpoint ._to_proto ()
120+ assert proto .ipAddressV4 == b'127.0.1.1'
121+ assert proto .port == 77777
122+ assert proto .domain_name == "redpanda.com"
123+
124+ def test_str ():
125+
126+ """Tests the human-readable string representation of the Endpoint."""
127+
128+ endpoint = Endpoint (address = b'127.0.1.1' , port = 77777 , domain_name = "redpanda.com" )
129+ result = str (endpoint )
130+
131+ # Verify return type
132+ assert isinstance (result , str ), "String representation should return a string"
133+ assert result == '127.0.1.1:77777'
134+
135+
136+ def test_str_with_none_values ():
137+ """Test string representation when address or port is None."""
138+ endpoint = Endpoint (address = None , port = None , domain_name = "example.com" )
139+ with pytest .raises (AttributeError ):
140+ str (endpoint )
141+
142+ @pytest .mark .parametrize ("invalid_data" , [
143+ {"port" : 77777 , "domain_name" : "test.com" },
144+ {"ip_address_v4" : "127.0.0.1" , "domain_name" : "test.com" },
145+ {"ip_address_v4" : "127.0.0.1" , "port" : 77777 },
146+ ])
147+ def test_from_dict_missing_fields (invalid_data ):
148+ """Test that from_dict raises ValueError when required fields are missing."""
149+ with pytest .raises (ValueError , match = "JSON data must contain" ):
150+ Endpoint .from_dict (invalid_data )
151+
152+ def test_from_dict_success ():
153+ """ Tests successful creation of an Endpoint from a dictionary (JSON format) """
154+ data = {
155+ "ip_address_v4" : "127.0.0.1" ,
156+ "port" : 77777 ,
157+ "domain_name" : "redpanda.com"
158+ }
159+ endpoint = Endpoint .from_dict (data )
160+
161+ assert endpoint .get_address () == b"127.0.0.1"
162+ assert endpoint .get_port () == 77777
163+ assert endpoint .get_domain_name () == "redpanda.com"
0 commit comments