aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/island/calendar/test_calctl.py
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/island/calendar/test_calctl.py')
-rw-r--r--desktop/island/calendar/test_calctl.py101
1 files changed, 90 insertions, 11 deletions
diff --git a/desktop/island/calendar/test_calctl.py b/desktop/island/calendar/test_calctl.py
index 98973ee..91ad384 100644
--- a/desktop/island/calendar/test_calctl.py
+++ b/desktop/island/calendar/test_calctl.py
@@ -117,7 +117,7 @@ END:VALARM
END:VEVENT
END:VCALENDAR
""")
-w = run("get", "weird-1")
+w = run("get", "weird-1", "home")
assert w["repeat"] == "custom" and w["alarm"] == "custom" and w["title"] == "Standup", w
assert w["start"] == "2026-10-05T09:00", w
occ = [e["start"] for e in run("list", "2026-10-01", "2026-11-01") if e["uid"] == "weird-1"]
@@ -128,7 +128,7 @@ assert len(occ) == 8 and occ[0] == "2026-10-05T09:00" and "2026-10-26T09:00" in
assert run("list", "2026-10-01", "2026-11-01")
# Unknown uid and bad usage: the error envelope, exit 1.
-assert "no event" in run("get", "nope", ok=False)
+assert "no event" in run("get", "nope", "home", ok=False)
assert "usage" in run("frobnicate", ok=False)
def ev(**kw):
@@ -156,7 +156,7 @@ assert len(mine) == 1, mine
assert mine[0]["start"] == "2026-10-15T14:30" and mine[0]["end"] == "2026-10-15T15:30"
assert mine[0]["alarm"] == "1h" and mine[0]["repeat"] == "none" and mine[0]["color"] == "green"
assert mine[0]["override"] is False
-assert run("get", uid)["title"] == "Dentist"
+assert run("get", uid, "home")["title"] == "Dentist"
# all-day, multi-day: exclusive DTEND on disk, inclusive end in JSON.
trip = run("new", ev(title="Trip", allDay=True, start="2026-10-30", end="2026-11-02", alarm="none"))["uid"]
@@ -171,11 +171,19 @@ assert not [e for e in run("list", "2026-10-01", "2026-10-30") if e["uid"] == tr
gym = run("new", ev(title="Gym", repeat="weekly", until="2026-10-29"))["uid"]
occ = [e["start"] for e in run("list", "2026-10-01", "2026-12-01") if e["uid"] == gym]
assert occ == ["2026-10-15T14:30", "2026-10-22T14:30", "2026-10-29T14:30"], occ
-assert run("get", gym)["until"] == "2026-10-29"
+assert os.stat(tmp / "a" / f"{gym}.ics").st_mode & 0o777 == 0o644
+
+# A late-evening series keeps its last occurrence: UNTIL is UTC on disk and
+# read back in local time.
+late = run("new", ev(title="Late", start="2026-10-05T22:30", end="2026-10-05T23:00",
+ repeat="weekly", until="2026-10-19"))["uid"]
+occ = [e["start"] for e in run("list", "2026-10-01", "2026-11-01") if e["uid"] == late]
+assert occ == ["2026-10-05T22:30", "2026-10-12T22:30", "2026-10-19T22:30"], occ
+assert run("get", gym, "home")["until"] == "2026-10-29"
# edit keeping custom: the rich rule, both alarms and an unknown property
# survive; SEQUENCE goes up.
-run("edit", "weird-1", ev(title="Standup v2", start="2026-10-05T09:30",
+run("edit", "weird-1", "home", ev(title="Standup v2", start="2026-10-05T09:30",
end="2026-10-05T10:00", repeat="custom", alarm="custom"))
t = (tmp / "a" / "weird.ics").read_text()
assert "BYDAY=MO,WE" in t and t.count("BEGIN:VALARM") == 2 and "X-FOO:bar" in t, t
@@ -183,24 +191,95 @@ assert "SUMMARY:Standup v2" in t and "SEQUENCE:1" in t, t
no_temp()
# edit to presets: the rule goes, the alarms become one.
-run("edit", "weird-1", ev(title="Standup v2", start="2026-10-05T09:30",
+run("edit", "weird-1", "home", ev(title="Standup v2", start="2026-10-05T09:30",
end="2026-10-05T10:00", repeat="none", alarm="10m"))
t = (tmp / "a" / "weird.ics").read_text()
assert "RRULE" not in t and t.count("BEGIN:VALARM") == 1 and "TRIGGER:-PT10M" in t, t
+# a custom rule cannot have its date moved: its BY* parts would stay on the
+# old day.
+put(tmp / "a" / "weird2.ics", """
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//test//EN
+BEGIN:VEVENT
+UID:weird-2
+DTSTAMP:20260101T000000Z
+DTSTART;TZID=Europe/Rome:20261005T090000
+DTEND;TZID=Europe/Rome:20261005T100000
+RRULE:FREQ=WEEKLY;BYDAY=MO
+SUMMARY:Standup
+END:VEVENT
+END:VCALENDAR
+""")
+assert "rule" in run("edit", "weird-2", "home", ev(title="Standup", start="2026-10-07T09:00",
+ end="2026-10-07T10:00", repeat="custom",
+ alarm="none"), ok=False)
+
+# an edit that changes nothing leaves the event's own encoding alone: zone,
+# DURATION, the alarm's ACTION and extras, SUMMARY's parameter.
+put(tmp / "a" / "foreign.ics", """
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//test//EN
+BEGIN:VEVENT
+UID:foreign-1
+DTSTAMP:20260101T000000Z
+DTSTART;TZID=America/New_York:20261005T100000
+DURATION:PT1H
+SUMMARY;LANGUAGE=it:Chiamata
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER:-PT1H
+ACKNOWLEDGED:20261001T080000Z
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""")
+g = run("get", "foreign-1", "home")
+same = {k: g[k] for k in ("calendar", "title", "allDay", "start", "end", "repeat", "until",
+ "alarm", "location", "notes")}
+run("edit", "foreign-1", "home", json.dumps(same))
+t = (tmp / "a" / "foreign.ics").read_text()
+for kept in ("DTSTART;TZID=America/New_York:20261005T100000", "DURATION:PT1H", "ACTION:AUDIO",
+ "ACKNOWLEDGED:20261001T080000Z", "SUMMARY;LANGUAGE=it:Chiamata"):
+ assert kept in t, (kept, t)
+# Changing only the title rewrites the title, nothing else.
+run("edit", "foreign-1", "home", json.dumps(dict(same, title="Call")))
+t = (tmp / "a" / "foreign.ics").read_text()
+assert "SUMMARY:Call" in t and "TZID=America/New_York" in t and "ACTION:AUDIO" in t, t
+
+# the same UID in two calendars: each command acts on the copy it names.
+for d in ("a", "b"):
+ put(tmp / d / "dup.ics", f"""
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//test//EN
+BEGIN:VEVENT
+UID:dup-1
+DTSTAMP:20260101T000000Z
+DTSTART;VALUE=DATE:20261020
+SUMMARY:copy {d}
+END:VEVENT
+END:VCALENDAR
+""")
+assert run("get", "dup-1", "birthdays")["title"] == "copy b"
+run("delete", "dup-1", "home")
+assert not (tmp / "a" / "dup.ics").exists() and (tmp / "b" / "dup.ics").exists()
+
# a calendar change moves the file.
-run("edit", uid, ev(calendar="birthdays"))
+run("edit", uid, "home", ev(calendar="birthdays"))
assert not f.exists() and (tmp / "b" / f.name).exists()
-assert run("get", uid)["calendar"] == "birthdays"
+assert run("get", uid, "birthdays")["calendar"] == "birthdays"
# cal-notif's per-event override is reported.
(tmp / "overrides.toml").write_text(f'"{uid}" = ["10m"]\n')
-assert run("get", uid)["override"] is True
+assert run("get", uid, "birthdays")["override"] is True
# delete.
-assert run("delete", uid) == {}
+assert run("delete", uid, "birthdays") == {}
assert not (tmp / "b" / f.name).exists()
-assert "no event" in run("get", uid, ok=False)
+assert "no event" in run("get", uid, "birthdays", ok=False)
# validation: the error envelope, exit 1, nothing written.
before = sorted(p.name for p in tmp.rglob("*.ics"))