;;; 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