diff --git a/tests/trestle/core/remote/cache_test.py b/tests/trestle/core/remote/cache_test.py index d17d7c97e..49daf5fb3 100644 --- a/tests/trestle/core/remote/cache_test.py +++ b/tests/trestle/core/remote/cache_test.py @@ -15,6 +15,7 @@ # limitations under the License. """Testing for cache functionality.""" +import datetime import getpass import pathlib import random @@ -68,6 +69,35 @@ def get_catalog_fetcher( return fetcher, catalog_data +def test_time_since_modification_uses_utc(monkeypatch: MonkeyPatch) -> None: + """Test cache age calculation uses UTC-aware datetimes.""" + + class _StatResult: + st_mtime = 1234.5 + + class _FakePath: + def stat(self) -> _StatResult: + return _StatResult() + + expected_last_mod = datetime.datetime(2026, 3, 15, 11, 0, 0, tzinfo=datetime.timezone.utc) + expected_now = datetime.datetime(2026, 3, 15, 12, 30, 0, tzinfo=datetime.timezone.utc) + + class _FakeDateTime: + @classmethod + def fromtimestamp(cls, ts: float, tz: datetime.tzinfo | None = None) -> datetime.datetime: + assert ts == _StatResult.st_mtime + assert tz == datetime.timezone.utc + return expected_last_mod + + @classmethod + def now(cls, tz: datetime.tzinfo | None = None) -> datetime.datetime: + assert tz == datetime.timezone.utc + return expected_now + + monkeypatch.setattr(cache.datetime, 'datetime', _FakeDateTime) + assert cache.FetcherBase._time_since_modification(_FakePath()) == datetime.timedelta(hours=1, minutes=30) + + def test_fetcher_oscal(tmp_trestle_dir: pathlib.Path) -> None: """Test whether fetcher can get an object from the cache as an oscal model.""" fetcher, catalog_data = get_catalog_fetcher(tmp_trestle_dir) diff --git a/trestle/core/remote/cache.py b/trestle/core/remote/cache.py index 05a6c1531..72d7a36bd 100644 --- a/trestle/core/remote/cache.py +++ b/trestle/core/remote/cache.py @@ -67,8 +67,8 @@ def __init__(self, trestle_root: pathlib.Path, uri: str) -> None: @staticmethod def _time_since_modification(file_path: pathlib.Path) -> datetime.timedelta: """Get time since last modification.""" - last_modification = datetime.datetime.fromtimestamp(file_path.stat().st_mtime) - return datetime.datetime.now() - last_modification + last_modification = datetime.datetime.fromtimestamp(file_path.stat().st_mtime, tz=datetime.timezone.utc) + return datetime.datetime.now(datetime.timezone.utc) - last_modification @abstractmethod def _do_fetch(self) -> None: