Skip to content

Commit c6bff5b

Browse files
feat: unit test for 'endpoint.py' (#1290)
Signed-off-by: mukundkumarjha <[email protected]>
1 parent 9a51751 commit c6bff5b

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
77
## [Unreleased]
88

99
### Added
10+
- Added unit test for 'endpoint.py' to increase coverage.
1011
- Automated assignment guard for `advanced` issues; requires completion of at least one `good first issue` and one `intermediate` issue before assignment (exempts maintainers, committers, and triage members). (#1142)
1112
- Added Hbar object support for TransferTransaction HBAR transfers:
1213
- Methods now accept `Union[int, Hbar]` for amount parameters with immediate normalization to tinybars

tests/unit/endpoint_test.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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

Comments
 (0)