aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/island/calendar/test_calctl.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-13 10:25:10 +0200
committerDanilo M. <danix@danix.xyz>2026-10-13 10:25:10 +0200
commit091abf04a3e9be7ce36a3bc91441048085f56900 (patch)
treee757b465b29c48aba51ff425e33e7e4fbe0f6955 /desktop/island/calendar/test_calctl.py
parent9bc2dba1f595ed5ed7121d17b3361a718cc3b1a6 (diff)
downloadquickshell-091abf04a3e9be7ce36a3bc91441048085f56900.tar.gz
quickshell-091abf04a3e9be7ce36a3bc91441048085f56900.zip
feat(calendar): calctl creates, edits and deletes events
Edits change only what the form owns: a custom rule, extra alarms and properties this tool does not know survive, which the test checks on an event with BYDAY, two alarms and an X- property. Writes go through a temporary file and a rename so khal, cal-notif and vdirsyncer never read half a file, and a vdirsyncer sync is started detached afterwards. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/island/calendar/test_calctl.py')
-rw-r--r--desktop/island/calendar/test_calctl.py80
1 files changed, 79 insertions, 1 deletions
diff --git a/desktop/island/calendar/test_calctl.py b/desktop/island/calendar/test_calctl.py
index e6acd10..98973ee 100644
--- a/desktop/island/calendar/test_calctl.py
+++ b/desktop/island/calendar/test_calctl.py
@@ -131,7 +131,85 @@ assert run("list", "2026-10-01", "2026-11-01")
assert "no event" in run("get", "nope", ok=False)
assert "usage" in run("frobnicate", ok=False)
-# --- writes (Task 3) go here ---
+def ev(**kw):
+ e = {"calendar": "home", "title": "Dentist", "allDay": False,
+ "start": "2026-10-15T14:30", "end": "2026-10-15T15:30",
+ "repeat": "none", "until": "", "alarm": "1h", "location": "", "notes": ""}
+ e.update(kw)
+ return json.dumps(e)
+
+
+def no_temp():
+ assert not list(tmp.rglob(".calctl-*")), "temp file left behind"
+
+
+# new, timed: the local zone with its VTIMEZONE, the alarm; read back in
+# local time.
+uid = run("new", ev())["uid"]
+f = tmp / "a" / f"{uid}.ics"
+t = f.read_text()
+assert "DTSTART;TZID=Europe/Rome:20261015T143000" in t, t
+assert "BEGIN:VTIMEZONE" in t and "TRIGGER:-PT1H" in t, t
+no_temp()
+mine = [e for e in run("list", "2026-10-01", "2026-11-01") if e["uid"] == uid]
+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"
+
+# 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"]
+t = (tmp / "a" / f"{trip}.ics").read_text()
+assert "DTSTART;VALUE=DATE:20261030" in t and "DTEND;VALUE=DATE:20261103" in t, t
+assert "VALARM" not in t
+nov = [e for e in run("list", "2026-11-01", "2026-12-01") if e["uid"] == trip]
+assert len(nov) == 1 and nov[0]["start"] == "2026-10-30" and nov[0]["end"] == "2026-11-02", nov
+assert not [e for e in run("list", "2026-10-01", "2026-10-30") if e["uid"] == trip]
+
+# weekly with an until-date, across the DST change.
+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"
+
+# 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",
+ 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
+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",
+ 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 calendar change moves the file.
+run("edit", uid, ev(calendar="birthdays"))
+assert not f.exists() and (tmp / "b" / f.name).exists()
+assert run("get", uid)["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
+
+# delete.
+assert run("delete", uid) == {}
+assert not (tmp / "b" / f.name).exists()
+assert "no event" in run("get", uid, ok=False)
+
+# validation: the error envelope, exit 1, nothing written.
+before = sorted(p.name for p in tmp.rglob("*.ics"))
+assert "title" in run("new", ev(title=" "), ok=False)
+assert "end" in run("new", ev(end="2026-10-15T14:00"), ok=False)
+assert "calendar" in run("new", ev(calendar="nope"), ok=False)
+assert "json" in run("new", "{not json", ok=False)
+assert sorted(p.name for p in tmp.rglob("*.ics")) == before
+no_temp()
shutil.rmtree(tmp)
print("test_calctl: ok")