Files
FabledScribe/tests/test_tz_helpers.py
T
bvandeusen e4e1d1da49 fix(tz): interpret calendar and briefing dates in user's local timezone
Two related bugs where the server defaulted naive datetimes to UTC instead
of the configured user timezone, causing all-day events to land on the
previous day and briefings to "disappear" at UTC midnight.

- New services/tz.py helpers: get_user_tz, user_today, user_briefing_date
  (the briefing day flips at 4am local to align with the compilation slot,
  so the 00:00-04:00 local window still shows yesterday's briefing until
  the new one is generated).
- calendar create/list/update tools now parse naive datetimes in the
  user's TZ before converting to UTC for storage, and tool descriptions
  tell the model to pass plain local dates.
- briefing_conversations.get_or_create_today_conversation and the
  reset-today route use user_briefing_date so the in-progress briefing
  doesn't get replaced at 19:00 NY / UTC midnight.
- _run_profile_closeout targets user-local "yesterday" for consistency.

Regression tests added for the TZ helpers and the calendar tool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:35:27 -04:00

64 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for user-timezone helpers (services/tz.py)."""
from datetime import datetime
from unittest.mock import AsyncMock, patch
from zoneinfo import ZoneInfo
import pytest
@pytest.mark.asyncio
async def test_get_user_tz_returns_configured_zone():
from fabledassistant.services import tz as tz_mod
with patch.object(tz_mod, "get_setting", AsyncMock(return_value="America/New_York")):
zone = await tz_mod.get_user_tz(1)
assert zone == ZoneInfo("America/New_York")
@pytest.mark.asyncio
async def test_get_user_tz_falls_back_to_utc_on_bad_input():
from fabledassistant.services import tz as tz_mod
with patch.object(tz_mod, "get_setting", AsyncMock(return_value="Not/AZone")):
zone = await tz_mod.get_user_tz(1)
assert zone == ZoneInfo("UTC")
@pytest.mark.asyncio
async def test_user_briefing_date_before_4am_returns_yesterday():
"""00:0003:59 local still shows yesterday's briefing day."""
from fabledassistant.services import tz as tz_mod
ny = ZoneInfo("America/New_York")
# 02:00 NY on 2026-04-13 → briefing day = 2026-04-12
fake_now = datetime(2026, 4, 13, 2, 0, tzinfo=ny)
class _FakeDatetime(datetime):
@classmethod
def now(cls, tz=None):
return fake_now
with patch.object(tz_mod, "get_user_tz", AsyncMock(return_value=ny)), \
patch.object(tz_mod, "datetime", _FakeDatetime):
day = await tz_mod.user_briefing_date(1)
assert day.isoformat() == "2026-04-12"
@pytest.mark.asyncio
async def test_user_briefing_date_after_4am_returns_today():
from fabledassistant.services import tz as tz_mod
ny = ZoneInfo("America/New_York")
fake_now = datetime(2026, 4, 13, 4, 30, tzinfo=ny)
class _FakeDatetime(datetime):
@classmethod
def now(cls, tz=None):
return fake_now
with patch.object(tz_mod, "get_user_tz", AsyncMock(return_value=ny)), \
patch.object(tz_mod, "datetime", _FakeDatetime):
day = await tz_mod.user_briefing_date(1)
assert day.isoformat() == "2026-04-13"