mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-01 19:18:35 -04:00
f23221420f
* fix(skills): replace deprecated utcnow in skill timestamp helper _now_iso() builds the 'created' value in skill frontmatter. datetime.utcnow() returns a naive datetime and has been deprecated since Python 3.12, scheduled for removal. Switch to the timezone-aware datetime.now(timezone.utc), keeping the serialized YYYY-MM-DDTHH:MM:SSZ shape unchanged so existing skill files keep parsing. timezone.utc is used rather than the datetime.UTC alias, which is 3.11+ only. Adds regression tests covering the deprecation, the serialized shape, and UTC correctness under a non-UTC local timezone -- the last guards against a bare datetime.now(), which yields the same shape but local wall time. Fixes #5697 * test(skills): skip timezone mutation where unsupported --------- Co-authored-by: Alexandre Teixeira <alexandremagteixeira@gmail.com>
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""Regression for issue #5697 — skill timestamps must not use ``datetime.utcnow()``.
|
|
|
|
``_now_iso()`` builds the ``created`` value in skill frontmatter. ``utcnow()``
|
|
returns a *naive* datetime and has been deprecated since Python 3.12, scheduled
|
|
for removal. The replacement must stay timezone-aware while keeping the
|
|
serialized ``YYYY-MM-DDTHH:MM:SSZ`` shape, so skill files written by older
|
|
versions keep parsing.
|
|
|
|
The UTC check matters on its own: a bare ``datetime.now()`` also produces the
|
|
right shape, but emits local wall time, which would silently backdate or
|
|
postdate skills for every user outside UTC.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import time
|
|
import warnings
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
|
|
from services.memory.skill_format import _now_iso
|
|
|
|
_ISO_Z = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$")
|
|
|
|
|
|
def test_now_iso_keeps_serialized_shape():
|
|
assert _ISO_Z.match(_now_iso())
|
|
|
|
|
|
def test_now_iso_emits_no_deprecation_warning():
|
|
with warnings.catch_warnings(record=True) as caught:
|
|
warnings.simplefilter("always")
|
|
_now_iso()
|
|
assert not [w for w in caught if issubclass(w.category, DeprecationWarning)]
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not hasattr(time, "tzset"),
|
|
reason="time.tzset is unavailable on this platform",
|
|
)
|
|
def test_now_iso_is_utc_not_local_time():
|
|
"""Pin UTC under a non-UTC local timezone, where the two visibly diverge."""
|
|
original_tz = os.environ.get("TZ")
|
|
os.environ["TZ"] = "Asia/Amman" # UTC+3, never UTC
|
|
time.tzset()
|
|
try:
|
|
emitted = datetime.strptime(_now_iso(), "%Y-%m-%dT%H:%M:%SZ").replace(
|
|
tzinfo=timezone.utc
|
|
)
|
|
drift = abs((emitted - datetime.now(timezone.utc)).total_seconds())
|
|
assert drift < 60, f"timestamp is {drift}s off UTC — local time leaked in"
|
|
finally:
|
|
if original_tz is None:
|
|
os.environ.pop("TZ", None)
|
|
else:
|
|
os.environ["TZ"] = original_tz
|
|
time.tzset()
|