diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-18 12:54:25 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-18 12:54:25 -0600 |
| commit | 2da0e8001ae390920f31f1db5e56aa2ed68bf1be (patch) | |
| tree | b9075be4442a0528f3eb64869fd4e585c7804219 /tests/test-calendar-sync--parse-rrule.el | |
| parent | 342e7df14b688ca995c831a68531550ad59e2cc2 (diff) | |
feat(calendar-sync): Add RRULE support and refactor expansion functions
Implements complete recurring event (RRULE) expansion for Google Calendar
with rolling window approach and comprehensive test coverage.
Features:
- RRULE expansion for DAILY, WEEKLY, MONTHLY, YEARLY frequencies
- Support for INTERVAL, BYDAY, UNTIL, and COUNT parameters
- Rolling window: -3 months to +12 months from current date
- Fixed COUNT parameter bug (events no longer appear beyond their limit)
- Fixed TZID parameter parsing (supports timezone-specific timestamps)
- Replaced debug messages with cj/log-silently
Refactoring:
- Extracted helper functions to eliminate code duplication:
- calendar-sync--date-to-time: Date to time conversion
- calendar-sync--before-date-p: Date comparison
- calendar-sync--create-occurrence: Event occurrence creation
- Refactored all expansion functions to use helper functions
- Reduced code duplication across daily/weekly/monthly/yearly expansion
Testing:
- 68 tests total across 5 test files
- Unit tests for RRULE parsing, property extraction, weekly expansion
- Integration tests for complete RRULE workflow
- Tests for helper functions validating refactored code
- All tests passing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'tests/test-calendar-sync--parse-rrule.el')
| -rw-r--r-- | tests/test-calendar-sync--parse-rrule.el | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/tests/test-calendar-sync--parse-rrule.el b/tests/test-calendar-sync--parse-rrule.el new file mode 100644 index 00000000..123caa5c --- /dev/null +++ b/tests/test-calendar-sync--parse-rrule.el @@ -0,0 +1,209 @@ +;;; test-calendar-sync--parse-rrule.el --- Tests for calendar-sync--parse-rrule -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for calendar-sync--parse-rrule function. +;; Tests parsing of iCalendar RRULE strings into plist format. + +;;; Code: + +(require 'ert) +(require 'calendar-sync) + +;;; Setup and Teardown + +(defun test-calendar-sync--parse-rrule-setup () + "Setup for calendar-sync--parse-rrule tests." + ;; No setup required for pure parsing tests + nil) + +(defun test-calendar-sync--parse-rrule-teardown () + "Teardown for calendar-sync--parse-rrule tests." + ;; No teardown required + nil) + +;;; Normal Cases + +(ert-deftest test-calendar-sync--parse-rrule-normal-weekly-returns-plist () + "Test parsing simple weekly recurrence rule." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-weekly-with-byday-returns-plist () + "Test parsing weekly recurrence with specific weekdays." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;BYDAY=SA"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (equal (plist-get result :byday) '("SA"))) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-daily-returns-plist () + "Test parsing daily recurrence rule." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY"))) + (should (eq (plist-get result :freq) 'daily)) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-monthly-returns-plist () + "Test parsing monthly recurrence rule." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=MONTHLY"))) + (should (eq (plist-get result :freq) 'monthly)) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-yearly-returns-plist () + "Test parsing yearly recurrence rule." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=YEARLY"))) + (should (eq (plist-get result :freq) 'yearly)) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-with-interval-returns-plist () + "Test parsing recurrence rule with custom interval." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;INTERVAL=2"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (= (plist-get result :interval) 2))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-with-count-returns-plist () + "Test parsing recurrence rule with count limit." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY;COUNT=10"))) + (should (eq (plist-get result :freq) 'daily)) + (should (= (plist-get result :count) 10))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-normal-with-until-returns-plist () + "Test parsing recurrence rule with end date." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let* ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;UNTIL=20261118T120000Z")) + (until (plist-get result :until))) + (should (eq (plist-get result :freq) 'weekly)) + (should (listp until)) + (should (= (nth 0 until) 2026)) ; year + (should (= (nth 1 until) 11)) ; month + ;; Day might be 17 or 18 depending on timezone conversion + (should (member (nth 2 until) '(17 18)))) + (test-calendar-sync--parse-rrule-teardown))) + +;;; Boundary Cases + +(ert-deftest test-calendar-sync--parse-rrule-boundary-multiple-byday-returns-list () + "Test parsing BYDAY with multiple weekdays." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;BYDAY=MO,WE,FR"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (equal (plist-get result :byday) '("MO" "WE" "FR")))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-boundary-all-parameters-returns-plist () + "Test parsing RRULE with all supported parameters." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;INTERVAL=2;BYDAY=SA;UNTIL=20261118T000000Z;COUNT=52"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (= (plist-get result :interval) 2)) + (should (equal (plist-get result :byday) '("SA"))) + (should (plist-get result :until)) + (should (= (plist-get result :count) 52))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-boundary-interval-one-returns-default () + "Test that default interval is 1 when not specified." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY"))) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-boundary-large-interval-returns-number () + "Test parsing RRULE with large interval value." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=MONTHLY;INTERVAL=12"))) + (should (= (plist-get result :interval) 12))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-boundary-large-count-returns-number () + "Test parsing RRULE with large count value." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY;COUNT=365"))) + (should (= (plist-get result :count) 365))) + (test-calendar-sync--parse-rrule-teardown))) + +;;; Error Cases + +(ert-deftest test-calendar-sync--parse-rrule-error-empty-string-returns-plist () + "Test parsing empty RRULE string returns plist with defaults." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule ""))) + (should (listp result)) + (should (= (plist-get result :interval) 1))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-error-unsupported-freq-returns-symbol () + "Test parsing RRULE with unsupported frequency." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=HOURLY"))) + (should (eq (plist-get result :freq) 'hourly))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-error-invalid-until-returns-nil () + "Test parsing RRULE with malformed UNTIL date." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY;UNTIL=invalid"))) + (should (eq (plist-get result :freq) 'daily)) + (should (null (plist-get result :until)))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-error-invalid-count-returns-zero () + "Test parsing RRULE with non-numeric COUNT." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=DAILY;COUNT=abc"))) + (should (eq (plist-get result :freq) 'daily)) + (should (= (plist-get result :count) 0))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-error-invalid-interval-returns-zero () + "Test parsing RRULE with non-numeric INTERVAL." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "FREQ=WEEKLY;INTERVAL=xyz"))) + (should (eq (plist-get result :freq) 'weekly)) + (should (= (plist-get result :interval) 0))) + (test-calendar-sync--parse-rrule-teardown))) + +(ert-deftest test-calendar-sync--parse-rrule-error-missing-freq-returns-plist () + "Test parsing RRULE without FREQ parameter." + (test-calendar-sync--parse-rrule-setup) + (unwind-protect + (let ((result (calendar-sync--parse-rrule "INTERVAL=2;COUNT=10"))) + (should (listp result)) + (should (null (plist-get result :freq))) + (should (= (plist-get result :interval) 2)) + (should (= (plist-get result :count) 10))) + (test-calendar-sync--parse-rrule-teardown))) + +(provide 'test-calendar-sync--parse-rrule) +;;; test-calendar-sync--parse-rrule.el ends here |
