summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-calendar-sync--api-command.el99
-rw-r--r--tests/test-calendar-sync--sync-dispatch.el81
2 files changed, 180 insertions, 0 deletions
diff --git a/tests/test-calendar-sync--api-command.el b/tests/test-calendar-sync--api-command.el
new file mode 100644
index 00000000..21c09f51
--- /dev/null
+++ b/tests/test-calendar-sync--api-command.el
@@ -0,0 +1,99 @@
+;;; test-calendar-sync--api-command.el --- Tests for API command builder -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Unit tests for the Google Calendar API fetch path's pure helpers:
+;; `calendar-sync--api-script' (resolves the helper script path) and
+;; `calendar-sync--api-command' (builds the make-process argument list).
+;; Covers Normal, Boundary, and Error cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'calendar-sync)
+
+;;; calendar-sync--api-script
+
+(ert-deftest test-calendar-sync--api-script-normal-resolves-to-helper ()
+ "Normal: the script path ends with the helper filename and is absolute."
+ (let ((path (calendar-sync--api-script)))
+ (should (file-name-absolute-p path))
+ (should (string-suffix-p "scripts/calendar_sync_api.py" path))))
+
+(ert-deftest test-calendar-sync--api-script-normal-no-dotdot ()
+ "Normal: the resolved path is collapsed (no literal ../ segment)."
+ (let ((path (calendar-sync--api-script)))
+ (should-not (string-match-p "\\.\\./" path))))
+
+;;; calendar-sync--api-command — Normal
+
+(ert-deftest test-calendar-sync--api-command-normal-structure ()
+ "Normal: command starts with the python binary + script, then the flags."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-past-months 3)
+ (calendar-sync-future-months 12)
+ (calendar-sync-skip-declined t))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should (equal (nth 0 cmd) "python3"))
+ (should (string-suffix-p "calendar_sync_api.py" (nth 1 cmd)))
+ (should (member "--account" cmd))
+ (should (member "work" cmd))
+ (should (member "--calendar-id" cmd))
+ (should (member "primary" cmd))
+ (should (member "--output" cmd))
+ (should (member "/tmp/out.org" cmd)))))
+
+(ert-deftest test-calendar-sync--api-command-normal-flag-pairing ()
+ "Normal: each value immediately follows its flag."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-skip-declined t))
+ (let ((cmd (calendar-sync--api-command "personal" "abc123" "/tmp/gcal.org")))
+ (should (equal "personal" (nth (1+ (cl-position "--account" cmd :test #'equal)) cmd)))
+ (should (equal "abc123" (nth (1+ (cl-position "--calendar-id" cmd :test #'equal)) cmd)))
+ (should (equal "/tmp/gcal.org" (nth (1+ (cl-position "--output" cmd :test #'equal)) cmd))))))
+
+(ert-deftest test-calendar-sync--api-command-normal-window-from-defvars ()
+ "Normal: past/future month flags reflect the configured defvars."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-past-months 6)
+ (calendar-sync-future-months 9)
+ (calendar-sync-skip-declined t))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should (equal "6" (nth (1+ (cl-position "--past-months" cmd :test #'equal)) cmd)))
+ (should (equal "9" (nth (1+ (cl-position "--future-months" cmd :test #'equal)) cmd))))))
+
+(ert-deftest test-calendar-sync--api-command-normal-honors-python-command ()
+ "Normal: a custom python command is used as argv[0]."
+ (let ((calendar-sync-python-command "/usr/bin/python3.14")
+ (calendar-sync-skip-declined t))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should (equal "/usr/bin/python3.14" (nth 0 cmd))))))
+
+;;; calendar-sync--api-command — Boundary (declined toggle)
+
+(ert-deftest test-calendar-sync--api-command-boundary-skip-declined-omits-flag ()
+ "Boundary: with skip-declined on (default), --keep-declined is absent.
+The helper filters declines by default, matching the .ics path."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-skip-declined t))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should-not (member "--keep-declined" cmd)))))
+
+(ert-deftest test-calendar-sync--api-command-boundary-keep-declined-adds-flag ()
+ "Boundary: with skip-declined nil, --keep-declined is passed through.
+This keeps the API path honoring the same toggle as the parser path."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-skip-declined nil))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should (member "--keep-declined" cmd)))))
+
+;;; calendar-sync--api-command — Error
+
+(ert-deftest test-calendar-sync--api-command-error-returns-string-list ()
+ "Error: every element of the command list is a string (make-process safe)."
+ (let ((calendar-sync-python-command "python3")
+ (calendar-sync-skip-declined nil))
+ (let ((cmd (calendar-sync--api-command "work" "primary" "/tmp/out.org")))
+ (should (cl-every #'stringp cmd)))))
+
+(provide 'test-calendar-sync--api-command)
+;;; test-calendar-sync--api-command.el ends here
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