"""Tests for i2p_time.clock — Clock offset management.""" import time import threading from unittest.mock import MagicMock from i2p_time.clock import Clock, _system_millis from i2p_time.build_time import BuildTime class TestClock: def test_now_returns_system_time_with_zero_offset(self): clock = Clock() before = _system_millis() now = clock.now() after = _system_millis() assert before <= now <= after + 1 # allow 1ms tolerance def test_get_offset_initially_zero_if_clock_ok(self): clock = Clock() # System clock should be within BuildTime bounds in normal operation offset = clock.get_offset() # If system clock is valid, offset is 0 if not clock._is_system_clock_bad: assert offset == 0 def test_set_offset_changes_now(self): clock = Clock() clock.set_offset(5000, force=True) now = clock.now() sys_now = _system_millis() # now() should be ~5000ms ahead of system time assert abs((now - sys_now) - 5000) < 100 def test_set_offset_rejects_beyond_max(self): clock = Clock() clock._is_system_clock_bad = False too_big = Clock.MAX_OFFSET + 1000 clock.set_offset(too_big) assert clock.get_offset() == 0 # unchanged def test_set_offset_force_bypasses_max(self): clock = Clock() clock._is_system_clock_bad = False big = Clock.MAX_OFFSET + 1000 clock.set_offset(big, force=True) assert clock.get_offset() == big def test_set_offset_ignores_small_changes(self): clock = Clock() # First set to establish _already_changed clock.set_offset(10000, force=True) # Small change (< MIN_OFFSET_CHANGE) should be ignored clock.set_offset(10000 + 100) assert clock.get_offset() == 10000 def test_get_updated_successfully(self): clock = Clock() assert not clock.get_updated_successfully() clock.set_offset(10000, force=True) assert clock.get_updated_successfully() def test_set_now_adjusts_offset(self): clock = Clock() # Set "real time" to 5 seconds ahead real = _system_millis() + 5000 if BuildTime.get_earliest_time() <= real <= BuildTime.get_latest_time(): clock.set_now(real) assert abs(clock.get_offset() - 5000) < 1000 def test_set_now_rejects_invalid_time(self): clock = Clock() clock.set_now(1000) # Way too early assert clock.get_offset() == 0 # unchanged def test_listener_called_on_offset_change(self): clock = Clock() listener = MagicMock() clock.add_update_listener(listener) clock.set_offset(10000, force=True) listener.offset_changed.assert_called_once_with(10000) def test_remove_listener(self): clock = Clock() listener = MagicMock() clock.add_update_listener(listener) clock.remove_update_listener(listener) clock.set_offset(10000, force=True) listener.offset_changed.assert_not_called() def test_max_live_offset_after_startup(self): clock = Clock() # Simulate having been running for > 10 minutes clock._already_changed = True clock._started_on = _system_millis() - (11 * 60 * 1000) clock._offset = 0 # Try to set a big offset (> MAX_LIVE_OFFSET) clock.set_offset(Clock.MAX_LIVE_OFFSET + 1000) assert clock.get_offset() == 0 # rejected class TestBuildTime: def test_earliest_time_is_reasonable(self): earliest = BuildTime.get_earliest_time() # Should be sometime in 2025 assert earliest > 1700000000000 # > ~Nov 2023 assert earliest < 2000000000000 # < ~May 2033 def test_latest_is_after_earliest(self): assert BuildTime.get_latest_time() > BuildTime.get_earliest_time() def test_build_time_between_bounds(self): assert BuildTime.get_earliest_time() <= BuildTime.get_build_time() assert BuildTime.get_build_time() <= BuildTime.get_latest_time()