aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-calendar-sync--syncing-p.el84
1 files changed, 84 insertions, 0 deletions
diff --git a/tests/test-calendar-sync--syncing-p.el b/tests/test-calendar-sync--syncing-p.el
new file mode 100644
index 000000000..b346bf776
--- /dev/null
+++ b/tests/test-calendar-sync--syncing-p.el
@@ -0,0 +1,84 @@
+;;; test-calendar-sync--syncing-p.el --- Tests for the in-flight sync guard -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Unit tests for `calendar-sync--syncing-p' (the per-calendar in-flight check
+;; that lets the dispatcher skip an overlapping timer tick) and for the
+;; load-state sanitize that clears a stale `syncing' status in a fresh process.
+
+;;; Code:
+
+(require 'ert)
+(require 'calendar-sync)
+
+(defun test-cs-syncing--reset ()
+ "Clear the module's per-calendar state hash."
+ (clrhash calendar-sync--calendar-states))
+
+;;; calendar-sync--syncing-p
+
+(ert-deftest test-calendar-sync--syncing-p-normal-true-when-syncing ()
+ "Normal: a calendar whose status is `syncing' reads as in-flight."
+ (test-cs-syncing--reset)
+ (calendar-sync--set-calendar-state "google" '(:status syncing))
+ (should (calendar-sync--syncing-p "google")))
+
+(ert-deftest test-calendar-sync--syncing-p-boundary-nil-when-no-state ()
+ "Boundary: a calendar with no recorded state is not in-flight."
+ (test-cs-syncing--reset)
+ (should-not (calendar-sync--syncing-p "never-seen")))
+
+(ert-deftest test-calendar-sync--syncing-p-error-nil-for-terminal-status ()
+ "Error: a terminal status (ok / error) is not in-flight."
+ (test-cs-syncing--reset)
+ (calendar-sync--set-calendar-state "google" '(:status ok))
+ (should-not (calendar-sync--syncing-p "google"))
+ (calendar-sync--set-calendar-state "proton" '(:status error))
+ (should-not (calendar-sync--syncing-p "proton")))
+
+;;; Dispatcher guard: an in-flight calendar skips both leaf syncers
+
+(ert-deftest test-calendar-sync--sync-calendar-skips-when-in-flight ()
+ "Normal: `calendar-sync--sync-calendar' does not launch a second sync for a
+calendar already marked syncing, so an overlapping timer tick is a no-op."
+ (test-cs-syncing--reset)
+ (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))))
+ (calendar-sync--set-calendar-state "proton" '(:status syncing))
+ (calendar-sync--sync-calendar '(:name "proton" :url "https://x/y.ics"
+ :file "/tmp/c.org"))
+ (should (null api-calls))
+ (should (null ics-calls)))))
+
+(ert-deftest test-calendar-sync--sync-calendar-dispatches-when-idle ()
+ "Boundary: an idle calendar (no in-flight status) still dispatches normally."
+ (test-cs-syncing--reset)
+ (let ((ics-calls '()))
+ (cl-letf (((symbol-function 'calendar-sync--sync-calendar-ics)
+ (lambda (cal) (push cal ics-calls))))
+ (calendar-sync--sync-calendar '(:name "proton" :url "https://x/y.ics"
+ :file "/tmp/c.org"))
+ (should (= 1 (length ics-calls))))))
+
+;;; load-state sanitize: a persisted `syncing' status is cleared on load
+
+(ert-deftest test-calendar-sync--load-state-clears-stale-syncing ()
+ "Error: a `syncing' status persisted before a crash is reset on load, so the
+in-flight guard cannot skip that calendar forever in the new session."
+ (test-cs-syncing--reset)
+ (let* ((dir (make-temp-file "cs-state-" t))
+ (calendar-sync--state-file (expand-file-name "state.el" dir)))
+ (unwind-protect
+ (progn
+ (with-temp-file calendar-sync--state-file
+ (prin1 '((timezone-offset . nil)
+ (calendar-states . (("google" . (:status syncing)))))
+ (current-buffer)))
+ (calendar-sync--load-state)
+ (should-not (calendar-sync--syncing-p "google")))
+ (delete-directory dir t))))
+
+(provide 'test-calendar-sync--syncing-p)
+;;; test-calendar-sync--syncing-p.el ends here