diff options
author | SebastianKrupinski <krupinskis05@gmail.com> | 2024-05-28 07:25:34 -0400 |
---|---|---|
committer | SebastianKrupinski <krupinskis05@gmail.com> | 2024-07-17 19:10:32 -0400 |
commit | 43ee9488905bef91ed9be3057afcfb4c912d3450 (patch) | |
tree | 7a172d930aaebaf30cd80c866ca868ed81c066b7 /apps/dav/tests | |
parent | 41356659c067c2f2573a2f4bc47df400289d4b97 (diff) | |
download | nextcloud-server-43ee9488905bef91ed9be3057afcfb4c912d3450.tar.gz nextcloud-server-43ee9488905bef91ed9be3057afcfb4c912d3450.zip |
feat: Improve recurrence invitations messages
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Diffstat (limited to 'apps/dav/tests')
-rw-r--r-- | apps/dav/tests/unit/CalDAV/EventReaderTest.php | 1025 | ||||
-rw-r--r-- | apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php | 1307 |
2 files changed, 2195 insertions, 137 deletions
diff --git a/apps/dav/tests/unit/CalDAV/EventReaderTest.php b/apps/dav/tests/unit/CalDAV/EventReaderTest.php new file mode 100644 index 00000000000..23f0172131d --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/EventReaderTest.php @@ -0,0 +1,1025 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Tests\unit\CalDAV; + +use DateTimeZone; +use OCA\DAV\CalDAV\EventReader; +use Sabre\VObject\Component\VCalendar; +use Test\TestCase; + +class EventReaderTest extends TestCase { + + /** @var VCalendar*/ + private $vCalendar1a; + /** @var VCalendar*/ + private $vCalendar1b; + /** @var VCalendar*/ + private $vCalendar1c; + /** @var VCalendar*/ + private $vCalendar1d; + /** @var VCalendar*/ + private $vCalendar2; + /** @var VCalendar*/ + private $vCalendar3; + + protected function setUp(): void { + + parent::setUp(); + + // construct calendar with a 1 hour event and same start/end time zones + $this->vCalendar1a = new VCalendar(); + $vEvent = $this->vCalendar1a->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']); + $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a 1 hour event and different start/end time zones + $this->vCalendar1b = new VCalendar(); + $vEvent = $this->vCalendar1b->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']); + $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Vancouver']); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a 1 hour event and global time zone + $this->vCalendar1c = new VCalendar(); + // time zone component + $vTimeZone = $this->vCalendar1c->add('VTIMEZONE'); + $vTimeZone->add('TZID', 'America/Toronto'); + // event component + $vEvent = $this->vCalendar1c->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000'); + $vEvent->add('DTEND', '20240701T090000'); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a 1 hour event and no time zone + $this->vCalendar1d = new VCalendar(); + $vEvent = $this->vCalendar1d->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000'); + $vEvent->add('DTEND', '20240701T090000'); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a full day event + $this->vCalendar2 = new VCalendar(); + // time zone component + $vTimeZone = $this->vCalendar2->add('VTIMEZONE'); + $vTimeZone->add('TZID', 'America/Toronto'); + // event component + $vEvent = $this->vCalendar2->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701'); + $vEvent->add('DTEND', '20240702'); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a multi day event + $this->vCalendar3 = new VCalendar(); + // time zone component + $vTimeZone = $this->vCalendar3->add('VTIMEZONE'); + $vTimeZone->add('TZID', 'America/Toronto'); + // event component + $vEvent = $this->vCalendar3->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701'); + $vEvent->add('DTEND', '20240706'); + $vEvent->add('SUMMARY', 'Test Recurrance Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + } + + public function testConstructFromCalendarString(): void { + + // construct event reader + $er = new EventReader($this->vCalendar1a->serialize(), '96a0e6b1-d886-4a55-a60d-152b31401dcc'); + // test object creation + $this->assertInstanceOf(EventReader::class, $er); + + } + + public function testConstructFromCalendarObject(): void { + + // construct event reader + $er = new EventReader($this->vCalendar1a, '96a0e6b1-d886-4a55-a60d-152b31401dcc'); + // test object creation + $this->assertInstanceOf(EventReader::class, $er); + + } + + public function testConstructFromEventObject(): void { + + // construct event reader + $er = new EventReader($this->vCalendar1a->VEVENT[0]); + // test object creation + $this->assertInstanceOf(EventReader::class, $er); + + } + + public function testStartDateTime(): void { + + /** test day part event with same start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1a, $this->vCalendar1a->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->startDateTime()); + + /** test day part event with different start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1b, $this->vCalendar1b->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->startDateTime()); + + /** test day part event with global time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1c, $this->vCalendar1c->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->startDateTime()); + + /** test day part event with no time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1d, $this->vCalendar1d->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('UTC')))), $er->startDateTime()); + + /** test full day event */ + // construct event reader + $er = new EventReader($this->vCalendar2, $this->vCalendar2->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T000000', (new DateTimeZone('America/Toronto')))), $er->startDateTime()); + + /** test multi day event */ + // construct event reader + $er = new EventReader($this->vCalendar3, $this->vCalendar3->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T000000', (new DateTimeZone('America/Toronto')))), $er->startDateTime()); + + } + + public function testStartTimeZone(): void { + + /** test day part event with same start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1a, $this->vCalendar1a->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->startTimeZone()); + + /** test day part event with different start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1b, $this->vCalendar1b->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->startTimeZone()); + + /** test day part event with global time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1c, $this->vCalendar1c->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->startTimeZone()); + + /** test day part event with no time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1d, $this->vCalendar1d->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('UTC')), $er->startTimeZone()); + + /** test full day event */ + // construct event reader + $er = new EventReader($this->vCalendar2, $this->vCalendar2->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->startTimeZone()); + + /** test multi day event */ + // construct event reader + $er = new EventReader($this->vCalendar3, $this->vCalendar3->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->startTimeZone()); + + } + + public function testEndDate(): void { + + /** test day part event with same start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1a, $this->vCalendar1a->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T090000', (new DateTimeZone('America/Toronto')))), $er->endDateTime()); + + /** test day part event with different start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1b, $this->vCalendar1b->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T090000', (new DateTimeZone('America/Vancouver')))), $er->endDateTime()); + + /** test day part event with global time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1c, $this->vCalendar1c->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T090000', (new DateTimeZone('America/Toronto')))), $er->endDateTime()); + + /** test day part event with no time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1d, $this->vCalendar1d->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240701T090000', (new DateTimeZone('UTC')))), $er->endDateTime()); + + /** test full day event */ + // construct event reader + $er = new EventReader($this->vCalendar2, $this->vCalendar2->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240702T000000', (new DateTimeZone('America/Toronto')))), $er->endDateTime()); + + /** test multi day event */ + // construct event reader + $er = new EventReader($this->vCalendar3, $this->vCalendar3->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240706T000000', (new DateTimeZone('America/Toronto')))), $er->endDateTime()); + + } + + public function testEndTimeZone(): void { + + /** test day part event with same start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1a, $this->vCalendar1a->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->endTimeZone()); + + /** test day part event with different start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1b, $this->vCalendar1b->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Vancouver')), $er->endTimeZone()); + + /** test day part event with global time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1c, $this->vCalendar1c->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->endTimeZone()); + + /** test day part event with no time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1d, $this->vCalendar1d->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('UTC')), $er->endTimeZone()); + + /** test full day event */ + // construct event reader + $er = new EventReader($this->vCalendar2, $this->vCalendar2->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->endTimeZone()); + + /** test multi day event */ + // construct event reader + $er = new EventReader($this->vCalendar3, $this->vCalendar3->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new DateTimeZone('America/Toronto')), $er->endTimeZone()); + + } + + public function testEntireDay(): void { + + /** test day part event with same start/end time zone */ + // construct event reader + $er = new EventReader($this->vCalendar1a, $this->vCalendar1a->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertFalse($er->entireDay()); + + /** test full day event */ + // construct event reader + $er = new EventReader($this->vCalendar2, $this->vCalendar2->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->entireDay()); + + /** test multi day event */ + // construct event reader + $er = new EventReader($this->vCalendar3, $this->vCalendar3->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->entireDay()); + + } + + public function testRecurs(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertFalse($er->recurs()); + + /** test rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurs()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurs()); + + } + + public function testRecurringPattern(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringPattern()); + + /** test absolute rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('A', $er->recurringPattern()); + + /** test relative rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=MO;BYSETPOS=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('R', $er->recurringPattern()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('A', $er->recurringPattern()); + + } + + public function testRecurringPrecision(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringPrecision()); + + /** test daily rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('daily', $er->recurringPrecision()); + + /** test weekly rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('weekly', $er->recurringPrecision()); + + /** test monthly rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8,15'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('monthly', $er->recurringPrecision()); + + /** test yearly rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYMONTHDAY=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('yearly', $er->recurringPrecision()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals('fixed', $er->recurringPrecision()); + + } + + public function testRecurringInterval(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringInterval()); + + /** test daily rrule recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(2, $er->recurringInterval()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringInterval()); + + } + + public function testRecurringConcludes(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertFalse($er->recurringConcludes()); + + /** test rrule recurrance with no end */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertFalse($er->recurringConcludes()); + + /** test rrule recurrance with until date end */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;UNTIL=20240712T080000Z;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurringConcludes()); + + /** test rrule recurrance with iteration end */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurringConcludes()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurringConcludes()); + + /** test rrule and rdate recurrance with rdate as last date */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + $vCalendar->VEVENT[0]->add('RDATE', '20240706,20240715'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurringConcludes()); + + /** test rrule and rdate recurrance with rrule as last date */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=7;BYDAY=MO,WE,FR'); + $vCalendar->VEVENT[0]->add('RDATE', '20240706,20240713'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertTrue($er->recurringConcludes()); + + } + + public function testRecurringConcludesAfter(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringConcludesAfter()); + + /** test rrule recurrance with count */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(6, $er->recurringConcludesAfter()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(2, $er->recurringConcludesAfter()); + + /** test rrule and rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + $vCalendar->VEVENT[0]->add('RDATE', '20240706,20240715'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(8, $er->recurringConcludesAfter()); + + } + + public function testRecurringConcludesOn(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringConcludesOn()); + + /** test rrule recurrance with no end */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertNull($er->recurringConcludesOn()); + + /** test rrule recurrance with until date end */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;UNTIL=20240712T080000Z;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + + // TODO: Fix until time zone + //$this->assertEquals((new \DateTime('20240712T080000', (new DateTimeZone('America/Toronto')))), $er->recurringConcludesOn()); + + /** test rdate recurrance */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703,20240705'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240705T000000', (new DateTimeZone('America/Toronto')))), $er->recurringConcludesOn()); + + /** test rrule and rdate recurrance with rdate as last date */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=6;BYDAY=MO,WE,FR'); + $vCalendar->VEVENT[0]->add('RDATE', '20240706,20240715'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240715T000000', (new DateTimeZone('America/Toronto')))), $er->recurringConcludesOn()); + + /** test rrule and rdate recurrance with rrule as last date */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;COUNT=7;BYDAY=MO,WE,FR'); + $vCalendar->VEVENT[0]->add('RDATE', '20240706,20240713'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals((new \DateTime('20240715T080000', (new DateTimeZone('America/Toronto')))), $er->recurringConcludesOn()); + + } + + public function testRecurringDaysOfWeek(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringDaysOfWeek()); + + /** test rrule recurrance with weekly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;UNTIL=20240712T080000Z;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(['MO','WE','FR'], $er->recurringDaysOfWeek()); + + } + + public function testRecurringDaysOfWeekNamed(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringDaysOfWeekNamed()); + + /** test rrule recurrance with weekly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;UNTIL=20240712T080000Z;BYDAY=MO,WE,FR'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(['Monday','Wednesday','Friday'], $er->recurringDaysOfWeekNamed()); + + } + + public function testRecurringDaysOfMonth(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringDaysOfMonth()); + + /** test rrule recurrance with monthly absolute dates*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=6,13,20,27'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([6,13,20,27], $er->recurringDaysOfMonth()); + + } + + public function testRecurringDaysOfYear(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringDaysOfYear()); + + /** test rrule recurrance with monthly absolute dates*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYYEARDAY=1,30,180,365'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([1,30,180,365], $er->recurringDaysOfYear()); + + } + + public function testRecurringWeeksOfMonth(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringWeeksOfMonth()); + + /** test rrule recurrance with monthly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=MO;BYSETPOS=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([1], $er->recurringWeeksOfMonth()); + + } + + public function testRecurringWeeksOfMonthNamed(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringWeeksOfMonthNamed()); + + /** test rrule recurrance with weekly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=MO;BYSETPOS=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(['First'], $er->recurringWeeksOfMonthNamed()); + + } + + public function testRecurringWeeksOfYear(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringWeeksOfYear()); + + /** test rrule recurrance with monthly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;INTERVAL=1;BYWEEKNO=35,42;BYDAY=TU'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([35,42], $er->recurringWeeksOfYear()); + + } + + public function testRecurringMonthsOfYear(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringMonthsOfYear()); + + /** test rrule recurrance with monthly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;INTERVAL=1;BYMONTH=7;BYMONTHDAY=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([7], $er->recurringMonthsOfYear()); + + } + + public function testRecurringMonthsOfYearNamed(): void { + + /** test no recurrance */ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals([], $er->recurringMonthsOfYearNamed()); + + /** test rrule recurrance with weekly days*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;INTERVAL=1;BYMONTH=7;BYMONTHDAY=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test set by constructor + $this->assertEquals(['July'], $er->recurringMonthsOfYearNamed()); + + } + + public function testRecurringIterationDaily(): void { + + /** test rrule recurrance with daily frequency*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;UNTIL=20240714T040000Z'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240704T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240707T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240710T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240713T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20240709T080000'))); + $this->assertEquals((new \DateTime('20240710T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationWeekly(): void { + + /** test rrule recurrance with weekly frequency*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240713T040000Z'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240703T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240705T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240708T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240710T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240712T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20240709T080000'))); + $this->assertEquals((new \DateTime('20240710T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationMonthlyAbsolute(): void { + + /** test rrule recurrance with monthly absolute frequency on the 1st of each month*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;COUNT=3;BYMONTHDAY=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240801T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240901T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20240809T080000'))); + $this->assertEquals((new \DateTime('20240901T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationMonthlyRelative(): void { + + /** test rrule recurrance with monthly relative frequency on the first monday of each month*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;COUNT=3;BYDAY=MO;BYSETPOS=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240805T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240902T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20240809T080000'))); + $this->assertEquals((new \DateTime('20240902T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationYearlyAbsolute(): void { + + /** test rrule recurrance with yearly absolute frequency on the 1st of july*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;COUNT=3;BYMONTH=7'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20250701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20260701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20250809T080000'))); + $this->assertEquals((new \DateTime('20260701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationYearlyRelative(): void { + + /** test rrule recurrance with yearly relative frequency on the first monday of july*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;COUNT=3;BYMONTH=7;BYDAY=MO;BYSETPOS=1'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20250707T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20260706T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20250809T080000'))); + $this->assertEquals((new \DateTime('20260706T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + + public function testRecurringIterationFixed(): void { + + /** test rrule recurrance with yearly relative frequency on the first monday of july*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000,20240905T080000,20241231T080000'); + // construct event reader + $er = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test initial recurrance + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240703T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20240905T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvance(); + $this->assertEquals((new \DateTime('20241231T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance (This is past the last recurrance and should return null) + $er->recurrenceAdvance(); + $this->assertNull($er->recurrenceDate()); + // test rewind to initial recurrance + $er->recurrenceRewind(); + $this->assertEquals((new \DateTime('20240701T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + // test next recurrance + $er->recurrenceAdvanceTo((new \DateTime('20240809T080000'))); + $this->assertEquals((new \DateTime('20240905T080000', (new DateTimeZone('America/Toronto')))), $er->recurrenceDate()); + + } + +} diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php index e4a9f1b75d0..667604f9b3e 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php @@ -10,15 +10,15 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; use OC\L10N\L10N; use OC\L10N\LazyL10N; use OC\URLGenerator; +use OCA\DAV\CalDAV\EventReader; use OCA\DAV\CalDAV\Schedule\IMipService; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\L10N\IFactory as L10NFactory; use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; use Sabre\VObject\Component\VCalendar; -use Sabre\VObject\Component\VEvent; -use Sabre\VObject\ITip\Message; use Sabre\VObject\Property\ICalendar\DateTime; use Test\TestCase; @@ -41,9 +41,23 @@ class IMipServiceTest extends TestCase { /** @var L10N|MockObject */ private $l10n; + /** @var ITimeFactory|MockObject */ + private $timeFactory; + /** @var IMipService */ private $service; + /** @var VCalendar*/ + private $vCalendar1a; + /** @var VCalendar*/ + private $vCalendar1b; + /** @var VCalendar*/ + private $vCalendar2; + /** @var VCalendar*/ + private $vCalendar3; + /** @var DateTime DateTime object that will be returned by DateTime() or DateTime('now') */ + public static $datetimeNow; + protected function setUp(): void { $this->urlGenerator = $this->createMock(URLGenerator::class); $this->config = $this->createMock(IConfig::class); @@ -51,6 +65,7 @@ class IMipServiceTest extends TestCase { $this->random = $this->createMock(ISecureRandom::class); $this->l10nFactory = $this->createMock(L10NFactory::class); $this->l10n = $this->createMock(LazyL10N::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); $this->l10nFactory->expects(self::once()) ->method('findGenericLanguage') ->willReturn('en'); @@ -63,8 +78,81 @@ class IMipServiceTest extends TestCase { $this->config, $this->db, $this->random, - $this->l10nFactory + $this->l10nFactory, + $this->timeFactory ); + + // construct calendar with a 1 hour event and same start/end time zones + $this->vCalendar1a = new VCalendar(); + $vEvent = $this->vCalendar1a->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']); + $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']); + $vEvent->add('SUMMARY', 'Testing Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a 1 hour event and different start/end time zones + $this->vCalendar1b = new VCalendar(); + $vEvent = $this->vCalendar1b->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']); + $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Vancouver']); + $vEvent->add('SUMMARY', 'Testing Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a full day event + $this->vCalendar2 = new VCalendar(); + // time zone component + $vTimeZone = $this->vCalendar2->add('VTIMEZONE'); + $vTimeZone->add('TZID', 'America/Toronto'); + // event component + $vEvent = $this->vCalendar2->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701'); + $vEvent->add('DTEND', '20240702'); + $vEvent->add('SUMMARY', 'Testing Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); + + // construct calendar with a multi day event + $this->vCalendar3 = new VCalendar(); + // time zone component + $vTimeZone = $this->vCalendar3->add('VTIMEZONE'); + $vTimeZone->add('TZID', 'America/Toronto'); + // event component + $vEvent = $this->vCalendar3->add('VEVENT', []); + $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc'); + $vEvent->add('DTSTART', '20240701'); + $vEvent->add('DTEND', '20240706'); + $vEvent->add('SUMMARY', 'Testing Event'); + $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']); + $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [ + 'CN' => 'Attendee One', + 'CUTYPE' => 'INDIVIDUAL', + 'PARTSTAT' => 'NEEDS-ACTION', + 'ROLE' => 'REQ-PARTICIPANT', + 'RSVP' => 'TRUE' + ]); } public function testGetFrom(): void { @@ -81,96 +169,93 @@ class IMipServiceTest extends TestCase { } public function testBuildBodyDataCreated(): void { - $vCalendar = new VCalendar(); - $oldVevent = null; - $newVevent = new VEvent($vCalendar, 'two', [ - 'UID' => 'uid-1234', - 'SEQUENCE' => 3, - 'LAST-MODIFIED' => 789456, - 'SUMMARY' => 'Second Breakfast', - 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), - 'RECURRENCE-ID' => new \DateTime('2016-01-01 00:00:00') + + // construct l10n return(s) + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'] ]); - + // construct time factory return(s) + $this->timeFactory->method('getDateTime')->willReturnCallback( + function ($v1, $v2) { + return match (true) { + $v1 == 'now' && $v2 == null => (new \DateTime('20240630T000000')) + }; + } + ); + /** test singleton partial day event*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // define expected output $expected = [ - 'meeting_when' => $this->service->generateWhenString($newVevent), + 'meeting_when' => $this->service->generateWhenString($eventReader), 'meeting_description' => '', - 'meeting_title' => 'Second Breakfast', + 'meeting_title' => 'Testing Event', 'meeting_location' => '', 'meeting_url' => '', 'meeting_url_html' => '', ]; - - $actual = $this->service->buildBodyData($newVevent, $oldVevent); - + // generate actual output + $actual = $this->service->buildBodyData($vCalendar->VEVENT[0], null); + // test output $this->assertEquals($expected, $actual); } public function testBuildBodyDataUpdate(): void { - $vCalendar = new VCalendar(); - $oldVevent = new VEvent($vCalendar, 'two', [ - 'UID' => 'uid-1234', - 'SEQUENCE' => 1, - 'LAST-MODIFIED' => 456789, - 'SUMMARY' => 'Elevenses', - 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), - 'RECURRENCE-ID' => new \DateTime('2016-01-01 00:00:00') - ]); - $oldVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); - $oldVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); - $newVevent = new VEvent($vCalendar, 'two', [ - 'UID' => 'uid-1234', - 'SEQUENCE' => 3, - 'LAST-MODIFIED' => 789456, - 'SUMMARY' => 'Second Breakfast', - 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), - 'RECURRENCE-ID' => new \DateTime('2016-01-01 00:00:00') + + // construct l10n return(s) + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'] ]); - + // construct time factory return(s) + $this->timeFactory->method('getDateTime')->willReturnCallback( + function ($v1, $v2) { + return match (true) { + $v1 == 'now' && $v2 == null => (new \DateTime('20240630T000000')) + }; + } + ); + /** test singleton partial day event*/ + $vCalendarNew = clone $this->vCalendar1a; + $vCalendarOld = clone $this->vCalendar1a; + // construct event reader + $eventReaderNew = new EventReader($vCalendarNew, $vCalendarNew->VEVENT[0]->UID->getValue()); + // alter old event label/title + $vCalendarOld->VEVENT[0]->SUMMARY->setValue('Testing Singleton Event'); + // define expected output $expected = [ - 'meeting_when' => $this->service->generateWhenString($newVevent), + 'meeting_when' => $this->service->generateWhenString($eventReaderNew), 'meeting_description' => '', - 'meeting_title' => 'Second Breakfast', + 'meeting_title' => 'Testing Event', 'meeting_location' => '', 'meeting_url' => '', 'meeting_url_html' => '', - 'meeting_when_html' => $this->service->generateWhenString($newVevent), - 'meeting_title_html' => sprintf("<span style='text-decoration: line-through'>%s</span><br />%s", 'Elevenses', 'Second Breakfast'), + 'meeting_when_html' => $this->service->generateWhenString($eventReaderNew), + 'meeting_title_html' => sprintf("<span style='text-decoration: line-through'>%s</span><br />%s", 'Testing Singleton Event', 'Testing Event'), 'meeting_description_html' => '', 'meeting_location_html' => '' ]; - - $actual = $this->service->buildBodyData($newVevent, $oldVevent); - - $this->assertEquals($expected, $actual); - } - - public function testGenerateWhenStringHourlyEvent(): void { - $vCalendar = new VCalendar(); - $vevent = new VEvent($vCalendar, 'two', [ - 'UID' => 'uid-1234', - 'SEQUENCE' => 1, - 'LAST-MODIFIED' => 456789, - 'SUMMARY' => 'Elevenses', - 'TZID' => 'Europe/Vienna', - 'DTSTART' => (new \DateTime('2016-01-01 08:00:00'))->setTimezone(new \DateTimeZone('Europe/Vienna')), - 'DTEND' => (new \DateTime('2016-01-01 09:00:00'))->setTimezone(new \DateTimeZone('Europe/Vienna')), - ]); - - $this->l10n->expects(self::exactly(3)) - ->method('l') - ->withConsecutive( - ['weekdayName', (new \DateTime('2016-01-01 08:00:00'))->setTimezone(new \DateTimeZone('Europe/Vienna')), ['width' => 'abbreviated']], - ['datetime', (new \DateTime('2016-01-01 08:00:00'))->setTimezone(new \DateTimeZone('Europe/Vienna')), ['width' => 'medium|short']], - ['time', (new \DateTime('2016-01-01 09:00:00'))->setTimezone(new \DateTimeZone('Europe/Vienna')), ['width' => 'short']] - )->willReturnOnConsecutiveCalls( - 'Fr.', - '01.01. 08:00', - '09:00' - ); - - $expected = 'Fr., 01.01. 08:00 - 09:00 (Europe/Vienna)'; - $actual = $this->service->generateWhenString($vevent); + // generate actual output + $actual = $this->service->buildBodyData($vCalendarNew->VEVENT[0], $vCalendarOld->VEVENT[0]); + // test output $this->assertEquals($expected, $actual); } @@ -250,73 +335,1021 @@ class IMipServiceTest extends TestCase { $this->assertEquals(1451606400, $occurrence); } - public function testGetCurrentAttendeeRequest(): void { - // Construct ITip Message - $message = new Message(); - $message->method = 'REQUEST'; - $message->sequence = 1; - $message->sender = 'mailto:organizer@example.com'; - $message->senderName = 'The Organizer'; - $message->recipient = 'mailto:attendee@example.com'; - $message->recipientName = 'The Attendee'; - $message->significantChange = true; - $message->message = new VCalendar(); - $message->message->add('VEVENT', ['UID' => '82496785-1915-4604-a5ce-4e2091639c9a', 'SEQUENCE' => 1]); - $message->message->VEVENT->add('SUMMARY', 'Fellowship meeting'); - $message->message->VEVENT->add('DTSTART', (new \DateTime('NOW'))->modify('+1 hour')); - $message->message->VEVENT->add('DTEND', (new \DateTime('NOW'))->modify('+2 hour')); - $message->message->VEVENT->add('ORGANIZER', 'mailto:organizer@example.com', ['CN' => 'The Organizer']); - $message->message->VEVENT->add('ATTENDEE', 'mailto:attendee@example.com', ['CN' => 'The Attendee']); - // Test getCurrentAttendee - $result = $this->service->getCurrentAttendee($message); - // Evaluate Result - $this->assertEquals($message->message->VEVENT->ATTENDEE, $result); + public function testGenerateWhenStringSingular(): void { + + // construct l10n return(s) + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024', + $v1 === 'date' && $v2 == (new \DateTime('20240701T000000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'full'] => 'July 1, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['In a %1$s on %2$s for the entire day', ['day', 'July 1, 2024'], 'In a day on July 1, 2024 for the entire day'], + ['In a %1$s on %2$s between %3$s - %4$s', ['day', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In %1$s %2$s on %3$s for the entire day', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024 for the entire day'], + ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'days', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 days on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In a %1$s on %2$s for the entire day', ['week', 'July 1, 2024'], 'In a week on July 1, 2024 for the entire day'], + ['In a %1$s on %2$s between %3$s - %4$s', ['week', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a week on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In %1$s %2$s on %3$s for the entire day', [2, 'weeks', 'July 1, 2024'], 'In 2 weeks on July 1, 2024 for the entire day'], + ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'weeks', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 weeks on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In a %1$s on %2$s for the entire day', ['month', 'July 1, 2024'], 'In a month on July 1, 2024 for the entire day'], + ['In a %1$s on %2$s between %3$s - %4$s', ['month', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a month on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In %1$s %2$s on %3$s for the entire day', [2, 'months', 'July 1, 2024'], 'In 2 months on July 1, 2024 for the entire day'], + ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'months', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 months on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In a %1$s on %2$s for the entire day', ['year', 'July 1, 2024'], 'In a year on July 1, 2024 for the entire day'], + ['In a %1$s on %2$s between %3$s - %4$s', ['year', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In a year on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['In %1$s %2$s on %3$s for the entire day', [2, 'years', 'July 1, 2024'], 'In 2 years on July 1, 2024 for the entire day'], + ['In %1$s %2$s on %3$s between %4$s - %5$s', [2, 'years', 'July 1, 2024', '8:00 AM', '9:00 AM (America/Toronto)'], 'In 2 years on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)'] + ]); + + // construct time factory return(s) + $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls( + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240621T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240621T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240614T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240614T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240530T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240530T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240430T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240430T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20230630T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20230630T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20220630T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20220630T170000', (new \DateTimeZone('America/Toronto')))) + ); + + /** test patrial day event in 1 day*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a day on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 1 day*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a day on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 2 days*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 days on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 2 days*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 days on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 1 week*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a week on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 1 week*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a week on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 2 weeks*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 weeks on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 2 weeks*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 weeks on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 1 month*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a month on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 1 month*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a month on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 2 months*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 months on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 2 months*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 months on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 1 year*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a year on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 1 year*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a year on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test patrial day event in 2 years*/ + $vCalendar = clone $this->vCalendar1a; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 years on July 1, 2024 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event in 2 years*/ + $vCalendar = clone $this->vCalendar2; + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 years on July 1, 2024 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + } + + public function testGenerateWhenStringRecurringDaily(): void { + + // construct l10n return maps + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240713T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 13, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['Every Day for the entire day', [], 'Every Day for the entire day'], + ['Every Day for the entire day until %1$s', ['July 13, 2024'], 'Every Day for the entire day until July 13, 2024'], + ['Every Day between %1$s - %2$s', ['8:00 AM', '9:00 AM (America/Toronto)'], 'Every Day between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Day between %1$s - %2$s until %3$s', ['8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every Day between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'], + ['Every %1$d Days for the entire day', [3], 'Every 3 Days for the entire day'], + ['Every %1$d Days for the entire day until %2$s', [3, 'July 13, 2024'], 'Every 3 Days for the entire day until July 13, 2024'], + ['Every %1$d Days between %2$s - %3$s', [3, '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Days between %2$s - %3$s until %4$s', [3, '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'], + ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'], + ]); + + /** test partial day event with every day interval and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Day between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event with every day interval and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;UNTIL=20240713T080000Z'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Day between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event every 3rd day interval and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event with every 3rd day interval and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;UNTIL=20240713T080000Z'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 3 Days between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every day interval and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Day for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every day interval and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=1;UNTIL=20240713T080000Z'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Day for the entire day until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every 3rd day interval and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 3 Days for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every 3rd day interval and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=3;UNTIL=20240713T080000Z'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 3 Days for the entire day until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + } + + public function testGenerateWhenStringRecurringWeekly(): void { + + // construct l10n return maps + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240722T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 13, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['Every Week on %1$s for the entire day', ['Monday, Wednesday, Friday'], 'Every Week on Monday, Wednesday, Friday for the entire day'], + ['Every Week on %1$s for the entire day until %2$s', ['Monday, Wednesday, Friday', 'July 13, 2024'], 'Every Week on Monday, Wednesday, Friday for the entire day until July 13, 2024'], + ['Every Week on %1$s between %2$s - %3$s', ['Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Week on %1$s between %2$s - %3$s until %4$s', ['Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'], + ['Every %1$d Weeks on %2$s for the entire day', [2, 'Monday, Wednesday, Friday'], 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day'], + ['Every %1$d Weeks on %2$s for the entire day until %3$s', [2, 'Monday, Wednesday, Friday', 'July 13, 2024'], 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day until July 13, 2024'], + ['Every %1$d Weeks on %2$s between %3$s - %4$s', [2, 'Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Weeks on %2$s between %3$s - %4$s until %5$s', [2, 'Monday, Wednesday, Friday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'], + ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'], + ['Monday', [], 'Monday'], + ['Wednesday', [], 'Wednesday'], + ['Friday', [], 'Friday'], + ]); + + /** test partial day event with every week interval on Mon, Wed, Fri and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event with every week interval on Mon, Wed, Fri and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240722T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Week on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event with every 2nd week interval on Mon, Wed, Fri and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test partial day event with every 2nd week interval on Mon, Wed, Fri and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;UNTIL=20240722T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Weeks on Monday, Wednesday, Friday between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every week interval on Mon, Wed, Fri and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Week on Monday, Wednesday, Friday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every week interval on Mon, Wed, Fri and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20240722T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Week on Monday, Wednesday, Friday for the entire day until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every 2nd week interval on Mon, Wed, Fri and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every 2nd week interval on Mon, Wed, Fri and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=WEEKLY;BYDAY=MO,WE,FR;INTERVAL=2;UNTIL=20240722T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Weeks on Monday, Wednesday, Friday for the entire day until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + } + + public function testGenerateWhenStringRecurringMonthly(): void { + + // construct l10n return maps + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20241231T080000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'December 31, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['Every Month on the %1$s for the entire day', ['1, 8'], 'Every Month on the 1, 8 for the entire day'], + ['Every Month on the %1$s for the entire day until %2$s', ['1, 8', 'December 31, 2024'], 'Every Month on the 1, 8 for the entire day until December 31, 2024'], + ['Every Month on the %1$s between %2$s - %3$s', ['1, 8', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Month on the %1$s between %2$s - %3$s until %4$s', ['1, 8', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'], + ['Every %1$d Months on the %2$s for the entire day', [2, '1, 8'], 'Every 2 Months on the 1, 8 for the entire day'], + ['Every %1$d Months on the %2$s for the entire day until %3$s', [2, '1, 8', 'December 31, 2024'], 'Every 2 Months on the 1, 8 for the entire day until December 31, 2024'], + ['Every %1$d Months on the %2$s between %3$s - %4$s', [2, '1, 8', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Months on the %2$s between %3$s - %4$s until %5$s', [2, '1, 8', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'], + ['Every Month on the %1$s for the entire day', ['First Sunday, Saturday'], 'Every Month on the First Sunday, Saturday for the entire day'], + ['Every Month on the %1$s for the entire day until %2$s', ['First Sunday, Saturday', 'December 31, 2024'], 'Every Month on the First Sunday, Saturday for the entire day until December 31, 2024'], + ['Every Month on the %1$s between %2$s - %3$s', ['First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Month on the %1$s between %2$s - %3$s until %4$s', ['First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'], + ['Every %1$d Months on the %2$s for the entire day', [2, 'First Sunday, Saturday'], 'Every 2 Months on the First Sunday, Saturday for the entire day'], + ['Every %1$d Months on the %2$s for the entire day until %3$s', [2, 'First Sunday, Saturday', 'December 31, 2024'], 'Every 2 Months on the First Sunday, Saturday for the entire day until December 31, 2024'], + ['Every %1$d Months on the %2$s between %3$s - %4$s', [2, 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Months on the %2$s between %3$s - %4$s until %5$s', [2, 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'December 31, 2024'], 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024'], + ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'], + ['Saturday', [], 'Saturday'], + ['Sunday', [], 'Sunday'], + ['First', [], 'First'], + ]); + + /** test absolute partial day event with every month interval on 1st, 8th and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every Month interval on 1st, 8th and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every 2nd Month interval on 1st, 8th and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every 2nd Month interval on 1st, 8th and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the 1, 8 between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every Month interval on 1st, 8th and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the 1, 8 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every Month interval on 1st, 8th and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the 1, 8 for the entire day until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every 2nd Month interval on 1st, 8th and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the 1, 8 for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every 2nd Month interval on 1st, 8th and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYMONTHDAY=1,8;INTERVAL=2;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the 1, 8 for the entire day until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every month interval on the 1st Saturday, Sunday and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every Month interval on the 1st Saturday, Sunday and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every 2nd Month interval on the 1st Saturday, Sunday and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every 2nd Month interval on the 1st Saturday, Sunday and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every Month interval on the 1st Saturday, Sunday and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the First Sunday, Saturday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every Month interval on the 1st Saturday, Sunday and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Month on the First Sunday, Saturday for the entire day until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every 2nd Month interval on the 1st Saturday, Sunday and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the First Sunday, Saturday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every 2nd Month interval on the 1st Saturday, Sunday and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=MONTHLY;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20241231T080000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Months on the First Sunday, Saturday for the entire day until December 31, 2024', + $this->service->generateWhenString($eventReader) + ); + + } + + public function testGenerateWhenStringRecurringYearly(): void { + + // construct l10n return maps + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20260731T040000', (new \DateTimeZone('UTC')))) && $v3 == ['width' => 'long'] => 'July 31, 2026' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['Every Year in %1$s on the %2$s for the entire day', ['July', '1st'], 'Every Year in July on the 1st for the entire day'], + ['Every Year in %1$s on the %2$s for the entire day until %3$s', ['July', '1st', 'July 31, 2026'], 'Every Year in July on the 1st for the entire day until July 31, 2026'], + ['Every Year in %1$s on the %2$s between %3$s - %4$s', ['July', '1st', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s', ['July', '1st', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'], + ['Every %1$d Years in %2$s on the %3$s for the entire day', [2, 'July', '1st'], 'Every 2 Years in July on the 1st for the entire day'], + ['Every %1$d Years in %2$s on the %3$s for the entire day until %4$s', [2, 'July', '1st', 'July 31, 2026'], 'Every 2 Years in July on the 1st for the entire day until July 31, 2026'], + ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s', [2, 'July', '1st', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s', [2, 'July', '1st', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'], + ['Every Year in %1$s on the %2$s for the entire day', ['July', 'First Sunday, Saturday'], 'Every Year in July on the First Sunday, Saturday for the entire day'], + ['Every Year in %1$s on the %2$s for the entire day until %3$s', ['July', 'First Sunday, Saturday', 'July 31, 2026'], 'Every Year in July on the First Sunday, Saturday for the entire day until July 31, 2026'], + ['Every Year in %1$s on the %2$s between %3$s - %4$s', ['July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s', ['July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'], + ['Every %1$d Years in %2$s on the %3$s for the entire day', [2, 'July', 'First Sunday, Saturday'], 'Every 2 Years in July on the First Sunday, Saturday for the entire day'], + ['Every %1$d Years in %2$s on the %3$s for the entire day until %4$s', [2, 'July', 'First Sunday, Saturday', 'July 31, 2026'], 'Every 2 Years in July on the First Sunday, Saturday for the entire day until July 31, 2026'], + ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s', [2, 'July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)'], 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)'], + ['Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s', [2, 'July', 'First Sunday, Saturday', '8:00 AM', '9:00 AM (America/Toronto)', 'July 31, 2026'], 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026'], + ['Could not generate event recurrence statement', [], 'Could not generate event recurrence statement'], + ['July', [], 'July'], + ['Saturday', [], 'Saturday'], + ['Sunday', [], 'Sunday'], + ['First', [], 'First'], + ]); + + /** test absolute partial day event with every year interval on July 1 and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every year interval on July 1 and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;UNTIL=20260731T040000Z'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every 2nd year interval on July 1 and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute partial day event with every 2nd year interval on July 1 and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the 1st between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every year interval on July 1 and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the 1st for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every year interval on July 1 and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the 1st for the entire day until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every 2nd year interval on July 1 and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the 1st for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test absolute entire day event with every 2nd year interval on July 1 and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;INTERVAL=2;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the 1st for the entire day until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every year interval on the 1st Saturday, Sunday in July and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every year interval on the 1st Saturday, Sunday in July and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every 2nd year interval on the 1st Saturday, Sunday in July and no conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto)', + $this->service->generateWhenString($eventReader) + ); + + /** test relative partial day event with every 2nd year interval on the 1st Saturday, Sunday in July and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the First Sunday, Saturday between 8:00 AM - 9:00 AM (America/Toronto) until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every year interval on the 1st Saturday, Sunday in July and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the First Sunday, Saturday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every year interval on the 1st Saturday, Sunday in July and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every Year in July on the First Sunday, Saturday for the entire day until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every 2nd year interval on the 1st Saturday, Sunday in July and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the First Sunday, Saturday for the entire day', + $this->service->generateWhenString($eventReader) + ); + + /** test relative entire day event with every 2nd year interval on the 1st Saturday, Sunday in July and conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=YEARLY;BYMONTH=7;BYDAY=SU,SA;BYSETPOS=1;INTERVAL=2;UNTIL=20260731T040000Z;'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'Every 2 Years in July on the First Sunday, Saturday for the entire day until July 31, 2026', + $this->service->generateWhenString($eventReader) + ); + } - public function testGetCurrentAttendeeReply(): void { - // Construct ITip Message - $message = new Message(); - $message->method = 'REPLY'; - $message->sequence = 2; - $message->sender = 'mailto:attendee@example.com'; - $message->senderName = 'The Attendee'; - $message->recipient = 'mailto:organizer@example.com'; - $message->recipientName = 'The Organizer'; - $message->significantChange = true; - $message->message = new VCalendar(); - $message->message->add('METHOD', 'REPLY'); - $message->message->add('VEVENT', ['UID' => '82496785-1915-4604-a5ce-4e2091639c9a', 'SEQUENCE' => 2]); - $message->message->VEVENT->add('SUMMARY', 'Fellowship meeting'); - $message->message->VEVENT->add('DTSTART', (new \DateTime('NOW'))->modify('+1 hour')); - $message->message->VEVENT->add('DTEND', (new \DateTime('NOW'))->modify('+2 hour')); - $message->message->VEVENT->add('ORGANIZER', 'mailto:organizer@example.com', ['CN' => 'The Organizer']); - $message->message->VEVENT->add('ATTENDEE', 'mailto:attendee@example.com', ['CN' => 'The Attendee']); - // Test getCurrentAttendee - $result = $this->service->getCurrentAttendee($message); - // Evaluate Result - $this->assertEquals($message->message->VEVENT->ATTENDEE, $result); + public function testGenerateWhenStringRecurringFixed(): void { + + // construct l10n return maps + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'time' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '8:00 AM', + $v1 === 'time' && $v2 == (new \DateTime('20240701T090000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'short'] => '9:00 AM', + $v1 === 'date' && $v2 == (new \DateTime('20240713T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 13, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['On specific dates for the entire day until %1$s', ['July 13, 2024'], 'On specific dates for the entire day until July 13, 2024'], + ['On specific dates between %1$s - %2$s until %3$s', ['8:00 AM', '9:00 AM (America/Toronto)', 'July 13, 2024'], 'On specific dates between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024'], + ]); + + /** test partial day event with every day interval and conclusion*/ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000,20240709T080000,20240713T080000'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'On specific dates between 8:00 AM - 9:00 AM (America/Toronto) until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + + /** test entire day event with every day interval and no conclusion*/ + $vCalendar = clone $this->vCalendar2; + $vCalendar->VEVENT[0]->add('RDATE', '20240703T080000,20240709T080000,20240713T080000'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'On specific dates for the entire day until July 13, 2024', + $this->service->generateWhenString($eventReader) + ); + } - public function testGetCurrentAttendeeMismatch(): void { - // Construct ITip Message - $message = new Message(); - $message->method = 'REQUEST'; - $message->sequence = 1; - $message->sender = 'mailto:organizer@example.com'; - $message->senderName = 'The Organizer'; - $message->recipient = 'mailto:mismatch@example.com'; - $message->recipientName = 'The Mismatch'; - $message->significantChange = true; - $message->message = new VCalendar(); - $message->message->add('VEVENT', ['UID' => '82496785-1915-4604-a5ce-4e2091639c9a', 'SEQUENCE' => 1]); - $message->message->VEVENT->add('SUMMARY', 'Fellowship meeting'); - $message->message->VEVENT->add('DTSTART', (new \DateTime('NOW'))->modify('+1 hour')); - $message->message->VEVENT->add('DTEND', (new \DateTime('NOW'))->modify('+2 hour')); - $message->message->VEVENT->add('ORGANIZER', 'mailto:organizer@example.com', ['CN' => 'The Organizer']); - $message->message->VEVENT->add('ATTENDEE', 'mailto:attendee@example.com', ['CN' => 'The Attendee']); - // Test getCurrentAttendee - $result = $this->service->getCurrentAttendee($message); - // Evaluate Result - $this->assertEquals(null, $result); + public function testGenerateOccurringString(): void { + + // construct l10n return(s) + $this->l10n->method('l')->willReturnCallback( + function ($v1, $v2, $v3) { + return match (true) { + $v1 === 'date' && $v2 == (new \DateTime('20240701T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 1, 2024', + $v1 === 'date' && $v2 == (new \DateTime('20240703T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 3, 2024', + $v1 === 'date' && $v2 == (new \DateTime('20240705T080000', (new \DateTimeZone('America/Toronto')))) && $v3 == ['width' => 'long'] => 'July 5, 2024' + }; + } + ); + $this->l10n->method('t')->willReturnMap([ + ['In a %1$s on %2$s', ['day', 'July 1, 2024'], 'In a day on July 1, 2024'], + ['In a %1$s on %2$s then on %3$s', ['day', 'July 1, 2024', 'July 3, 2024'], 'In a day on July 1, 2024 then on July 3, 2024'], + ['In a %1$s on %2$s then on %3$s and %4$s', ['day', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024'], + ['In %1$s %2$s on %3$s', [2, 'days', 'July 1, 2024'], 'In 2 days on July 1, 2024'], + ['In %1$s %2$s on %3$s then on %4$s', [2, 'days', 'July 1, 2024', 'July 3, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024'], + ['In %1$s %2$s on %3$s then on %4$s and %5$s', [2, 'days', 'July 1, 2024', 'July 3, 2024', 'July 5, 2024'], 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024'], + ]); + + // construct time factory return(s) + $this->timeFactory->method('getDateTime')->willReturnOnConsecutiveCalls( + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240629T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + (new \DateTime('20240628T170000', (new \DateTimeZone('America/Toronto')))), + ); + + /** test patrial day recurring event in 1 day with single occurance remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a day on July 1, 2024', + $this->service->generateOccurringString($eventReader) + ); + + /** test patrial day recurring event in 1 day with two occurances remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a day on July 1, 2024 then on July 3, 2024', + $this->service->generateOccurringString($eventReader) + ); + + /** test patrial day recurring event in 1 day with three occurances remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In a day on July 1, 2024 then on July 3, 2024 and July 5, 2024', + $this->service->generateOccurringString($eventReader) + ); + + /** test patrial day recurring event in 2 days with single occurance remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=1'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 days on July 1, 2024', + $this->service->generateOccurringString($eventReader) + ); + + /** test patrial day recurring event in 2 days with two occurances remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=2'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 days on July 1, 2024 then on July 3, 2024', + $this->service->generateOccurringString($eventReader) + ); + + /** test patrial day recurring event in 2 days with three occurances remaining */ + $vCalendar = clone $this->vCalendar1a; + $vCalendar->VEVENT[0]->add('RRULE', 'FREQ=DAILY;INTERVAL=2;COUNT=3'); + // construct event reader + $eventReader = new EventReader($vCalendar, $vCalendar->VEVENT[0]->UID->getValue()); + // test output + $this->assertEquals( + 'In 2 days on July 1, 2024 then on July 3, 2024 and July 5, 2024', + $this->service->generateOccurringString($eventReader) + ); } + } |