From b7cb1c51e5663419344d8b55766635801f3ee4c8 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 5 Feb 2026 15:13:57 -0600 Subject: =?UTF-8?q?feat(calendar-sync):=20add=20event=20details=20?= =?UTF-8?q?=E2=80=94=20attendees,=20organizer,=20status,=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ICS text unescaping (RFC 5545), HTML stripping, and new fields (attendees/status, organizer, meeting URL) to calendar-sync.el. event-to-org now outputs org property drawers. 88 new tests across 10 test files, 146/146 pass. Also fix pre-existing test require order and keymap guard issues. --- tests/test-calendar-sync--clean-text.el | 58 +++++++ tests/test-calendar-sync--convert-tz-to-local.el | 2 +- tests/test-calendar-sync--event-to-org.el | 126 +++++++++++++++ tests/test-calendar-sync--expand-weekly.el | 2 +- tests/test-calendar-sync--extract-meeting-url.el | 54 +++++++ tests/test-calendar-sync--extract-tzid.el | 1 + tests/test-calendar-sync--find-user-status.el | 89 +++++++++++ .../test-calendar-sync--get-all-property-lines.el | 61 +++++++ tests/test-calendar-sync--get-property.el | 1 + tests/test-calendar-sync--helpers.el | 1 + .../test-calendar-sync--normalize-line-endings.el | 1 + tests/test-calendar-sync--parse-attendee-line.el | 97 +++++++++++ tests/test-calendar-sync--parse-organizer.el | 50 ++++++ tests/test-calendar-sync--parse-rrule.el | 1 + tests/test-calendar-sync--strip-html.el | 99 ++++++++++++ tests/test-calendar-sync--unescape-ics-text.el | 79 +++++++++ tests/test-calendar-sync-properties.el | 2 +- tests/test-calendar-sync.el | 2 +- ...test-integration-calendar-sync-event-details.el | 177 +++++++++++++++++++++ tests/test-org-agenda-build-list.el | 7 +- 20 files changed, 903 insertions(+), 7 deletions(-) create mode 100644 tests/test-calendar-sync--clean-text.el create mode 100644 tests/test-calendar-sync--event-to-org.el create mode 100644 tests/test-calendar-sync--extract-meeting-url.el create mode 100644 tests/test-calendar-sync--find-user-status.el create mode 100644 tests/test-calendar-sync--get-all-property-lines.el create mode 100644 tests/test-calendar-sync--parse-attendee-line.el create mode 100644 tests/test-calendar-sync--parse-organizer.el create mode 100644 tests/test-calendar-sync--strip-html.el create mode 100644 tests/test-calendar-sync--unescape-ics-text.el create mode 100644 tests/test-integration-calendar-sync-event-details.el (limited to 'tests') diff --git a/tests/test-calendar-sync--clean-text.el b/tests/test-calendar-sync--clean-text.el new file mode 100644 index 00000000..86c8532b --- /dev/null +++ b/tests/test-calendar-sync--clean-text.el @@ -0,0 +1,58 @@ +;;; test-calendar-sync--clean-text.el --- Tests for clean-text composition -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--clean-text function. +;; Composes unescape-ics-text + strip-html, trims whitespace. Returns nil for nil. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--clean-text-normal-both-html-and-ics () + "Test text with both HTML tags and ICS escapes." + (should (string= "Hello, World\nNext line" + (calendar-sync--clean-text "Hello\\, World
Next line")))) + +(ert-deftest test-calendar-sync--clean-text-normal-pure-ics-escapes () + "Test text with only ICS escapes." + (should (string= "a, b; c" + (calendar-sync--clean-text "a\\, b\\; c")))) + +(ert-deftest test-calendar-sync--clean-text-normal-pure-html () + "Test text with only HTML." + (should (string= "bold and italic" + (calendar-sync--clean-text "bold and italic")))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--clean-text-boundary-already-clean () + "Test already-clean text passes through unchanged." + (should (string= "no escapes here" + (calendar-sync--clean-text "no escapes here")))) + +(ert-deftest test-calendar-sync--clean-text-boundary-empty-string () + "Test empty string returns empty string." + (should (string= "" (calendar-sync--clean-text "")))) + +(ert-deftest test-calendar-sync--clean-text-boundary-whitespace-only () + "Test whitespace-only string returns empty after trim." + (should (string= "" (calendar-sync--clean-text " \n \t ")))) + +(ert-deftest test-calendar-sync--clean-text-boundary-leading-trailing-whitespace () + "Test leading/trailing whitespace is trimmed." + (should (string= "content" + (calendar-sync--clean-text " content ")))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--clean-text-error-nil-input () + "Test nil input returns nil." + (should (null (calendar-sync--clean-text nil)))) + +(provide 'test-calendar-sync--clean-text) +;;; test-calendar-sync--clean-text.el ends here diff --git a/tests/test-calendar-sync--convert-tz-to-local.el b/tests/test-calendar-sync--convert-tz-to-local.el index cf45aa61..07822780 100644 --- a/tests/test-calendar-sync--convert-tz-to-local.el +++ b/tests/test-calendar-sync--convert-tz-to-local.el @@ -9,8 +9,8 @@ ;;; Code: (require 'ert) -(require 'calendar-sync) (require 'testutil-calendar-sync) +(require 'calendar-sync) ;;; Normal Cases diff --git a/tests/test-calendar-sync--event-to-org.el b/tests/test-calendar-sync--event-to-org.el new file mode 100644 index 00000000..e6609e20 --- /dev/null +++ b/tests/test-calendar-sync--event-to-org.el @@ -0,0 +1,126 @@ +;;; test-calendar-sync--event-to-org.el --- Tests for updated event-to-org formatter -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--event-to-org function (updated version). +;; Tests the new property drawer format with LOCATION, ORGANIZER, STATUS, URL. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--event-to-org-normal-all-fields () + "Test event with all fields produces property drawer + description." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Team Standup" + :start start + :end end + :description "Daily sync meeting" + :location "Conference Room A" + :organizer (list :cn "John Smith" :email "john@example.com") + :status "accepted" + :url "https://meet.google.com/abc-defg-hij"))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p "\\* Team Standup" result)) + (should (string-match-p ":PROPERTIES:" result)) + (should (string-match-p ":LOCATION: Conference Room A" result)) + (should (string-match-p ":ORGANIZER: John Smith" result)) + (should (string-match-p ":STATUS: accepted" result)) + (should (string-match-p ":URL: https://meet.google.com/abc-defg-hij" result)) + (should (string-match-p ":END:" result)) + (should (string-match-p "Daily sync meeting" result))))) + +(ert-deftest test-calendar-sync--event-to-org-normal-summary-and-time-only () + "Test event with only summary and time (no drawer)." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Quick Chat" + :start start + :end end))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p "\\* Quick Chat" result)) + (should-not (string-match-p ":PROPERTIES:" result))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--event-to-org-boundary-no-description () + "Test event with location but no description." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Meeting" + :start start + :end end + :location "Room B"))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p ":LOCATION: Room B" result)) + ;; After :END: there should be no body text + (should-not (string-match-p ":END:\n." result))))) + +(ert-deftest test-calendar-sync--event-to-org-boundary-no-location-no-attendees () + "Test event without location or attendees produces no drawer." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Simple Event" + :start start + :end end + :description "Just a note"))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p "\\* Simple Event" result)) + ;; Description goes after timestamp, no drawer needed + (should (string-match-p "Just a note" result))))) + +(ert-deftest test-calendar-sync--event-to-org-boundary-only-status () + "Test event with only status produces drawer." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Status Only" + :start start + :end end + :status "tentative"))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p ":PROPERTIES:" result)) + (should (string-match-p ":STATUS: tentative" result)) + (should (string-match-p ":END:" result))))) + +(ert-deftest test-calendar-sync--event-to-org-boundary-description-with-asterisks () + "Test event description containing org-special asterisks." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Meeting" + :start start + :end end + :description "* agenda item 1\n** sub-item"))) + (let ((result (calendar-sync--event-to-org event))) + ;; Description should be present + (should (string-match-p "agenda item" result))))) + +(ert-deftest test-calendar-sync--event-to-org-boundary-organizer-email-only () + "Test organizer without CN shows email." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary "Meeting" + :start start + :end end + :organizer (list :cn nil :email "org@example.com")))) + (let ((result (calendar-sync--event-to-org event))) + (should (string-match-p ":ORGANIZER: org@example.com" result))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--event-to-org-error-missing-summary () + "Test event with nil summary still produces output." + (let* ((start (test-calendar-sync-time-days-from-now 5 14 0)) + (end (test-calendar-sync-time-days-from-now 5 15 0)) + (event (list :summary nil + :start start + :end end))) + ;; Should not error - produce some output + (should (stringp (calendar-sync--event-to-org event))))) + +(provide 'test-calendar-sync--event-to-org) +;;; test-calendar-sync--event-to-org.el ends here diff --git a/tests/test-calendar-sync--expand-weekly.el b/tests/test-calendar-sync--expand-weekly.el index fe333c98..a6143bce 100644 --- a/tests/test-calendar-sync--expand-weekly.el +++ b/tests/test-calendar-sync--expand-weekly.el @@ -8,8 +8,8 @@ ;;; Code: (require 'ert) -(require 'calendar-sync) (require 'testutil-calendar-sync) +(require 'calendar-sync) ;;; Setup and Teardown diff --git a/tests/test-calendar-sync--extract-meeting-url.el b/tests/test-calendar-sync--extract-meeting-url.el new file mode 100644 index 00000000..2f677991 --- /dev/null +++ b/tests/test-calendar-sync--extract-meeting-url.el @@ -0,0 +1,54 @@ +;;; test-calendar-sync--extract-meeting-url.el --- Tests for meeting URL extraction -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--extract-meeting-url function. +;; Extracts URL from X-GOOGLE-CONFERENCE (preferred) or URL property. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--extract-meeting-url-normal-google-conference () + "Test extracting X-GOOGLE-CONFERENCE URL." + (let ((event "BEGIN:VEVENT\nX-GOOGLE-CONFERENCE:https://meet.google.com/abc-defg-hij\nSUMMARY:Test\nEND:VEVENT")) + (should (string= "https://meet.google.com/abc-defg-hij" + (calendar-sync--extract-meeting-url event))))) + +(ert-deftest test-calendar-sync--extract-meeting-url-normal-url-property () + "Test extracting URL property." + (let ((event "BEGIN:VEVENT\nURL:https://zoom.us/j/123456\nSUMMARY:Test\nEND:VEVENT")) + (should (string= "https://zoom.us/j/123456" + (calendar-sync--extract-meeting-url event))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--extract-meeting-url-boundary-both-present () + "Test X-GOOGLE-CONFERENCE is preferred when both present." + (let ((event "BEGIN:VEVENT\nURL:https://zoom.us/j/123456\nX-GOOGLE-CONFERENCE:https://meet.google.com/abc\nSUMMARY:Test\nEND:VEVENT")) + (should (string= "https://meet.google.com/abc" + (calendar-sync--extract-meeting-url event))))) + +(ert-deftest test-calendar-sync--extract-meeting-url-boundary-neither-present () + "Test returns nil when neither URL property exists." + (let ((event "BEGIN:VEVENT\nSUMMARY:Test\nDTSTART:20260210T140000Z\nEND:VEVENT")) + (should (null (calendar-sync--extract-meeting-url event))))) + +(ert-deftest test-calendar-sync--extract-meeting-url-boundary-url-with-params () + "Test URL property with parameters." + (let ((event "BEGIN:VEVENT\nURL;VALUE=URI:https://teams.microsoft.com/l/meetup-join/abc\nSUMMARY:Test\nEND:VEVENT")) + (should (string-match-p "teams.microsoft.com" + (calendar-sync--extract-meeting-url event))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--extract-meeting-url-error-nil-event () + "Test nil event returns nil." + (should (null (calendar-sync--extract-meeting-url nil)))) + +(provide 'test-calendar-sync--extract-meeting-url) +;;; test-calendar-sync--extract-meeting-url.el ends here diff --git a/tests/test-calendar-sync--extract-tzid.el b/tests/test-calendar-sync--extract-tzid.el index d16aae40..95f51143 100644 --- a/tests/test-calendar-sync--extract-tzid.el +++ b/tests/test-calendar-sync--extract-tzid.el @@ -8,6 +8,7 @@ ;;; Code: (require 'ert) +(require 'testutil-calendar-sync) (require 'calendar-sync) ;;; Normal Cases diff --git a/tests/test-calendar-sync--find-user-status.el b/tests/test-calendar-sync--find-user-status.el new file mode 100644 index 00000000..523f9a9f --- /dev/null +++ b/tests/test-calendar-sync--find-user-status.el @@ -0,0 +1,89 @@ +;;; test-calendar-sync--find-user-status.el --- Tests for find-user-status -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--find-user-status function. +;; Given attendees list and user emails, returns user's PARTSTAT as lowercase string. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Test Data + +(defun test-find-user-status--make-attendees () + "Create sample attendees list for testing." + (list (list :cn "Alice" :email "alice@example.com" :partstat "ACCEPTED" :role "REQ-PARTICIPANT") + (list :cn "Craig" :email "craig@example.com" :partstat "ACCEPTED" :role "REQ-PARTICIPANT") + (list :cn "Bob" :email "bob@example.com" :partstat "DECLINED" :role "OPT-PARTICIPANT"))) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--find-user-status-normal-accepted () + "Test finding user who accepted." + (let ((attendees (test-find-user-status--make-attendees)) + (emails '("craig@example.com"))) + (should (string= "accepted" + (calendar-sync--find-user-status attendees emails))))) + +(ert-deftest test-calendar-sync--find-user-status-normal-declined () + "Test finding user who declined." + (let ((attendees (test-find-user-status--make-attendees)) + (emails '("bob@example.com"))) + (should (string= "declined" + (calendar-sync--find-user-status attendees emails))))) + +(ert-deftest test-calendar-sync--find-user-status-normal-tentative () + "Test finding user with tentative status." + (let ((attendees (list (list :cn "Test" :email "test@example.com" :partstat "TENTATIVE"))) + (emails '("test@example.com"))) + (should (string= "tentative" + (calendar-sync--find-user-status attendees emails))))) + +(ert-deftest test-calendar-sync--find-user-status-normal-needs-action () + "Test finding user with needs-action status." + (let ((attendees (list (list :cn "Test" :email "test@example.com" :partstat "NEEDS-ACTION"))) + (emails '("test@example.com"))) + (should (string= "needs-action" + (calendar-sync--find-user-status attendees emails))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--find-user-status-boundary-not-in-list () + "Test user not in attendee list returns nil." + (let ((attendees (test-find-user-status--make-attendees)) + (emails '("stranger@example.com"))) + (should (null (calendar-sync--find-user-status attendees emails))))) + +(ert-deftest test-calendar-sync--find-user-status-boundary-empty-attendees () + "Test empty attendee list returns nil." + (should (null (calendar-sync--find-user-status '() '("test@example.com"))))) + +(ert-deftest test-calendar-sync--find-user-status-boundary-multiple-emails () + "Test matching on second email in user emails list." + (let ((attendees (test-find-user-status--make-attendees)) + (emails '("primary@other.com" "craig@example.com"))) + (should (string= "accepted" + (calendar-sync--find-user-status attendees emails))))) + +(ert-deftest test-calendar-sync--find-user-status-boundary-case-insensitive () + "Test case-insensitive email matching." + (let ((attendees (list (list :cn "Test" :email "Craig@Example.COM" :partstat "ACCEPTED"))) + (emails '("craig@example.com"))) + (should (string= "accepted" + (calendar-sync--find-user-status attendees emails))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--find-user-status-error-nil-attendees () + "Test nil attendees returns nil." + (should (null (calendar-sync--find-user-status nil '("test@example.com"))))) + +(ert-deftest test-calendar-sync--find-user-status-error-nil-emails () + "Test nil emails list returns nil." + (should (null (calendar-sync--find-user-status (test-find-user-status--make-attendees) nil)))) + +(provide 'test-calendar-sync--find-user-status) +;;; test-calendar-sync--find-user-status.el ends here diff --git a/tests/test-calendar-sync--get-all-property-lines.el b/tests/test-calendar-sync--get-all-property-lines.el new file mode 100644 index 00000000..c95041c9 --- /dev/null +++ b/tests/test-calendar-sync--get-all-property-lines.el @@ -0,0 +1,61 @@ +;;; test-calendar-sync--get-all-property-lines.el --- Tests for get-all-property-lines -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--get-all-property-lines function. +;; Like get-property-line but returns ALL matching lines. +;; Needed because ATTENDEE appears multiple times in an event. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--get-all-property-lines-normal-multiple () + "Test extracting multiple ATTENDEE lines." + (let ((event "BEGIN:VEVENT\nATTENDEE;CN=Alice:mailto:alice@example.com\nATTENDEE;CN=Bob:mailto:bob@example.com\nEND:VEVENT")) + (let ((result (calendar-sync--get-all-property-lines event "ATTENDEE"))) + (should (= 2 (length result))) + (should (string-match-p "Alice" (nth 0 result))) + (should (string-match-p "Bob" (nth 1 result)))))) + +(ert-deftest test-calendar-sync--get-all-property-lines-normal-single () + "Test extracting single matching line." + (let ((event "BEGIN:VEVENT\nATTENDEE;CN=Alice:mailto:alice@example.com\nSUMMARY:Test\nEND:VEVENT")) + (let ((result (calendar-sync--get-all-property-lines event "ATTENDEE"))) + (should (= 1 (length result))) + (should (string-match-p "Alice" (car result)))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--get-all-property-lines-boundary-no-match () + "Test no matching property returns empty list." + (let ((event "BEGIN:VEVENT\nSUMMARY:Test\nEND:VEVENT")) + (should (null (calendar-sync--get-all-property-lines event "ATTENDEE"))))) + +(ert-deftest test-calendar-sync--get-all-property-lines-boundary-continuation () + "Test handling of continuation lines (lines starting with space)." + (let ((event "BEGIN:VEVENT\nATTENDEE;CN=Very Long Name;PARTSTAT=ACCEPTED:\n mailto:longname@example.com\nSUMMARY:Test\nEND:VEVENT")) + (let ((result (calendar-sync--get-all-property-lines event "ATTENDEE"))) + (should (= 1 (length result))) + (should (string-match-p "longname@example.com" (car result)))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--get-all-property-lines-error-nil-event () + "Test nil event returns nil." + (should (null (calendar-sync--get-all-property-lines nil "ATTENDEE")))) + +(ert-deftest test-calendar-sync--get-all-property-lines-error-nil-property () + "Test nil property returns nil." + (should (null (calendar-sync--get-all-property-lines "BEGIN:VEVENT\nEND:VEVENT" nil)))) + +(ert-deftest test-calendar-sync--get-all-property-lines-error-empty-event () + "Test empty event string returns nil." + (should (null (calendar-sync--get-all-property-lines "" "ATTENDEE")))) + +(provide 'test-calendar-sync--get-all-property-lines) +;;; test-calendar-sync--get-all-property-lines.el ends here diff --git a/tests/test-calendar-sync--get-property.el b/tests/test-calendar-sync--get-property.el index 79fefc8f..8b71f8e3 100644 --- a/tests/test-calendar-sync--get-property.el +++ b/tests/test-calendar-sync--get-property.el @@ -12,6 +12,7 @@ ;;; Code: (require 'ert) +(require 'testutil-calendar-sync) (require 'calendar-sync) ;;; Setup and Teardown diff --git a/tests/test-calendar-sync--helpers.el b/tests/test-calendar-sync--helpers.el index eb868952..864faa7f 100644 --- a/tests/test-calendar-sync--helpers.el +++ b/tests/test-calendar-sync--helpers.el @@ -7,6 +7,7 @@ ;;; Code: (require 'ert) +(require 'testutil-calendar-sync) (require 'calendar-sync) ;;; Setup and Teardown diff --git a/tests/test-calendar-sync--normalize-line-endings.el b/tests/test-calendar-sync--normalize-line-endings.el index 7f0830cc..cbada921 100644 --- a/tests/test-calendar-sync--normalize-line-endings.el +++ b/tests/test-calendar-sync--normalize-line-endings.el @@ -12,6 +12,7 @@ ;;; Code: (require 'ert) +(require 'testutil-calendar-sync) (require 'calendar-sync) ;;; Normal Cases diff --git a/tests/test-calendar-sync--parse-attendee-line.el b/tests/test-calendar-sync--parse-attendee-line.el new file mode 100644 index 00000000..5da7aacd --- /dev/null +++ b/tests/test-calendar-sync--parse-attendee-line.el @@ -0,0 +1,97 @@ +;;; test-calendar-sync--parse-attendee-line.el --- Tests for attendee parsing -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--parse-attendee-line function. +;; Parses single ATTENDEE line into plist (:cn NAME :email EMAIL :partstat STATUS :role ROLE). +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--parse-attendee-line-normal-accepted () + "Test parsing attendee with PARTSTAT=ACCEPTED." + (let ((line "ATTENDEE;CN=Alice Smith;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:alice@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "Alice Smith" (plist-get result :cn))) + (should (string= "alice@example.com" (plist-get result :email))) + (should (string= "ACCEPTED" (plist-get result :partstat))) + (should (string= "REQ-PARTICIPANT" (plist-get result :role)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-normal-declined () + "Test parsing attendee with PARTSTAT=DECLINED." + (let ((line "ATTENDEE;CN=Bob Jones;PARTSTAT=DECLINED:mailto:bob@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "Bob Jones" (plist-get result :cn))) + (should (string= "bob@example.com" (plist-get result :email))) + (should (string= "DECLINED" (plist-get result :partstat)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-normal-tentative () + "Test parsing attendee with PARTSTAT=TENTATIVE." + (let ((line "ATTENDEE;CN=Carol;PARTSTAT=TENTATIVE:mailto:carol@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "TENTATIVE" (plist-get result :partstat)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-normal-needs-action () + "Test parsing attendee with PARTSTAT=NEEDS-ACTION." + (let ((line "ATTENDEE;CN=Dave;PARTSTAT=NEEDS-ACTION:mailto:dave@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "NEEDS-ACTION" (plist-get result :partstat)))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--parse-attendee-line-boundary-no-cn () + "Test attendee without CN uses email as fallback." + (let ((line "ATTENDEE;PARTSTAT=ACCEPTED:mailto:noCN@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (null (plist-get result :cn))) + (should (string= "noCN@example.com" (plist-get result :email)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-boundary-no-partstat () + "Test attendee without PARTSTAT." + (let ((line "ATTENDEE;CN=Eve:mailto:eve@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "Eve" (plist-get result :cn))) + (should (string= "eve@example.com" (plist-get result :email))) + (should (null (plist-get result :partstat)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-boundary-minimal () + "Test minimal attendee line with just mailto." + (let ((line "ATTENDEE:mailto:min@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "min@example.com" (plist-get result :email)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-boundary-cutype-resource () + "Test attendee with CUTYPE=RESOURCE." + (let ((line "ATTENDEE;CN=Room A;CUTYPE=RESOURCE;PARTSTAT=ACCEPTED:mailto:room-a@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "Room A" (plist-get result :cn))) + (should (string= "ACCEPTED" (plist-get result :partstat)))))) + +(ert-deftest test-calendar-sync--parse-attendee-line-boundary-extra-params () + "Test attendee with extra unknown parameters." + (let ((line "ATTENDEE;CN=Frank;RSVP=TRUE;X-NUM-GUESTS=0;PARTSTAT=ACCEPTED:mailto:frank@example.com")) + (let ((result (calendar-sync--parse-attendee-line line))) + (should (string= "Frank" (plist-get result :cn))) + (should (string= "ACCEPTED" (plist-get result :partstat)))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--parse-attendee-line-error-nil () + "Test nil input returns nil." + (should (null (calendar-sync--parse-attendee-line nil)))) + +(ert-deftest test-calendar-sync--parse-attendee-line-error-empty () + "Test empty string returns nil." + (should (null (calendar-sync--parse-attendee-line "")))) + +(ert-deftest test-calendar-sync--parse-attendee-line-error-malformed () + "Test malformed line returns nil." + (should (null (calendar-sync--parse-attendee-line "not an attendee line")))) + +(provide 'test-calendar-sync--parse-attendee-line) +;;; test-calendar-sync--parse-attendee-line.el ends here diff --git a/tests/test-calendar-sync--parse-organizer.el b/tests/test-calendar-sync--parse-organizer.el new file mode 100644 index 00000000..5f21d902 --- /dev/null +++ b/tests/test-calendar-sync--parse-organizer.el @@ -0,0 +1,50 @@ +;;; test-calendar-sync--parse-organizer.el --- Tests for organizer parsing -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--parse-organizer function. +;; Parses ORGANIZER property line into plist (:cn NAME :email EMAIL). +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--parse-organizer-normal-cn-and-email () + "Test parsing organizer with both CN and email." + (let ((event "BEGIN:VEVENT\nORGANIZER;CN=John Smith:mailto:john@example.com\nSUMMARY:Test\nEND:VEVENT")) + (let ((result (calendar-sync--parse-organizer event))) + (should (string= "John Smith" (plist-get result :cn))) + (should (string= "john@example.com" (plist-get result :email)))))) + +(ert-deftest test-calendar-sync--parse-organizer-normal-no-cn () + "Test parsing organizer without CN." + (let ((event "BEGIN:VEVENT\nORGANIZER:mailto:organizer@example.com\nSUMMARY:Test\nEND:VEVENT")) + (let ((result (calendar-sync--parse-organizer event))) + (should (null (plist-get result :cn))) + (should (string= "organizer@example.com" (plist-get result :email)))))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--parse-organizer-boundary-no-organizer () + "Test event without ORGANIZER returns nil." + (let ((event "BEGIN:VEVENT\nSUMMARY:Test\nDTSTART:20260210T140000Z\nEND:VEVENT")) + (should (null (calendar-sync--parse-organizer event))))) + +(ert-deftest test-calendar-sync--parse-organizer-boundary-complex-cn () + "Test organizer with complex CN (quotes, special chars)." + (let ((event "BEGIN:VEVENT\nORGANIZER;CN=\"Dr. Jane O'Brien\":mailto:jane@example.com\nSUMMARY:Test\nEND:VEVENT")) + (let ((result (calendar-sync--parse-organizer event))) + (should (plist-get result :email))))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--parse-organizer-error-nil-event () + "Test nil event returns nil." + (should (null (calendar-sync--parse-organizer nil)))) + +(provide 'test-calendar-sync--parse-organizer) +;;; test-calendar-sync--parse-organizer.el ends here diff --git a/tests/test-calendar-sync--parse-rrule.el b/tests/test-calendar-sync--parse-rrule.el index 123caa5c..099e4e44 100644 --- a/tests/test-calendar-sync--parse-rrule.el +++ b/tests/test-calendar-sync--parse-rrule.el @@ -7,6 +7,7 @@ ;;; Code: (require 'ert) +(require 'testutil-calendar-sync) (require 'calendar-sync) ;;; Setup and Teardown diff --git a/tests/test-calendar-sync--strip-html.el b/tests/test-calendar-sync--strip-html.el new file mode 100644 index 00000000..fda2bbc5 --- /dev/null +++ b/tests/test-calendar-sync--strip-html.el @@ -0,0 +1,99 @@ +;;; test-calendar-sync--strip-html.el --- Tests for HTML stripping -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--strip-html function. +;; Converts
/
to newline, strips all other tags, decodes HTML entities. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--strip-html-normal-br-to-newline () + "Test
converted to newline." + (should (string= "line1\nline2" + (calendar-sync--strip-html "line1
line2")))) + +(ert-deftest test-calendar-sync--strip-html-normal-br-self-closing () + "Test
converted to newline." + (should (string= "line1\nline2" + (calendar-sync--strip-html "line1
line2")))) + +(ert-deftest test-calendar-sync--strip-html-normal-br-space-self-closing () + "Test
converted to newline." + (should (string= "line1\nline2" + (calendar-sync--strip-html "line1
line2")))) + +(ert-deftest test-calendar-sync--strip-html-normal-strip-p-tags () + "Test

and

tags are stripped." + (should (string= "paragraph text" + (calendar-sync--strip-html "

paragraph text

")))) + +(ert-deftest test-calendar-sync--strip-html-normal-strip-bold () + "Test and tags are stripped." + (should (string= "bold text" + (calendar-sync--strip-html "bold text")))) + +(ert-deftest test-calendar-sync--strip-html-normal-combined-tags () + "Test mixed tags are handled." + (should (string= "Hello\nWorld" + (calendar-sync--strip-html "

Hello


World")))) + +(ert-deftest test-calendar-sync--strip-html-normal-entity-amp () + "Test & decoded to &." + (should (string= "A & B" + (calendar-sync--strip-html "A & B")))) + +(ert-deftest test-calendar-sync--strip-html-normal-entity-lt () + "Test < decoded to <." + (should (string= "a < b" + (calendar-sync--strip-html "a < b")))) + +(ert-deftest test-calendar-sync--strip-html-normal-entity-gt () + "Test > decoded to >." + (should (string= "a > b" + (calendar-sync--strip-html "a > b")))) + +(ert-deftest test-calendar-sync--strip-html-normal-entity-quot () + "Test " decoded to double quote." + (should (string= "say \"hello\"" + (calendar-sync--strip-html "say "hello"")))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--strip-html-boundary-empty-string () + "Test empty string returns empty string." + (should (string= "" (calendar-sync--strip-html "")))) + +(ert-deftest test-calendar-sync--strip-html-boundary-no-html () + "Test plain text passes through unchanged." + (should (string= "just plain text" + (calendar-sync--strip-html "just plain text")))) + +(ert-deftest test-calendar-sync--strip-html-boundary-only-tags () + "Test string of only tags returns empty." + (should (string= "" + (calendar-sync--strip-html "

")))) + +(ert-deftest test-calendar-sync--strip-html-boundary-multiple-br () + "Test multiple consecutive
collapse." + (should (string-match-p "^line1\n+line2$" + (calendar-sync--strip-html "line1


line2")))) + +(ert-deftest test-calendar-sync--strip-html-boundary-nested-tags () + "Test nested tags are stripped correctly." + (should (string= "nested text" + (calendar-sync--strip-html "

nested text

")))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--strip-html-error-nil-input () + "Test nil input returns nil." + (should (null (calendar-sync--strip-html nil)))) + +(provide 'test-calendar-sync--strip-html) +;;; test-calendar-sync--strip-html.el ends here diff --git a/tests/test-calendar-sync--unescape-ics-text.el b/tests/test-calendar-sync--unescape-ics-text.el new file mode 100644 index 00000000..a83e97d3 --- /dev/null +++ b/tests/test-calendar-sync--unescape-ics-text.el @@ -0,0 +1,79 @@ +;;; test-calendar-sync--unescape-ics-text.el --- Tests for ICS text unescaping -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--unescape-ics-text function. +;; RFC 5545 defines escape sequences: \n→newline, \,→comma, \\→backslash, \;→semicolon. +;; Covers Normal, Boundary, and Error cases. + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--unescape-ics-text-normal-newline () + "Test \\n escape is converted to actual newline." + (should (string= "line1\nline2" + (calendar-sync--unescape-ics-text "line1\\nline2")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-normal-comma () + "Test \\, escape is converted to comma." + (should (string= "one, two" + (calendar-sync--unescape-ics-text "one\\, two")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-normal-backslash () + "Test \\\\ escape is converted to single backslash." + (should (string= "path\\file" + (calendar-sync--unescape-ics-text "path\\\\file")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-normal-semicolon () + "Test \\; escape is converted to semicolon." + (should (string= "a;b" + (calendar-sync--unescape-ics-text "a\\;b")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-normal-mixed () + "Test multiple different escapes in one string." + (should (string= "Hello, World\nPath\\to;file" + (calendar-sync--unescape-ics-text "Hello\\, World\\nPath\\\\to\\;file")))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-empty-string () + "Test empty string returns empty string." + (should (string= "" (calendar-sync--unescape-ics-text "")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-no-escapes () + "Test string with no escapes passes through unchanged." + (should (string= "plain text" + (calendar-sync--unescape-ics-text "plain text")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-escape-at-start () + "Test escape sequence at string start." + (should (string= "\nfoo" + (calendar-sync--unescape-ics-text "\\nfoo")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-escape-at-end () + "Test escape sequence at string end." + (should (string= "foo\n" + (calendar-sync--unescape-ics-text "foo\\n")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-consecutive-escapes () + "Test consecutive escape sequences." + (should (string= "\n\n" + (calendar-sync--unescape-ics-text "\\n\\n")))) + +(ert-deftest test-calendar-sync--unescape-ics-text-boundary-only-escapes () + "Test string composed entirely of escapes." + (should (string= ",;\n\\" + (calendar-sync--unescape-ics-text "\\,\\;\\n\\\\")))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--unescape-ics-text-error-nil-input () + "Test nil input returns nil." + (should (null (calendar-sync--unescape-ics-text nil)))) + +(provide 'test-calendar-sync--unescape-ics-text) +;;; test-calendar-sync--unescape-ics-text.el ends here diff --git a/tests/test-calendar-sync-properties.el b/tests/test-calendar-sync-properties.el index 6054fc5e..c25bb99f 100644 --- a/tests/test-calendar-sync-properties.el +++ b/tests/test-calendar-sync-properties.el @@ -19,8 +19,8 @@ ;;; Code: (require 'ert) -(require 'calendar-sync) (require 'testutil-calendar-sync) +(require 'calendar-sync) (defconst test-calendar-sync-property-trials 30 "Number of random trials to run for each property test. diff --git a/tests/test-calendar-sync.el b/tests/test-calendar-sync.el index 7cda5e73..144d6486 100644 --- a/tests/test-calendar-sync.el +++ b/tests/test-calendar-sync.el @@ -8,8 +8,8 @@ ;;; Code: (require 'ert) -(require 'calendar-sync) (require 'testutil-calendar-sync) +(require 'calendar-sync) ;;; Test Data diff --git a/tests/test-integration-calendar-sync-event-details.el b/tests/test-integration-calendar-sync-event-details.el new file mode 100644 index 00000000..2ecc73e0 --- /dev/null +++ b/tests/test-integration-calendar-sync-event-details.el @@ -0,0 +1,177 @@ +;;; test-integration-calendar-sync-event-details.el --- Integration tests for event details -*- lexical-binding: t; -*- + +;;; Commentary: +;; Integration tests for the enhanced event details workflow. +;; Tests the complete flow from raw ICS with HTML description, attendees, +;; organizer through parse-ics to verify org output. +;; +;; Components integrated: +;; - calendar-sync--unescape-ics-text (ICS text unescaping) +;; - calendar-sync--strip-html (HTML tag removal) +;; - calendar-sync--clean-text (combined cleaning) +;; - calendar-sync--get-all-property-lines (multi-line property extraction) +;; - calendar-sync--parse-attendee-line (attendee parsing) +;; - calendar-sync--find-user-status (user status lookup) +;; - calendar-sync--parse-organizer (organizer extraction) +;; - calendar-sync--extract-meeting-url (meeting URL extraction) +;; - calendar-sync--parse-event (full event parsing) +;; - calendar-sync--event-to-org (org format output with property drawer) +;; - calendar-sync--parse-ics (full ICS pipeline) + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Test Helpers + +(defun test-integration-details--make-full-ics (start-time) + "Create ICS with HTML description, attendees, organizer, and meeting URL. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Google Inc//Google Calendar//EN\n" + "BEGIN:VEVENT\n" + "UID:full-test@example.com\n" + "SUMMARY:Team Planning\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "DESCRIPTION:Discuss Q1 goals
Review metrics\\, update roadmap\n" + "LOCATION:Conference Room B\n" + "ORGANIZER;CN=John Smith:mailto:john@example.com\n" + "ATTENDEE;CN=John Smith;PARTSTAT=ACCEPTED:mailto:john@example.com\n" + "ATTENDEE;CN=Craig Jennings;PARTSTAT=ACCEPTED:mailto:craigmartinjennings@gmail.com\n" + "ATTENDEE;CN=Alice;PARTSTAT=DECLINED:mailto:alice@example.com\n" + "X-GOOGLE-CONFERENCE:https://meet.google.com/abc-defg-hij\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-plain-ics (start-time) + "Create simple ICS without attendees or special properties. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:plain-test@example.com\n" + "SUMMARY:Simple Reminder\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-recurring-with-attendees (start-time) + "Create ICS with recurring event that has attendees. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:recurring-attendees@example.com\n" + "SUMMARY:Weekly Standup\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "RRULE:FREQ=WEEKLY;COUNT=3\n" + "ORGANIZER;CN=Manager:mailto:manager@example.com\n" + "ATTENDEE;CN=Craig;PARTSTAT=ACCEPTED:mailto:craigmartinjennings@gmail.com\n" + "LOCATION:Virtual\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-ics-escaped-location (start-time) + "Create ICS with ICS-escaped location field. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:escaped-loc@example.com\n" + "SUMMARY:Offsite Meeting\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "LOCATION:123 Main St\\, Suite 400\\, New Orleans\\, LA\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +;;; Integration Tests + +(ert-deftest test-integration-details-full-pipeline () + "Test full pipeline: ICS with HTML + attendees + organizer → clean org output. +Verifies: +- HTML in description is cleaned +- ICS escapes in description are unescaped +- Property drawer contains LOCATION, ORGANIZER, STATUS, URL +- Description appears as body text after drawer" + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-full-ics start-time)) + (calendar-sync-user-emails '("craigmartinjennings@gmail.com")) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + ;; Summary present + (should (string-match-p "Team Planning" org-output)) + ;; Clean description (HTML
→ newline, ICS \, → comma) + (should (string-match-p "Discuss Q1 goals" org-output)) + (should (string-match-p "Review metrics, update roadmap" org-output)) + (should-not (string-match-p "
" org-output)) + ;; Property drawer + (should (string-match-p ":PROPERTIES:" org-output)) + (should (string-match-p ":LOCATION: Conference Room B" org-output)) + (should (string-match-p ":ORGANIZER: John Smith" org-output)) + (should (string-match-p ":STATUS: accepted" org-output)) + (should (string-match-p ":URL: https://meet.google.com/abc-defg-hij" org-output)) + (should (string-match-p ":END:" org-output)))) + +(ert-deftest test-integration-details-plain-event-no-drawer () + "Test plain event without attendees/location produces no property drawer." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-plain-ics start-time)) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + (should (string-match-p "Simple Reminder" org-output)) + (should-not (string-match-p ":PROPERTIES:" org-output)))) + +(ert-deftest test-integration-details-recurring-with-attendees () + "Test recurring event with attendees flows details to expanded instances." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-recurring-with-attendees start-time)) + (calendar-sync-user-emails '("craigmartinjennings@gmail.com")) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + ;; Should have multiple occurrences of Weekly Standup + (let ((count 0) + (pos 0)) + (while (string-match "Weekly Standup" org-output pos) + (setq count (1+ count)) + (setq pos (match-end 0))) + (should (>= count 2))))) + +(ert-deftest test-integration-details-escaped-location () + "Test ICS escapes in location are cleaned in org output." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-ics-escaped-location start-time)) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + (should (string-match-p "Offsite Meeting" org-output)) + ;; Location should have real commas, not escaped + (should (string-match-p "123 Main St, Suite 400" org-output)) + (should-not (string-match-p "\\\\," org-output)))) + +(provide 'test-integration-calendar-sync-event-details) +;;; test-integration-calendar-sync-event-details.el ends here diff --git a/tests/test-org-agenda-build-list.el b/tests/test-org-agenda-build-list.el index 9b9ba7f3..94e89a3d 100644 --- a/tests/test-org-agenda-build-list.el +++ b/tests/test-org-agenda-build-list.el @@ -16,6 +16,7 @@ (defvar schedule-file "/tmp/test-schedule.org") (defvar gcal-file "/tmp/test-gcal.org") (defvar pcal-file "/tmp/test-pcal.org") +(defvar dcal-file "/tmp/test-dcal.org") (defvar projects-dir "/tmp/test-projects/") ;; Now load the actual production module @@ -184,12 +185,12 @@ When directory scan returns empty: (cj/build-org-agenda-list) - ;; Should have base files only (inbox, schedule, gcal, pcal) - (should (= (length org-agenda-files) 4)) + ;; Should have base files only (inbox, schedule, gcal, pcal, dcal) + (should (= (length org-agenda-files) 5)) ;; Cache should contain base files (should cj/org-agenda-files-cache) - (should (= (length cj/org-agenda-files-cache) 4))) + (should (= (length cj/org-agenda-files-cache) 5))) (test-org-agenda-teardown))) (ert-deftest test-org-agenda-build-list-boundary-building-flag-set-during-build () -- cgit v1.2.3