diff options
| -rw-r--r-- | tests/test-calendar-sync--apply-single-exception.el | 67 | ||||
| -rw-r--r-- | tests/test-calendar-sync--exdate-matches-p.el | 47 | ||||
| -rw-r--r-- | tests/test-calendar-sync--extract-cn.el | 48 | ||||
| -rw-r--r-- | tests/test-calendar-sync--extract-email.el | 42 | ||||
| -rw-r--r-- | tests/test-calendar-sync--occurrence-matches-exception-p.el | 56 | ||||
| -rw-r--r-- | tests/test-calendar-sync--unfold-continuation.el | 53 |
6 files changed, 313 insertions, 0 deletions
diff --git a/tests/test-calendar-sync--apply-single-exception.el b/tests/test-calendar-sync--apply-single-exception.el new file mode 100644 index 00000000..2fcf7c71 --- /dev/null +++ b/tests/test-calendar-sync--apply-single-exception.el @@ -0,0 +1,67 @@ +;;; test-calendar-sync--apply-single-exception.el --- Tests for exception application -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--apply-single-exception. +;; Applies exception overrides to an occurrence plist, returning a modified copy. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--apply-single-exception-normal-updates-time () + "Exception start/end override the occurrence times." + (let ((occ (list :start '(2026 3 15 14 0) :end '(2026 3 15 15 0) + :summary "Standup" :uid "abc")) + (exc (list :start '(2026 3 15 16 0) :end '(2026 3 15 17 0)))) + (let ((result (calendar-sync--apply-single-exception occ exc))) + (should (equal '(2026 3 15 16 0) (plist-get result :start))) + (should (equal '(2026 3 15 17 0) (plist-get result :end)))))) + +(ert-deftest test-calendar-sync--apply-single-exception-normal-updates-summary () + "Exception with summary overrides the occurrence summary." + (let ((occ (list :start '(2026 3 15 14 0) :summary "Original")) + (exc (list :start '(2026 3 15 14 0) :summary "Modified"))) + (let ((result (calendar-sync--apply-single-exception occ exc))) + (should (equal "Modified" (plist-get result :summary)))))) + +(ert-deftest test-calendar-sync--apply-single-exception-normal-preserves-unset-fields () + "Fields not present in exception are preserved from occurrence." + (let ((occ (list :start '(2026 3 15 14 0) :summary "Keep" :uid "abc" + :location "Room 1")) + (exc (list :start '(2026 3 15 16 0)))) + (let ((result (calendar-sync--apply-single-exception occ exc))) + (should (equal "Keep" (plist-get result :summary))) + (should (equal "abc" (plist-get result :uid))) + (should (equal "Room 1" (plist-get result :location)))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--apply-single-exception-boundary-nil-end () + "Exception without :end preserves occurrence :end." + (let ((occ (list :start '(2026 3 15 14 0) :end '(2026 3 15 15 0))) + (exc (list :start '(2026 3 15 16 0)))) + (let ((result (calendar-sync--apply-single-exception occ exc))) + (should (equal '(2026 3 15 15 0) (plist-get result :end)))))) + +(ert-deftest test-calendar-sync--apply-single-exception-boundary-does-not-mutate () + "Original occurrence plist is not mutated." + (let ((occ (list :start '(2026 3 15 14 0) :summary "Original")) + (exc (list :start '(2026 3 15 16 0) :summary "Changed"))) + (calendar-sync--apply-single-exception occ exc) + (should (equal "Original" (plist-get occ :summary))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--apply-single-exception-error-empty-exception () + "Exception with no fields still returns a valid plist with occurrence data." + (let ((occ (list :start '(2026 3 15 14 0) :summary "Keep")) + (exc (list :start nil))) + (let ((result (calendar-sync--apply-single-exception occ exc))) + (should (equal "Keep" (plist-get result :summary)))))) + +(provide 'test-calendar-sync--apply-single-exception) +;;; test-calendar-sync--apply-single-exception.el ends here diff --git a/tests/test-calendar-sync--exdate-matches-p.el b/tests/test-calendar-sync--exdate-matches-p.el new file mode 100644 index 00000000..62c2f21e --- /dev/null +++ b/tests/test-calendar-sync--exdate-matches-p.el @@ -0,0 +1,47 @@ +;;; test-calendar-sync--exdate-matches-p.el --- Tests for EXDATE matching -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--exdate-matches-p. +;; Checks if an occurrence start matches an excluded date. +;; Date-only EXDATEs (nil hour) match any time on that day. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--exdate-matches-p-normal-exact-match () + "Exact datetime match returns t." + (should (calendar-sync--exdate-matches-p '(2026 3 15 14 0) '(2026 3 15 14 0)))) + +(ert-deftest test-calendar-sync--exdate-matches-p-normal-no-match () + "Different date returns nil." + (should-not (calendar-sync--exdate-matches-p '(2026 3 15 14 0) '(2026 3 16 14 0)))) + +(ert-deftest test-calendar-sync--exdate-matches-p-normal-date-only-wildcard () + "Date-only EXDATE (nil hour) matches any time on that day." + (should (calendar-sync--exdate-matches-p '(2026 3 15 14 0) '(2026 3 15 nil nil))) + (should (calendar-sync--exdate-matches-p '(2026 3 15 9 30) '(2026 3 15 nil nil)))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--exdate-matches-p-boundary-different-time () + "Same date but different time with timed EXDATE returns nil." + (should-not (calendar-sync--exdate-matches-p '(2026 3 15 14 0) '(2026 3 15 15 0)))) + +(ert-deftest test-calendar-sync--exdate-matches-p-boundary-nil-minute-vs-zero () + "Nil minute in occurrence treated as 0." + (should (calendar-sync--exdate-matches-p '(2026 3 15 14 nil) '(2026 3 15 14 0)))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--exdate-matches-p-error-nil-inputs () + "Nil occurrence-start or exdate returns nil." + (should-not (calendar-sync--exdate-matches-p nil '(2026 3 15 14 0))) + (should-not (calendar-sync--exdate-matches-p '(2026 3 15 14 0) nil))) + +(provide 'test-calendar-sync--exdate-matches-p) +;;; test-calendar-sync--exdate-matches-p.el ends here diff --git a/tests/test-calendar-sync--extract-cn.el b/tests/test-calendar-sync--extract-cn.el new file mode 100644 index 00000000..0cb8f80c --- /dev/null +++ b/tests/test-calendar-sync--extract-cn.el @@ -0,0 +1,48 @@ +;;; test-calendar-sync--extract-cn.el --- Tests for CN parameter extraction -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--extract-cn. +;; Extracts and dequotes the CN= parameter from iCal property lines. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--extract-cn-normal-unquoted () + "Extracts unquoted CN value." + (should (equal "Craig Jennings" + (calendar-sync--extract-cn + "ATTENDEE;CN=Craig Jennings;PARTSTAT=ACCEPTED:mailto:c@test.com")))) + +(ert-deftest test-calendar-sync--extract-cn-normal-quoted () + "Strips surrounding quotes from CN value." + (should (equal "Craig Jennings" + (calendar-sync--extract-cn + "ATTENDEE;CN=\"Craig Jennings\";PARTSTAT=ACCEPTED:mailto:c@test.com")))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--extract-cn-boundary-cn-at-end () + "CN as last parameter before colon." + (should (equal "Bob" + (calendar-sync--extract-cn "ORGANIZER;CN=Bob:mailto:bob@test.com")))) + +(ert-deftest test-calendar-sync--extract-cn-boundary-special-chars () + "CN with accented characters." + (should (equal "José García" + (calendar-sync--extract-cn + "ATTENDEE;CN=José García;PARTSTAT=ACCEPTED:mailto:j@test.com")))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--extract-cn-error-no-cn () + "Line without CN= returns nil." + (should (null (calendar-sync--extract-cn + "ATTENDEE;PARTSTAT=ACCEPTED:mailto:c@test.com")))) + +(provide 'test-calendar-sync--extract-cn) +;;; test-calendar-sync--extract-cn.el ends here diff --git a/tests/test-calendar-sync--extract-email.el b/tests/test-calendar-sync--extract-email.el new file mode 100644 index 00000000..75cc9e58 --- /dev/null +++ b/tests/test-calendar-sync--extract-email.el @@ -0,0 +1,42 @@ +;;; test-calendar-sync--extract-email.el --- Tests for email extraction -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--extract-email. +;; Extracts email address from mailto: values in iCal property lines. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--extract-email-normal-standard () + "Extracts email from standard mailto: value." + (should (equal "craig@test.com" + (calendar-sync--extract-email + "ATTENDEE;CN=Craig:mailto:craig@test.com")))) + +(ert-deftest test-calendar-sync--extract-email-normal-organizer () + "Works on ORGANIZER lines too." + (should (equal "boss@corp.com" + (calendar-sync--extract-email + "ORGANIZER;CN=Boss:mailto:boss@corp.com")))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--extract-email-boundary-plus-addressing () + "Handles plus-addressed emails." + (should (equal "user+tag@test.com" + (calendar-sync--extract-email + "ATTENDEE:mailto:user+tag@test.com")))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--extract-email-error-no-mailto () + "Line without mailto: returns nil." + (should (null (calendar-sync--extract-email "ATTENDEE;CN=Craig:urn:invalid")))) + +(provide 'test-calendar-sync--extract-email) +;;; test-calendar-sync--extract-email.el ends here diff --git a/tests/test-calendar-sync--occurrence-matches-exception-p.el b/tests/test-calendar-sync--occurrence-matches-exception-p.el new file mode 100644 index 00000000..416dbb26 --- /dev/null +++ b/tests/test-calendar-sync--occurrence-matches-exception-p.el @@ -0,0 +1,56 @@ +;;; test-calendar-sync--occurrence-matches-exception-p.el --- Tests for occurrence-exception matching -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--occurrence-matches-exception-p. +;; Compares occurrence :start against exception :recurrence-id on year/month/day/hour/minute. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-normal-exact-match () + "Identical datetime in occurrence and exception returns t." + (let ((occ (list :start '(2026 3 15 14 0))) + (exc (list :recurrence-id '(2026 3 15 14 0)))) + (should (calendar-sync--occurrence-matches-exception-p occ exc)))) + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-normal-no-match () + "Different dates return nil." + (let ((occ (list :start '(2026 3 15 14 0))) + (exc (list :recurrence-id '(2026 3 16 14 0)))) + (should-not (calendar-sync--occurrence-matches-exception-p occ exc)))) + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-normal-all-day () + "All-day events (nil hour/minute) match when dates are equal." + (let ((occ (list :start '(2026 3 15 nil nil))) + (exc (list :recurrence-id '(2026 3 15 nil nil)))) + (should (calendar-sync--occurrence-matches-exception-p occ exc)))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-boundary-different-time () + "Same date but different hour returns nil." + (let ((occ (list :start '(2026 3 15 14 0))) + (exc (list :recurrence-id '(2026 3 15 15 0)))) + (should-not (calendar-sync--occurrence-matches-exception-p occ exc)))) + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-boundary-nil-minute-vs-zero () + "Nil minute treated as 0 for comparison." + (let ((occ (list :start '(2026 3 15 14 nil))) + (exc (list :recurrence-id '(2026 3 15 14 0)))) + (should (calendar-sync--occurrence-matches-exception-p occ exc)))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--occurrence-matches-exception-p-error-nil-start () + "Nil :start returns nil." + (let ((occ (list :summary "No start")) + (exc (list :recurrence-id '(2026 3 15 14 0)))) + (should-not (calendar-sync--occurrence-matches-exception-p occ exc)))) + +(provide 'test-calendar-sync--occurrence-matches-exception-p) +;;; test-calendar-sync--occurrence-matches-exception-p.el ends here diff --git a/tests/test-calendar-sync--unfold-continuation.el b/tests/test-calendar-sync--unfold-continuation.el new file mode 100644 index 00000000..6c6e1705 --- /dev/null +++ b/tests/test-calendar-sync--unfold-continuation.el @@ -0,0 +1,53 @@ +;;; test-calendar-sync--unfold-continuation.el --- Tests for RFC 5545 line unfolding -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for calendar-sync--unfold-continuation. +;; Unfolds RFC 5545 continuation lines (lines starting with space/tab after newline). +;; Returns (unfolded-value . new-position) cons. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--unfold-continuation-normal-single-continuation () + "Single continuation line is appended to value." + ;; Simulate how get-property calls unfold-continuation: match-end is + ;; the position right after the matched value on the first line. + ;; "DESCRIPTION:First part" is 22 chars, so match-end for the value = 22. + ;; The \n at position 22 starts the continuation check. + (let* ((text "DESCRIPTION:First part\n Second part\nNEXT:value")) + ;; Use get-property which exercises unfold-continuation with correct positions + (should (equal "First partSecond part" + (calendar-sync--get-property text "DESCRIPTION"))))) + +(ert-deftest test-calendar-sync--unfold-continuation-normal-multiple-continuations () + "Multiple continuation lines are all appended." + (let* ((text "DESC:Line1\n Line2\n Line3\nNEXT:x")) + (should (equal "Line1Line2Line3" + (calendar-sync--get-property text "DESC"))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--unfold-continuation-boundary-no-continuation () + "No continuation lines returns value unchanged." + (let* ((text "SUMMARY:Test\nDTSTART:20260315")) + (should (equal "Test" (calendar-sync--get-property text "SUMMARY"))))) + +(ert-deftest test-calendar-sync--unfold-continuation-boundary-tab-continuation () + "Tab-indented continuation lines are also unfolded." + (let* ((text "DESC:Start\n\tTabbed\nNEXT:x")) + (should (equal "StartTabbed" (calendar-sync--get-property text "DESC"))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--unfold-continuation-error-start-at-end () + "Value at end of text with no continuation returns unchanged." + (let* ((text "SUMMARY:Test")) + (should (equal "Test" (calendar-sync--get-property text "SUMMARY"))))) + +(provide 'test-calendar-sync--unfold-continuation) +;;; test-calendar-sync--unfold-continuation.el ends here |
