@@ -244,18 +244,28 @@ def test_reboot(self):
244244
245245 @mock .patch ("pyntc.devices.jnpr_device.time.sleep" )
246246 def test_wait_for_device_to_reboot (self , mock_sleep ):
247- with mock .patch .object (self .device , "open" ) as mock_open :
248- # Emulate the device disconnected and reconnecting
249- type(self .device .native ).connected = mock .PropertyMock (side_effect = [True , False , True ])
250- mock_open .side_effect = [Exception , Exception , True ]
251- self .device .reboot (wait_for_reload = True , timeout = 3 )
252- mock_open .assert_has_calls ([mock .call ()] * 3 )
247+ """Reboot completes when the device returns with a lower uptime than the baseline."""
248+ with (
249+ mock .patch .object (self .device , "open" ) as mock_open ,
250+ mock .patch .object (self .device , "close" ),
251+ mock .patch .object (type (self .device ), "uptime" , new_callable = mock .PropertyMock ) as mock_uptime ,
252+ ):
253+ # First read is the pre-reboot baseline; second is the post-reboot uptime.
254+ mock_uptime .side_effect = [455 , 30 ]
255+ self .device .reboot (wait_for_reload = True , timeout = 30 )
256+
257+ self .device .sw .reboot .assert_called_with (in_min = 0 )
258+ mock_open .assert_called ()
253259
254260 @mock .patch ("pyntc.devices.jnpr_device.time.sleep" )
255261 def test_wait_for_device_to_reboot_error (self , mock_sleep ):
256- with mock .patch .object (self .device , "open" ) as mock_open :
257- type(self .device .native ).connected = mock .PropertyMock (side_effect = [True , False ])
258- mock_open .side_effect = Exception
262+ """Raise RebootTimeoutError when the device never reports a lower uptime within the timeout."""
263+ with (
264+ mock .patch .object (self .device , "open" ),
265+ mock .patch .object (self .device , "close" ),
266+ mock .patch .object (type (self .device ), "uptime" , new_callable = mock .PropertyMock ) as mock_uptime ,
267+ ):
268+ mock_uptime .return_value = 455
259269 with pytest .raises (RebootTimeoutError ):
260270 self .device .reboot (wait_for_reload = True , timeout = 1 )
261271
@@ -290,12 +300,62 @@ def test_checkpoint(self, mock_scp):
290300 self .device .show .assert_called_with ("show config" )
291301
292302 def test_uptime (self ):
303+ """Cold cache (_uptime is None) refreshes facts and parses the uptime."""
304+ self .assertIsNone (self .device ._uptime )
305+ uptime = self .device .uptime
306+ self .assertEqual (uptime , 455 )
307+ self .device .native .facts_refresh .assert_called_once_with (keys = "RE0" )
308+
309+ def test_uptime_cached (self ):
310+ """A populated cache is returned as-is, with no device round-trip."""
311+ self .device ._uptime = 1234
293312 uptime = self .device .uptime
294- assert uptime == 455
313+ self .assertEqual (uptime , 1234 )
314+ self .device .native .facts_refresh .assert_not_called ()
315+
316+ def test_uptime_refreshes_after_cache_cleared (self ):
317+ """Clearing the cache forces a fresh read."""
318+ self .assertEqual (self .device .uptime , 455 )
319+
320+ self .device ._uptime = None
321+ self .device .native .facts = {"RE0" : {"up_time" : "30 seconds" }}
322+
323+ self .assertEqual (self .device .uptime , 30 )
324+
325+ def test_uptime_none_when_facts_unavailable (self ):
326+ """Missing/unavailable facts return None gracefully instead of raising an Exception."""
327+ self .device ._uptime = None
328+ self .device .native .facts = {}
329+ self .assertIsNone (self .device .uptime )
295330
296331 def test_uptime_string (self ):
332+ """Cold cache (_uptime_string is None) refreshes facts and formats the uptime."""
333+ self .assertIsNone (self .device ._uptime_string )
334+ uptime_string = self .device .uptime_string
335+ self .assertEqual (uptime_string , "00:00:07:35" )
336+ self .device .native .facts_refresh .assert_called_once_with (keys = "RE0" )
337+
338+ def test_uptime_string_cached (self ):
339+ """A populated cache is returned as-is, with no device round-trip."""
340+ self .device ._uptime_string = "01:02:03:04"
297341 uptime_string = self .device .uptime_string
298- assert uptime_string == "00:00:07:35"
342+ self .assertEqual (uptime_string , "01:02:03:04" )
343+ self .device .native .facts_refresh .assert_not_called ()
344+
345+ def test_uptime_string_refreshes_after_cache_cleared (self ):
346+ """Clearing the cache forces a fresh read."""
347+ self .assertEqual (self .device .uptime_string , "00:00:07:35" )
348+
349+ self .device ._uptime_string = None
350+ self .device .native .facts = {"RE0" : {"up_time" : "30 seconds" }}
351+
352+ self .assertEqual (self .device .uptime_string , "00:00:00:30" )
353+
354+ def test_uptime_string_none_when_facts_unavailable (self ):
355+ """Missing/unavailable facts return None gracefully instead of raising an Exception."""
356+ self .device ._uptime_string = None
357+ self .device .native .facts = {}
358+ self .assertIsNone (self .device .uptime_string )
299359
300360 def test_vendor (self ):
301361 vendor = self .device .vendor
0 commit comments