diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-20 11:58:40 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-20 11:58:40 -0400 |
| commit | 4a6201dd0117df55d164cee969f7c3c8123f6b28 (patch) | |
| tree | 8e71986b40f19c3fa598ed436a648795b58784d8 /tests/test-calendar-sync--sync-dispatch.el | |
| parent | d6a995b9090ca35190e59e765d5c14daf887e9d8 (diff) | |
| download | dotemacs-4a6201dd0117df55d164cee969f7c3c8123f6b28.tar.gz dotemacs-4a6201dd0117df55d164cee969f7c3c8123f6b28.zip | |
feat(calendar-sync): dispatch Google calendars through API helper
The Python helper from d6a995b could fetch and render on its own, but nothing in Emacs called it. This wires it in. Each entry in calendar-sync-calendars now takes a :fetcher key. 'api routes through the helper, and the default 'ics keeps the existing curl + Elisp parser path. Proton and any plain .ics feed work unchanged because the key defaults to 'ics.
The 'api path reads :account and :calendar-id off the calendar plist, builds the helper command (honoring the past/future window and the calendar-sync-skip-declined toggle), and runs it through make-process. The script writes the org file directly, so the sentinel only handles state bookkeeping and failure reporting, the same as the .ics worker.
I split the old --sync-calendar body into --sync-calendar-ics and turned --sync-calendar into a dispatcher. The command builder and script-path resolution are pure functions, tested directly. The dispatch routing is tested with both leaf syncers stubbed, so no process runs. I added 14 tests across the two new files, and the full suite is green.
Running the 'api path still needs the one-time OAuth bootstrap from docs/calendar-sync-api-setup.org.
Diffstat (limited to 'tests/test-calendar-sync--sync-dispatch.el')
| -rw-r--r-- | tests/test-calendar-sync--sync-dispatch.el | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/tests/test-calendar-sync--sync-dispatch.el b/tests/test-calendar-sync--sync-dispatch.el new file mode 100644 index 00000000..22deeef0 --- /dev/null +++ b/tests/test-calendar-sync--sync-dispatch.el @@ -0,0 +1,81 @@ +;;; test-calendar-sync--sync-dispatch.el --- Tests for fetcher dispatch -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for `calendar-sync--sync-calendar' dispatch. It routes a +;; calendar plist to the API helper when :fetcher is \\='api, and to the .ics +;; path otherwise (\\='ics, nil, or any other value). The two leaf syncers are +;; stubbed so no external process runs. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'calendar-sync) + +(defmacro test-sync-dispatch--with-stubs (&rest body) + "Run BODY with both leaf syncers stubbed to record their calls. +Binds `api-calls' and `ics-calls' to lists of the calendars each received." + (declare (indent 0)) + `(let ((api-calls '()) + (ics-calls '())) + (cl-letf (((symbol-function 'calendar-sync--sync-calendar-api) + (lambda (cal) (push cal api-calls))) + ((symbol-function 'calendar-sync--sync-calendar-ics) + (lambda (cal) (push cal ics-calls)))) + ,@body))) + +;;; Normal + +(ert-deftest test-calendar-sync--sync-dispatch-normal-api-fetcher () + "Normal: :fetcher \\='api routes to the API syncer only." + (test-sync-dispatch--with-stubs + (let ((cal '(:name "google" :fetcher api :account "work" + :calendar-id "primary" :file "/tmp/gcal.org"))) + (calendar-sync--sync-calendar cal) + (should (equal (list cal) api-calls)) + (should (null ics-calls))))) + +(ert-deftest test-calendar-sync--sync-dispatch-normal-ics-fetcher () + "Normal: :fetcher \\='ics routes to the .ics syncer only." + (test-sync-dispatch--with-stubs + (let ((cal '(:name "proton" :fetcher ics :url "https://x/y.ics" + :file "/tmp/pcal.org"))) + (calendar-sync--sync-calendar cal) + (should (equal (list cal) ics-calls)) + (should (null api-calls))))) + +;;; Boundary + +(ert-deftest test-calendar-sync--sync-dispatch-boundary-missing-fetcher-defaults-ics () + "Boundary: a calendar with no :fetcher key defaults to the .ics path. +This is what keeps existing Proton/.ics config working unchanged." + (test-sync-dispatch--with-stubs + (let ((cal '(:name "legacy" :url "https://x/y.ics" :file "/tmp/c.org"))) + (calendar-sync--sync-calendar cal) + (should (equal (list cal) ics-calls)) + (should (null api-calls))))) + +(ert-deftest test-calendar-sync--sync-dispatch-boundary-nil-fetcher-defaults-ics () + "Boundary: an explicit :fetcher nil also defaults to the .ics path." + (test-sync-dispatch--with-stubs + (let ((cal '(:name "legacy" :fetcher nil :url "https://x/y.ics" + :file "/tmp/c.org"))) + (calendar-sync--sync-calendar cal) + (should (equal (list cal) ics-calls)) + (should (null api-calls))))) + +;;; Error + +(ert-deftest test-calendar-sync--sync-dispatch-error-unknown-fetcher-defaults-ics () + "Error: an unrecognized :fetcher value falls back to the .ics path. +Only \\='api is special-cased; anything else takes the safe default rather +than crashing." + (test-sync-dispatch--with-stubs + (let ((cal '(:name "weird" :fetcher carrier-pigeon :url "https://x/y.ics" + :file "/tmp/c.org"))) + (calendar-sync--sync-calendar cal) + (should (equal (list cal) ics-calls)) + (should (null api-calls))))) + +(provide 'test-calendar-sync--sync-dispatch) +;;; test-calendar-sync--sync-dispatch.el ends here |
