1+ import  unittest 
2+ from  unittest .mock  import  MagicMock , patch 
3+ 
4+ from  check_ping  import  CheckPing 
5+ from  pydantic  import  ValidationError 
6+ 
7+ 
8+ class  TestCheckPing (unittest .TestCase ):
9+     def  setUp (self ):
10+         # Patch the DistributedRunner to avoid actual k8s calls 
11+         self .mock_distributed_runner_patch  =  patch ("check_ping.DistributedRunner" )
12+         self .MockDistributedRunner  =  self .mock_distributed_runner_patch .start ()
13+         self .mock_runner_instance  =  MagicMock ()
14+         self .MockDistributedRunner .return_value  =  self .mock_runner_instance 
15+ 
16+         self .valid_params  =  {
17+             "pingTarget" : "8.8.8.8" ,
18+         }
19+ 
20+     def  tearDown (self ):
21+         self .mock_distributed_runner_patch .stop ()
22+ 
23+     def  test_init_with_minimal_parameters (self ):
24+         """Tests that CheckPing initializes correctly with minimal valid parameters.""" 
25+         check  =  CheckPing (self .valid_params )
26+         self .assertEqual (check .namespace , "cluster-health" )
27+         self .assertEqual (check .image , "alpine:3.22" )
28+         self .assertEqual (check .count , 1 )
29+         self .assertEqual (check .timeout_seconds , 300 )
30+         self .assertEqual (check .ping_target , "8.8.8.8" )
31+         self .assertIsNone (check .secondary_network_config )
32+         self .assertIsNone (check .avg_rtt_ms_threshold )
33+         self .assertIsNone (check .max_rtt_ms_threshold )
34+ 
35+     def  test_init_with_custom_parameters (self ):
36+         """Tests initialization with custom parameters.""" 
37+         params  =  {
38+             "namespace" : "custom-ns" ,
39+             "image" : "my-custom/ping:latest" ,
40+             "secondaryNetworkConfig" : "my-net-config" ,
41+             "timeoutSeconds" : 120 ,
42+             "count" : 5 ,
43+             "avgRttMsThreshold" : 50.0 ,
44+             "maxRttMsThreshold" : 100.0 ,
45+             "pingTarget" : "1.1.1.1" ,
46+         }
47+         check  =  CheckPing (params )
48+         self .assertEqual (check .namespace , "custom-ns" )
49+         self .assertEqual (check .image , "my-custom/ping:latest" )
50+         self .assertEqual (check .secondary_network_config , "my-net-config" )
51+         self .assertEqual (check .timeout_seconds , 120 )
52+         self .assertEqual (check .count , 5 )
53+         self .assertEqual (check .avg_rtt_ms_threshold , 50.0 )
54+         self .assertEqual (check .max_rtt_ms_threshold , 100.0 )
55+         self .assertEqual (check .ping_target , "1.1.1.1" )
56+ 
57+     def  test_init_with_missing_required_parameters (self ):
58+         """Tests that initialization fails if required parameters are missing.""" 
59+         with  self .assertRaises (ValidationError ):
60+             CheckPing ({"namespace" : "test" })  # Missing pingTarget 
61+ 
62+     # --- Log Parsing Tests --- 
63+ 
64+     def  test_parse_pod_logs_success_no_rtt_check (self ):
65+         """Tests the log parsing function with successful ping results and no RTT checks.""" 
66+         check  =  CheckPing (self .valid_params )
67+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
68+         logs  =  """ 
69+         PING 8.8.8.8 (8.8.8.8): 56 data bytes 
70+         64 bytes from 8.8.8.8: seq=0 ttl=118 time=10.5 ms 
71+ 
72+         --- 8.8.8.8 ping statistics --- 
73+         1 packets transmitted, 1 packets received, 0% packet loss 
74+         """ 
75+         self .assertTrue (log_parser (logs , "node-1" ))
76+ 
77+     def  test_parse_pod_logs_success_with_rtt_check_iputils (self ):
78+         """Tests log parsing with successful RTTs (iputils-ping format).""" 
79+         params  =  self .valid_params .copy ()
80+         params ["avgRttMsThreshold" ] =  20.0 
81+         params ["maxRttMsThreshold" ] =  30.0 
82+         check  =  CheckPing (params )
83+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
84+         logs  =  """ 
85+         PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 
86+         64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=15.1 ms 
87+ 
88+         --- 8.8.8.8 ping statistics --- 
89+         1 packets transmitted, 1 received, 0% packet loss, time 0ms 
90+         rtt min/avg/max/mdev = 15.100/15.100/15.100/0.000 ms 
91+         """ 
92+         self .assertTrue (log_parser (logs , "node-1" ))
93+ 
94+     def  test_parse_pod_logs_success_with_rtt_check_busybox (self ):
95+         """Tests log parsing with successful RTTs (busybox ping format).""" 
96+         params  =  self .valid_params .copy ()
97+         params ["avgRttMsThreshold" ] =  20.0 
98+         params ["maxRttMsThreshold" ] =  30.0 
99+         check  =  CheckPing (params )
100+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
101+         logs  =  """ 
102+         PING 8.8.8.8 (8.8.8.8): 56 data bytes 
103+         64 bytes from 8.8.8.8: seq=0 ttl=118 time=15.1 ms 
104+ 
105+         --- 8.8.8.8 ping statistics --- 
106+         1 packets transmitted, 1 packets received, 0% packet loss 
107+         round-trip min/avg/max = 15.100/15.100/15.100 ms 
108+         """ 
109+         self .assertTrue (log_parser (logs , "node-1" ))
110+ 
111+     def  test_parse_pod_logs_packet_loss_fail (self ):
112+         """Tests log parsing when packet loss is detected.""" 
113+         check  =  CheckPing (self .valid_params )
114+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
115+         logs  =  "2 packets transmitted, 1 packets received, 50% packet loss" 
116+         self .assertFalse (log_parser (logs , "node-1" ))
117+ 
118+     def  test_parse_pod_logs_packet_loss_parse_error (self ):
119+         """Tests log parsing with malformed packet loss data.""" 
120+         check  =  CheckPing (self .valid_params )
121+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
122+         logs  =  "Some unexpected output without packet loss information." 
123+         self .assertFalse (log_parser (logs , "node-1" ))
124+ 
125+     def  test_parse_pod_logs_wrong_packet_count (self ):
126+         """Tests log parsing when the transmitted packet count is wrong.""" 
127+         params  =  self .valid_params .copy ()
128+         params ["count" ] =  5 
129+         check  =  CheckPing (params )
130+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
131+         logs  =  "1 packets transmitted, 1 received, 0% packet loss" 
132+         self .assertFalse (log_parser (logs , "node-1" ))
133+ 
134+     def  test_parse_pod_logs_avg_rtt_fail (self ):
135+         """Tests log parsing when average RTT is above the threshold.""" 
136+         params  =  self .valid_params .copy ()
137+         params ["avgRttMsThreshold" ] =  10.0 
138+         check  =  CheckPing (params )
139+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
140+         logs  =  """ 
141+         1 packets transmitted, 1 received, 0% packet loss 
142+         rtt min/avg/max/mdev = 15.0/15.0/15.0/0.0 ms 
143+         """ 
144+         self .assertFalse (log_parser (logs , "node-1" ))
145+ 
146+     def  test_parse_pod_logs_max_rtt_fail (self ):
147+         """Tests log parsing when max RTT is above the threshold.""" 
148+         params  =  self .valid_params .copy ()
149+         params ["maxRttMsThreshold" ] =  20.0 
150+         check  =  CheckPing (params )
151+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
152+         logs  =  """ 
153+         1 packets transmitted, 1 received, 0% packet loss 
154+         rtt min/avg/max/mdev = 15.0/18.0/25.0/2.0 ms 
155+         """ 
156+         self .assertFalse (log_parser (logs , "node-1" ))
157+ 
158+     def  test_parse_pod_logs_rtt_parse_error (self ):
159+         """Tests log parsing with malformed RTT data when RTT check is enabled.""" 
160+         params  =  self .valid_params .copy ()
161+         params ["avgRttMsThreshold" ] =  10.0 
162+         check  =  CheckPing (params )
163+         log_parser  =  check .parse_pod_logs_from_ping_test_func ()
164+         logs  =  "1 packets transmitted, 1 received, 0% packet loss. No RTT data." 
165+         self .assertFalse (log_parser (logs , "node-1" ))
166+ 
167+     # --- is_healthy Tests --- 
168+ 
169+     def  test_is_healthy_success (self ):
170+         """Tests is_healthy when the distributed run is successful.""" 
171+         self .mock_runner_instance .run_on_all_nodes .return_value  =  True 
172+         check  =  CheckPing (self .valid_params )
173+         self .assertTrue (check .is_healthy ())
174+         self .mock_runner_instance .run_on_all_nodes .assert_called_once ()
175+ 
176+     def  test_is_healthy_failure (self ):
177+         """Tests is_healthy when the distributed run fails.""" 
178+         self .mock_runner_instance .run_on_all_nodes .return_value  =  False 
179+         check  =  CheckPing (self .valid_params )
180+         self .assertFalse (check .is_healthy ())
181+         self .mock_runner_instance .run_on_all_nodes .assert_called_once ()
182+ 
183+     def  test_is_healthy_constructs_runner_correctly (self ):
184+         """Tests that DistributedRunner is constructed with the correct pod template.""" 
185+         params  =  {
186+             "pingTarget" : "8.8.8.8" ,
187+             "count" : 5 ,
188+             "namespace" : "ping-ns" ,
189+             "timeoutSeconds" : 99 ,
190+         }
191+         check  =  CheckPing (params )
192+         check .is_healthy ()
193+ 
194+         self .MockDistributedRunner .assert_called_once ()
195+         _ , kwargs  =  self .MockDistributedRunner .call_args 
196+ 
197+         self .assertEqual (kwargs ["pod_name_prefix" ], "ping" )
198+         self .assertEqual (kwargs ["namespace" ], "ping-ns" )
199+         self .assertEqual (kwargs ["timeout_seconds" ], 99 )
200+         self .assertIsNotNone (kwargs ["log_parse_function" ])
201+ 
202+         pod_template  =  kwargs ["pod_template" ]
203+         container  =  pod_template .spec .containers [0 ]
204+         self .assertEqual (container .image , "alpine:3.22" )
205+         self .assertEqual (container .command , ["ping" , "-c" , "5" , "8.8.8.8" ])
206+         self .assertIsNone (pod_template .metadata .annotations )
207+ 
208+     def  test_is_healthy_with_secondary_network (self ):
209+         """Tests that the pod template includes secondary network annotations.""" 
210+         params  =  self .valid_params .copy ()
211+         params ["secondaryNetworkConfig" ] =  '{"name": "net1"}' 
212+         check  =  CheckPing (params )
213+         check .is_healthy ()
214+ 
215+         _ , kwargs  =  self .MockDistributedRunner .call_args 
216+         pod_template  =  kwargs ["pod_template" ]
217+         self .assertEqual (
218+             pod_template .metadata .annotations ,
219+             {"networking.gke.io/interfaces" : '{"name": "net1"}' },
220+         )
221+ 
222+ 
223+ if  __name__  ==  "__main__" :
224+     unittest .main ()
0 commit comments