fix(mcp): list_events tz-aware UTC range + end-of-day inclusive

Phase 6 smoke caught:

  Error executing tool list_events:
    can't compare offset-naive and offset-aware datetimes

Event.start_dt is stored timezone-aware; the wrapper was passing naive
datetimes built from datetime.fromisoformat("YYYY-MM-DD"), so the SQL
comparison crashed. Also: the docstring promises "date_to inclusive at
end-of-day" but the code was using midnight-of-date_to, which would
silently miss same-day events after midnight.

Extracted the range math into _day_range_utc() so create/update_event's
_combine() can stay as-is (it stays naive — the service localizes
create/update inputs against the user's tz, that path didn't crash).

Test updated to match: assert tz-aware UTC datetimes and the +24h
bump for end-of-day-inclusive semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 14:57:38 -04:00
parent 02fe500d61
commit 05b0bf97d7
2 changed files with 32 additions and 7 deletions
+7 -3
View File
@@ -31,7 +31,10 @@ def _fake_event(**overrides) -> MagicMock:
@pytest.mark.asyncio
async def test_list_events_passes_datetime_range():
async def test_list_events_passes_timezone_aware_range():
"""Range must be tz-aware (UTC) and date_to inclusive at end-of-day —
Event.start_dt is tz-aware in the DB; naive comparisons raise TypeError."""
from datetime import timezone
mock = AsyncMock(return_value=[
{"id": 1, "title": "morning standup"},
])
@@ -39,8 +42,9 @@ async def test_list_events_passes_datetime_range():
out = await list_events(date_from="2026-06-01", date_to="2026-06-08")
args, _ = mock.call_args
assert args[0] == 7 # user_id
assert args[1] == datetime(2026, 6, 1)
assert args[2] == datetime(2026, 6, 8)
assert args[1] == datetime(2026, 6, 1, tzinfo=timezone.utc)
# date_to is end-of-day inclusive → start of 2026-06-09 (24h past start of 2026-06-08)
assert args[2] == datetime(2026, 6, 9, tzinfo=timezone.utc)
assert out["total"] == 1