@@ -33,3 +33,231 @@ pub async fn check_service_connectivity(ip: &str, port: u16) -> bool {
3333pub async fn check_node_agent_connectivity ( ip : & str ) -> bool {
3434 check_service_connectivity ( ip, 47004 ) . await
3535}
36+
37+ #[ cfg( test) ]
38+ mod tests {
39+ use super :: * ;
40+ use std:: net:: TcpListener ;
41+ use tokio:: net:: TcpListener as TokioTcpListener ;
42+ use tokio:: time:: Duration ;
43+
44+ /// Helper function to start a mock TCP server for testing
45+ async fn start_mock_server ( port : u16 ) -> Result < TokioTcpListener , std:: io:: Error > {
46+ let addr = format ! ( "127.0.0.1:{}" , port) ;
47+ TokioTcpListener :: bind ( addr) . await
48+ }
49+
50+ /// Helper function to find an available port for testing
51+ fn find_available_port ( ) -> u16 {
52+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
53+ listener. local_addr ( ) . unwrap ( ) . port ( )
54+ }
55+
56+ #[ tokio:: test]
57+ async fn test_check_service_connectivity_success ( ) {
58+ // Start a mock server on an available port
59+ let port = find_available_port ( ) ;
60+ let _listener = start_mock_server ( port) . await . unwrap ( ) ;
61+
62+ // Test successful connection
63+ let result = check_service_connectivity ( "127.0.0.1" , port) . await ;
64+ assert ! ( result, "Should successfully connect to mock server" ) ;
65+ }
66+
67+ #[ tokio:: test]
68+ async fn test_check_service_connectivity_connection_refused ( ) {
69+ // Test connection to a port where no service is running
70+ let port = find_available_port ( ) ; // Get an available port but don't start a server
71+ let result = check_service_connectivity ( "127.0.0.1" , port) . await ;
72+ assert ! ( !result, "Should fail to connect to non-existent service" ) ;
73+ }
74+
75+ #[ tokio:: test]
76+ async fn test_check_service_connectivity_invalid_ip ( ) {
77+ // Test connection to invalid IP address
78+ let result = check_service_connectivity ( "999.999.999.999" , 8080 ) . await ;
79+ assert ! ( !result, "Should fail to connect to invalid IP address" ) ;
80+ }
81+
82+ #[ tokio:: test]
83+ async fn test_check_service_connectivity_localhost_variations ( ) {
84+ // Test different localhost representations
85+ let port = find_available_port ( ) ;
86+ let _listener = start_mock_server ( port) . await . unwrap ( ) ;
87+
88+ // Test with 127.0.0.1
89+ let result1 = check_service_connectivity ( "127.0.0.1" , port) . await ;
90+ assert ! ( result1, "Should connect to 127.0.0.1" ) ;
91+
92+ // Test with localhost (may not work in all environments)
93+ let result2 = check_service_connectivity ( "localhost" , port) . await ;
94+ // Note: This might fail in some test environments, so we just test it doesn't panic
95+ // In a real environment, localhost should resolve to 127.0.0.1
96+ println ! ( "Localhost connection result: {}" , result2) ;
97+ }
98+
99+ #[ tokio:: test]
100+ async fn test_check_service_connectivity_timeout_simulation ( ) {
101+ // Test with an IP that should cause timeout (non-routable IP)
102+ // Using 10.254.254.254 which is typically non-routable
103+ let result = check_service_connectivity ( "10.254.254.254" , 12345 ) . await ;
104+ assert ! ( !result, "Should timeout and return false" ) ;
105+ }
106+
107+ #[ tokio:: test]
108+ async fn test_check_service_connectivity_various_ports ( ) {
109+ // Test with different port numbers
110+ let port = find_available_port ( ) ;
111+ let _listener = start_mock_server ( port) . await . unwrap ( ) ;
112+
113+ // Test with the actual port
114+ let result1 = check_service_connectivity ( "127.0.0.1" , port) . await ;
115+ assert ! ( result1, "Should connect to correct port" ) ;
116+
117+ // Test with a different port (should fail)
118+ let wrong_port = if port == 65535 { port - 1 } else { port + 1 } ;
119+ let result2 = check_service_connectivity ( "127.0.0.1" , wrong_port) . await ;
120+ assert ! ( !result2, "Should fail to connect to wrong port" ) ;
121+ }
122+
123+ #[ tokio:: test]
124+ async fn test_check_service_connectivity_edge_case_ports ( ) {
125+ // Test with edge case port numbers
126+
127+ // Test with port 0 (should fail)
128+ let result1 = check_service_connectivity ( "127.0.0.1" , 0 ) . await ;
129+ assert ! ( !result1, "Should fail to connect to port 0" ) ;
130+
131+ // Test with high port number (but valid)
132+ let result2 = check_service_connectivity ( "127.0.0.1" , 65534 ) . await ;
133+ assert ! (
134+ !result2,
135+ "Should fail to connect to non-listening high port"
136+ ) ;
137+ }
138+
139+ #[ tokio:: test]
140+ async fn test_check_node_agent_connectivity_success ( ) {
141+ // Test the NodeAgent specific function with a mock server on port 47004
142+ // Note: We can't easily bind to 47004 in tests as it might be in use
143+ // So we test the function call structure
144+ let result = check_node_agent_connectivity ( "127.0.0.1" ) . await ;
145+ // We don't assert the result since no actual NodeAgent is running
146+ // but we verify the function executes without panic
147+ println ! ( "NodeAgent connectivity test result: {}" , result) ;
148+ }
149+
150+ #[ tokio:: test]
151+ async fn test_check_node_agent_connectivity_with_mock ( ) {
152+ // Create a mock server on port 47004 if available
153+ match start_mock_server ( 47004 ) . await {
154+ Ok ( _listener) => {
155+ let result = check_node_agent_connectivity ( "127.0.0.1" ) . await ;
156+ assert ! ( result, "Should connect to mock NodeAgent on port 47004" ) ;
157+ }
158+ Err ( _) => {
159+ // Port 47004 might be in use, test with unavailable service
160+ let result = check_node_agent_connectivity ( "127.0.0.1" ) . await ;
161+ // Just verify it doesn't panic - result depends on what's running on 47004
162+ println ! (
163+ "NodeAgent connectivity result (port might be in use): {}" ,
164+ result
165+ ) ;
166+ }
167+ }
168+ }
169+
170+ #[ tokio:: test]
171+ async fn test_check_node_agent_connectivity_invalid_ip ( ) {
172+ // Test NodeAgent connectivity with invalid IP
173+ let result = check_node_agent_connectivity ( "999.999.999.999" ) . await ;
174+ assert ! ( !result, "Should fail to connect NodeAgent at invalid IP" ) ;
175+ }
176+
177+ #[ tokio:: test]
178+ async fn test_check_node_agent_connectivity_unreachable_ip ( ) {
179+ // Test NodeAgent connectivity with unreachable IP
180+ let result = check_node_agent_connectivity ( "10.254.254.254" ) . await ;
181+ assert ! (
182+ !result,
183+ "Should fail to connect NodeAgent at unreachable IP"
184+ ) ;
185+ }
186+
187+ #[ tokio:: test]
188+ async fn test_connectivity_functions_with_empty_ip ( ) {
189+ // Test with empty IP string
190+ let result1 = check_service_connectivity ( "" , 8080 ) . await ;
191+ assert ! ( !result1, "Should fail with empty IP string" ) ;
192+
193+ let result2 = check_node_agent_connectivity ( "" ) . await ;
194+ assert ! ( !result2, "Should fail NodeAgent connectivity with empty IP" ) ;
195+ }
196+
197+ #[ tokio:: test]
198+ async fn test_connectivity_timeout_behavior ( ) {
199+ // Test that the timeout is actually working (should complete within reasonable time)
200+ let start = std:: time:: Instant :: now ( ) ;
201+ let result = check_service_connectivity ( "10.254.254.254" , 12345 ) . await ;
202+ let duration = start. elapsed ( ) ;
203+
204+ assert ! ( !result, "Should return false for unreachable service" ) ;
205+ assert ! (
206+ duration >= Duration :: from_secs( 3 ) ,
207+ "Should wait at least 3 seconds for timeout"
208+ ) ;
209+ assert ! (
210+ duration < Duration :: from_secs( 5 ) ,
211+ "Should not wait more than 5 seconds total"
212+ ) ;
213+ }
214+
215+ #[ tokio:: test]
216+ async fn test_multiple_concurrent_connectivity_checks ( ) {
217+ // Test multiple concurrent connectivity checks
218+ let port = find_available_port ( ) ;
219+ let _listener = start_mock_server ( port) . await . unwrap ( ) ;
220+
221+ let tasks = vec ! [
222+ tokio:: spawn( check_service_connectivity( "127.0.0.1" , port) ) ,
223+ tokio:: spawn( check_service_connectivity( "127.0.0.1" , port) ) ,
224+ tokio:: spawn( check_service_connectivity( "127.0.0.1" , port) ) ,
225+ ] ;
226+
227+ let results = futures:: future:: join_all ( tasks) . await ;
228+
229+ for result in results {
230+ let connectivity_result = result. unwrap ( ) ;
231+ assert ! (
232+ connectivity_result,
233+ "All concurrent connections should succeed"
234+ ) ;
235+ }
236+ }
237+
238+ #[ tokio:: test]
239+ async fn test_connectivity_with_ipv6_localhost ( ) {
240+ // Test with IPv6 localhost address
241+ let result = check_service_connectivity ( "::1" , 8080 ) . await ;
242+ // This may fail in environments without IPv6 support, so we just ensure no panic
243+ println ! ( "IPv6 localhost connectivity result: {}" , result) ;
244+ }
245+
246+ #[ tokio:: test]
247+ async fn test_service_connectivity_function_parameters ( ) {
248+ // Test various parameter combinations to ensure robustness
249+ let test_cases = vec ! [
250+ ( "127.0.0.1" , 1 ) , // Low port number
251+ ( "127.0.0.1" , 65535 ) , // High port number
252+ ( "0.0.0.0" , 8080 ) , // All interfaces IP
253+ ( "255.255.255.255" , 8080 ) , // Broadcast IP
254+ ] ;
255+
256+ for ( ip, port) in test_cases {
257+ let result = check_service_connectivity ( ip, port) . await ;
258+ // We don't assert specific results as they depend on network configuration
259+ // but ensure no panics occur
260+ println ! ( "Connectivity test for {}:{} = {}" , ip, port, result) ;
261+ }
262+ }
263+ }
0 commit comments