refactor: simplify dodo core experience
ci / docker (push) Successful in 3m54s

This commit is contained in:
2026-09-06 14:41:08 +08:00
parent fcaabce99f
commit d595af4f52
29 changed files with 187 additions and 955 deletions
+1 -164
View File
@@ -15,7 +15,6 @@ def boot(client):
def test_bootstrap_returns_navigation_and_current_user(client):
inbox = boot(client)
client.post("/api/v1/folders", json={"name": "工作"})
client.post("/api/v1/tags", json={"name": "重要", "color": "#ff0000"})
client.post("/api/v1/tasks", json={"title": "首屏任务", "list_id": inbox["id"]})
response = client.get("/api/v1/bootstrap")
@@ -25,172 +24,10 @@ def test_bootstrap_returns_navigation_and_current_user(client):
assert data["user"]["username"] == "owner"
assert any(row["id"] == inbox["id"] for row in data["lists"])
assert [row["name"] for row in data["folders"]] == ["工作"]
assert [row["name"] for row in data["tags"]] == ["重要"]
assert "tags" not in data
assert data["inbox_id"] == inbox["id"]
def test_calendar_endpoint_is_removed(client):
boot(client)
assert client.get("/api/v1/calendar", params={"start": "2026-09-01", "end": "2026-09-30"}).status_code == 404
def test_calendar_parser_handles_tzid_recurrence_folding_and_multiday():
from backend.mvp import parse_ics_events
ics = """BEGIN:VCALENDAR\r
VERSION:2.0\r
BEGIN:VEVENT\r
UID:tzid-recurring\r
DTSTART;TZID=Asia/Shanghai:20260906T090000\r
DTEND;TZID=Asia/Shanghai:20260906T100000\r
RRULE:FREQ=DAILY;COUNT=2\r
EXDATE;TZID=Asia/Shanghai:20260907T090000\r
SUMMARY:折叠\r
标题\r
END:VEVENT\r
BEGIN:VEVENT\r
UID:multi-day\r
DTSTART;TZID=Asia/Shanghai:20260905T230000\r
DTEND;TZID=Asia/Shanghai:20260906T010000\r
SUMMARY:跨天事件\r
END:VEVENT\r
END:VCALENDAR\r
"""
events = parse_ics_events(
ics,
"测试日历",
"#fff",
datetime(2026, 9, 5, 16, tzinfo=UTC),
datetime(2026, 9, 6, 16, tzinfo=UTC),
)
assert [(row["title"], row["starts_at"].isoformat()) for row in events] == [
("折叠标题", "2026-09-06T01:00:00+00:00"),
("跨天事件", "2026-09-05T15:00:00+00:00"),
]
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
from backend.mvp import _validated_calendar_target
monkeypatch.setattr(
"backend.mvp.socket.getaddrinfo",
lambda *_args, **_kwargs: [(None, None, None, None, ("127.0.0.1", 80))],
)
try:
_validated_calendar_target("http://example.com/calendar.ics")
except HTTPException as exc:
assert exc.status_code == 422
else:
raise AssertionError("private calendar target should be rejected")
def test_recurrence_mutations_keep_exact_timestamp_validation(client):
inbox = boot(client)