fix: calendar recurrence tz/overrides, today habits schedule, list archive mobile access
ci / docker (push) Successful in 3m23s
ci / docker (push) Successful in 3m23s
This commit is contained in:
@@ -70,6 +70,111 @@ END:VCALENDAR\r
|
||||
]
|
||||
|
||||
|
||||
def test_calendar_parser_keeps_recurrence_in_wall_clock_timezone_across_dst():
|
||||
from backend.mvp import parse_ics_events
|
||||
|
||||
ics = """BEGIN:VCALENDAR\r
|
||||
VERSION:2.0\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:dst-recurring\r
|
||||
DTSTART;TZID=America/New_York:20260307T090000\r
|
||||
DTEND;TZID=America/New_York:20260307T100000\r
|
||||
RRULE:FREQ=DAILY;COUNT=3\r
|
||||
SUMMARY:Morning meeting\r
|
||||
END:VEVENT\r
|
||||
END:VCALENDAR\r
|
||||
"""
|
||||
|
||||
events = parse_ics_events(
|
||||
ics,
|
||||
"DST calendar",
|
||||
"#fff",
|
||||
datetime(2026, 3, 7, tzinfo=UTC),
|
||||
datetime(2026, 3, 11, tzinfo=UTC),
|
||||
)
|
||||
|
||||
assert [row["starts_at"].isoformat() for row in events] == [
|
||||
"2026-03-07T14:00:00+00:00",
|
||||
"2026-03-08T13:00:00+00:00",
|
||||
"2026-03-09T13:00:00+00:00",
|
||||
]
|
||||
|
||||
|
||||
def test_calendar_parser_replaces_master_occurrence_with_override():
|
||||
from backend.mvp import parse_ics_events
|
||||
|
||||
ics = """BEGIN:VCALENDAR\r
|
||||
VERSION:2.0\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:overridden-series\r
|
||||
DTSTART;TZID=Asia/Shanghai:20260906T090000\r
|
||||
DTEND;TZID=Asia/Shanghai:20260906T100000\r
|
||||
RRULE:FREQ=DAILY;COUNT=2\r
|
||||
SUMMARY:原始事件\r
|
||||
END:VEVENT\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:overridden-series\r
|
||||
RECURRENCE-ID;TZID=Asia/Shanghai:20260907T090000\r
|
||||
DTSTART;TZID=Asia/Shanghai:20260907T110000\r
|
||||
DTEND;TZID=Asia/Shanghai:20260907T120000\r
|
||||
SUMMARY:改期事件\r
|
||||
END:VEVENT\r
|
||||
END:VCALENDAR\r
|
||||
"""
|
||||
|
||||
events = parse_ics_events(
|
||||
ics,
|
||||
"测试日历",
|
||||
"#fff",
|
||||
datetime(2026, 9, 5, tzinfo=UTC),
|
||||
datetime(2026, 9, 8, tzinfo=UTC),
|
||||
)
|
||||
|
||||
assert [(row["title"], row["starts_at"].isoformat()) for row in events] == [
|
||||
("原始事件", "2026-09-06T01:00:00+00:00"),
|
||||
("改期事件", "2026-09-07T03:00:00+00:00"),
|
||||
]
|
||||
|
||||
|
||||
def test_calendar_parser_skips_cancelled_events_and_cancelled_overrides():
|
||||
from backend.mvp import parse_ics_events
|
||||
|
||||
ics = """BEGIN:VCALENDAR\r
|
||||
VERSION:2.0\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:cancelled-single\r
|
||||
DTSTART:20260906T090000\r
|
||||
STATUS:CANCELLED\r
|
||||
SUMMARY:已取消单次事件\r
|
||||
END:VEVENT\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:partly-cancelled-series\r
|
||||
DTSTART;TZID=Asia/Shanghai:20260906T090000\r
|
||||
RRULE:FREQ=DAILY;COUNT=2\r
|
||||
SUMMARY:重复事件\r
|
||||
END:VEVENT\r
|
||||
BEGIN:VEVENT\r
|
||||
UID:partly-cancelled-series\r
|
||||
RECURRENCE-ID;TZID=Asia/Shanghai:20260907T090000\r
|
||||
STATUS:CANCELLED\r
|
||||
SUMMARY:已取消实例\r
|
||||
END:VEVENT\r
|
||||
END:VCALENDAR\r
|
||||
"""
|
||||
|
||||
events = parse_ics_events(
|
||||
ics,
|
||||
"测试日历",
|
||||
"#fff",
|
||||
datetime(2026, 9, 5, tzinfo=UTC),
|
||||
datetime(2026, 9, 8, tzinfo=UTC),
|
||||
)
|
||||
|
||||
assert [(row["title"], row["starts_at"].isoformat()) for row in events] == [
|
||||
("重复事件", "2026-09-06T01:00:00+00:00"),
|
||||
]
|
||||
|
||||
|
||||
def test_calendar_url_rejects_private_addresses(monkeypatch):
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
Reference in New Issue
Block a user