diff options
author | Anna Larch <anna@nextcloud.com> | 2022-12-12 20:46:37 +0100 |
---|---|---|
committer | Anna Larch <anna@nextcloud.com> | 2023-02-02 16:25:59 +0100 |
commit | 38e9cb6a052f4d0bc4e34293febbe9f132055a83 (patch) | |
tree | c634406b31b3d0c19b28a3c1684bdcd0325e7d03 /apps/dav/tests | |
parent | db30974348ee88fbf81d9cc0f1a1071d1adcbd09 (diff) | |
download | nextcloud-server-38e9cb6a052f4d0bc4e34293febbe9f132055a83.tar.gz nextcloud-server-38e9cb6a052f4d0bc4e34293febbe9f132055a83.zip |
Use recurrence instance to build iMip email
instead of the main VEVENT of a repeating event
Fixes part of https://github.com/nextcloud/calendar/issues/3919
Signed-off-by: Anna Larch <anna@nextcloud.com>
Diffstat (limited to 'apps/dav/tests')
3 files changed, 875 insertions, 204 deletions
diff --git a/apps/dav/tests/unit/CalDAV/EventComparisonServiceTest.php b/apps/dav/tests/unit/CalDAV/EventComparisonServiceTest.php new file mode 100644 index 00000000000..c21be3065c5 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/EventComparisonServiceTest.php @@ -0,0 +1,146 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @author 2023 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV; + +use OCA\DAV\CalDAV\EventComparisonService; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VEvent; +use Test\TestCase; + +class EventComparisonServiceTest extends TestCase +{ + /** @var EventComparisonService */ + private $eventComparisonService; + + protected function setUp(): void + { + $this->eventComparisonService = new EventComparisonService(); + } + + public function testNoModifiedEvent(): void + { + $vCalendarOld = new VCalendar(); + $vCalendarNew = new VCalendar(); + + $vEventOld = $vCalendarOld->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z', + ]); + $vEventOld->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventOld->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $vEventNew = $vCalendarNew->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z', + ]); + $vEventNew->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventNew->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $result = $this->eventComparisonService->findModified($vCalendarNew, $vCalendarOld); + $this->assertEmpty($result['old']); + $this->assertEmpty($result['new']); + } + + public function testNewEvent(): void + { + $vCalendarOld = null; + $vCalendarNew = new VCalendar(); + + $vEventNew = $vCalendarNew->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z', + ]); + $vEventNew->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventNew->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $result = $this->eventComparisonService->findModified($vCalendarNew, $vCalendarOld); + $this->assertNull($result['old']); + $this->assertEquals([$vEventNew], $result['new']); + } + + public function testModifiedUnmodifiedEvent(): void + { + $vCalendarOld = new VCalendar(); + $vCalendarNew = new VCalendar(); + + $vEventOld1 = $vCalendarOld->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + $vEventOld1->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventOld1->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $vEventOld2 = $vCalendarOld->add('VEVENT', [ + 'UID' => 'uid-1235', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + $vEventOld2->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventOld2->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $vEventNew1 = $vCalendarNew->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + $vEventNew1->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventNew1->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $vEventNew2 = $vCalendarNew->add('VEVENT', [ + 'UID' => 'uid-1235', + 'LAST-MODIFIED' => 123457, + 'SEQUENCE' => 3, + 'SUMMARY' => 'Fellowship meeting 2', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + $vEventNew2->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $vEventNew2->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + + $result = $this->eventComparisonService->findModified($vCalendarNew, $vCalendarOld); + $this->assertEquals([$vEventOld2], $result['old']); + $this->assertEquals([$vEventNew2], $result['new']); + } +} diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php index ecb602813cc..fdd707247ac 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php @@ -29,28 +29,27 @@ */ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; +use OCA\DAV\CalDAV\EventComparisonService; use OCA\DAV\CalDAV\Schedule\IMipPlugin; +use OCA\DAV\CalDAV\Schedule\IMipService; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\IConfig; -use OCP\IDBConnection; -use OCP\IL10N; -use OCP\IURLGenerator; use OCP\IUserManager; -use OCP\L10N\IFactory; use OCP\Mail\IAttachment; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; use OCP\Mail\IMessage; -use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VEvent; use Sabre\VObject\ITip\Message; use Test\TestCase; +use function array_merge; class IMipPluginTest extends TestCase { + /** @var IMessage|MockObject */ private $mailMessage; @@ -72,19 +71,28 @@ class IMipPluginTest extends TestCase { /** @var IUserManager|MockObject */ private $userManager; - /** @var IQueryBuilder|MockObject */ - private $queryBuilder; - /** @var IMipPlugin */ private $plugin; + /** @var IMipService|MockObject */ + private $service; + + /** @var Defaults|MockObject */ + private $defaults; + + /** @var LoggerInterface|MockObject */ + private $logger; + + /** @var EventComparisonService|MockObject */ + private $eventComparisonService; + protected function setUp(): void { $this->mailMessage = $this->createMock(IMessage::class); $this->mailMessage->method('setFrom')->willReturn($this->mailMessage); $this->mailMessage->method('setReplyTo')->willReturn($this->mailMessage); $this->mailMessage->method('setTo')->willReturn($this->mailMessage); - $this->mailer = $this->getMockBuilder(IMailer::class)->disableOriginalConstructor()->getMock(); + $this->mailer = $this->createMock(IMailer::class); $this->mailer->method('createMessage')->willReturn($this->mailMessage); $this->emailTemplate = $this->createMock(IEMailTemplate::class); @@ -93,249 +101,482 @@ class IMipPluginTest extends TestCase { $this->emailAttachment = $this->createMock(IAttachment::class); $this->mailer->method('createAttachment')->willReturn($this->emailAttachment); - /** @var LoggerInterface|MockObject $logger */ - $logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock(); + $this->logger = $this->createMock(LoggerInterface::class); - $this->timeFactory = $this->getMockBuilder(ITimeFactory::class)->disableOriginalConstructor()->getMock(); + $this->timeFactory = $this->createMock(ITimeFactory::class); $this->timeFactory->method('getTime')->willReturn(1496912528); // 2017-01-01 $this->config = $this->createMock(IConfig::class); $this->userManager = $this->createMock(IUserManager::class); - $l10n = $this->createMock(IL10N::class); - $l10n->method('t') - ->willReturnCallback(function ($text, $parameters = []) { - return vsprintf($text, $parameters); - }); - $l10nFactory = $this->createMock(IFactory::class); - $l10nFactory->method('get')->willReturn($l10n); - - $urlGenerator = $this->createMock(IURLGenerator::class); - - $this->queryBuilder = $this->createMock(IQueryBuilder::class); - $db = $this->createMock(IDBConnection::class); - $db->method('getQueryBuilder') - ->with() - ->willReturn($this->queryBuilder); - - $random = $this->createMock(ISecureRandom::class); - $random->method('generate') - ->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') - ->willReturn('random_token'); - - $defaults = $this->createMock(Defaults::class); - $defaults->method('getName') + $this->defaults = $this->createMock(Defaults::class); + $this->defaults->method('getName') ->willReturn('Instance Name 123'); - $this->plugin = new IMipPlugin($this->config, $this->mailer, $logger, $this->timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, $this->userManager, 'user123'); + $this->service = $this->createMock(IMipService::class); + + $this->eventComparisonService = $this->createMock(EventComparisonService::class); + + $this->plugin = new IMipPlugin( + $this->config, + $this->mailer, + $this->logger, + $this->timeFactory, + $this->defaults, + $this->userManager, + 'user123', + $this->service, + $this->eventComparisonService + ); } - public function testDelivery(): void { - $this->config - ->expects($this->any()) - ->method('getAppValue') - ->willReturnMap([ - ['dav', 'invitation_link_recipients', 'yes', 'yes'], - ]); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $message = $this->_testMessage(); - $this->_expectSend(); + public function testDeliveryNoSignificantChange(): void { + $message = new Message(); + $message->method = 'REQUEST'; + $message->message = new VCalendar(); + $message->message->add('VEVENT', array_merge([ + 'UID' => 'uid-1234', + 'SEQUENCE' => 0, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $message->message->VEVENT->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $message->message->VEVENT->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE']); + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->senderName = 'Mr. Wizard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + $message->significantChange = false; $this->plugin->schedule($message); - $this->assertEquals('1.1', $message->getScheduleStatus()); + $this->assertEquals('1.0', $message->getScheduleStatus()); } - public function testFailedDelivery(): void { - $this->config - ->expects($this->any()) + public function testParsingSingle(): void { + $message = new Message(); + $message->method = 'REQUEST'; + $newVCalendar = new VCalendar(); + $newVevent = new VEvent($newVCalendar, 'one', array_merge([ + 'UID' => 'uid-1234', + 'SEQUENCE' => 1, + 'SUMMARY' => 'Fellowship meeting without (!) Boromir', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $message->message = $newVCalendar; + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->senderName = 'Mr. Wizard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + // save the old copy in the plugin + $oldVCalendar = new VCalendar(); + $oldVEvent = new VEvent($oldVCalendar, 'one', [ + 'UID' => 'uid-1234', + 'SEQUENCE' => 0, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => 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']); + $oldVEvent->add('ATTENDEE', 'mailto:' . 'boromir@tra.it.or', ['RSVP' => 'TRUE']); + $oldVCalendar->add($oldVEvent); + $data = ['invitee_name' => 'Mr. Wizard', + 'meeting_title' => 'Fellowship meeting without (!) Boromir', + 'attendee_name' => 'frodo@hobb.it' + ]; + $this->plugin->setVCalendar($oldVCalendar); + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(true); + $this->eventComparisonService->expects(self::once()) + ->method('findModified') + ->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]); + $this->service->expects(self::once()) + ->method('buildBodyData') + ->with($newVevent, $oldVEvent) + ->willReturn($data); + $this->userManager->expects(self::never()) + ->method('getDisplayName'); + $this->service->expects(self::once()) + ->method('getFrom'); + $this->service->expects(self::once()) + ->method('addSubjectAndHeading') + ->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting without (!) Boromir'); + $this->service->expects(self::once()) + ->method('addBulletList') + ->with($this->emailTemplate, $newVevent, $data); + $this->service->expects(self::once()) + ->method('getAttendeeRsvpOrReqForParticipant') + ->willReturn(true); + $this->config->expects(self::once()) ->method('getAppValue') - ->willReturnMap([ - ['dav', 'invitation_link_recipients', 'yes', 'yes'], - ]); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $message = $this->_testMessage(); - $this->mailer + ->with('dav', 'invitation_link_recipients', 'yes') + ->willReturn('yes'); + $this->service->expects(self::once()) + ->method('createInvitationToken') + ->with($message,$newVevent, '1496912700') + ->willReturn('token'); + $this->service->expects(self::once()) + ->method('addResponseButtons') + ->with($this->emailTemplate, 'token'); + $this->service->expects(self::once()) + ->method('addMoreOptionsButton') + ->with($this->emailTemplate, 'token'); + $this->mailer->expects(self::once()) ->method('send') - ->willThrowException(new \Exception()); - $this->_expectSend(); - $this->plugin->schedule($message); - $this->assertEquals('5.0', $message->getScheduleStatus()); - } - - public function testInvalidEmailDelivery(): void { - $this->mailer->method('validateMailAddress')->willReturn(false); - - $message = $this->_testMessage(); + ->willReturn([]); $this->plugin->schedule($message); - $this->assertEquals('5.0', $message->getScheduleStatus()); + $this->assertEquals('1.1', $message->getScheduleStatus()); } - public function testDeliveryWithNoCommonName(): void { - $this->config - ->expects($this->any()) - ->method('getAppValue') - ->willReturnMap([ - ['dav', 'invitation_link_recipients', 'yes', 'yes'], - ]); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $message = $this->_testMessage(); - $message->senderName = null; - - $this->userManager->expects($this->once()) + public function testParsingRecurrence(): void { + $message = new Message(); + $message->method = 'REQUEST'; + $newVCalendar = new VCalendar(); + $newVevent = new VEvent($newVCalendar, 'one', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z' + ]); + $newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $newvEvent2 = new VEvent($newVCalendar, 'two', [ + 'UID' => 'uid-1234', + 'SEQUENCE' => 1, + 'SUMMARY' => 'Elevenses', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RECURRENCE-ID' => new \DateTime('2016-01-01 00:00:00') + ]); + $newvEvent2->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newvEvent2->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $message->message = $newVCalendar; + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + // save the old copy in the plugin + $oldVCalendar = new VCalendar(); + $oldVEvent = new VEvent($oldVCalendar, 'one', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z' + ]); + $oldVEvent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $oldVEvent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $data = ['invitee_name' => 'Mr. Wizard', + 'meeting_title' => 'Elevenses', + 'attendee_name' => 'frodo@hobb.it' + ]; + $this->plugin->setVCalendar($oldVCalendar); + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(true); + $this->eventComparisonService->expects(self::once()) + ->method('findModified') + ->willReturn(['old' => [] ,'new' => [$newVevent]]); + $this->service->expects(self::once()) + ->method('buildBodyData') + ->with($newVevent, null) + ->willReturn($data); + $this->userManager->expects(self::once()) ->method('getDisplayName') - ->with('user123') ->willReturn('Mr. Wizard'); - - $this->_expectSend(); + $this->service->expects(self::once()) + ->method('getFrom'); + $this->service->expects(self::once()) + ->method('addSubjectAndHeading') + ->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Elevenses'); + $this->service->expects(self::once()) + ->method('addBulletList') + ->with($this->emailTemplate, $newVevent, $data); + $this->service->expects(self::once()) + ->method('getAttendeeRsvpOrReqForParticipant') + ->willReturn(true); + $this->config->expects(self::once()) + ->method('getAppValue') + ->with('dav', 'invitation_link_recipients', 'yes') + ->willReturn('yes'); + $this->service->expects(self::once()) + ->method('createInvitationToken') + ->with($message, $newVevent, '1496912700') + ->willReturn('token'); + $this->service->expects(self::once()) + ->method('addResponseButtons') + ->with($this->emailTemplate, 'token'); + $this->service->expects(self::once()) + ->method('addMoreOptionsButton') + ->with($this->emailTemplate, 'token'); + $this->mailer->expects(self::once()) + ->method('send') + ->willReturn([]); $this->plugin->schedule($message); $this->assertEquals('1.1', $message->getScheduleStatus()); } - /** - * @dataProvider dataNoMessageSendForPastEvents - */ - public function testNoMessageSendForPastEvents(array $veventParams, bool $expectsMail): void { - $this->config - ->method('getAppValue') - ->willReturn('yes'); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $message = $this->_testMessage($veventParams); + public function testEmailValidationFailed() { + $message = new Message(); + $message->method = 'REQUEST'; + $message->message = new VCalendar(); + $message->message->add('VEVENT', array_merge([ + 'UID' => 'uid-1234', + 'SEQUENCE' => 0, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $message->message->VEVENT->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $message->message->VEVENT->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE']); + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->senderName = 'Mr. Wizard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; - $this->_expectSend('frodo@hobb.it', $expectsMail, $expectsMail); + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(false); $this->plugin->schedule($message); - - if ($expectsMail) { - $this->assertEquals('1.1', $message->getScheduleStatus()); - } else { - $this->assertEquals(false, $message->getScheduleStatus()); - } + $this->assertEquals('5.0', $message->getScheduleStatus()); } - public function dataNoMessageSendForPastEvents() { - return [ - [['DTSTART' => new \DateTime('2017-01-01 00:00:00')], false], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00')], false], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-12-31 00:00:00')], true], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DURATION' => 'P1D'], false], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DURATION' => 'P52W'], true], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY'], true], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;COUNT=3'], false], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;UNTIL=20170301T000000Z'], false], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;COUNT=33'], true], - [['DTSTART' => new \DateTime('2017-01-01 00:00:00'), 'DTEND' => new \DateTime('2017-01-01 00:00:00'), 'RRULE' => 'FREQ=WEEKLY;UNTIL=20171001T000000Z'], true], + public function testFailedDelivery(): void { + $message = new Message(); + $message->method = 'REQUEST'; + $newVcalendar = new VCalendar(); + $newVevent = new VEvent($newVcalendar, 'one', array_merge([ + 'UID' => 'uid-1234', + 'SEQUENCE' => 1, + 'SUMMARY' => 'Fellowship meeting without (!) Boromir', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $message->message = $newVcalendar; + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->senderName = 'Mr. Wizard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + // save the old copy in the plugin + $oldVcalendar = new VCalendar(); + $oldVevent = new VEvent($oldVcalendar, 'one', [ + 'UID' => 'uid-1234', + 'SEQUENCE' => 0, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => 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']); + $oldVevent->add('ATTENDEE', 'mailto:' . 'boromir@tra.it.or', ['RSVP' => 'TRUE']); + $oldVcalendar->add($oldVevent); + $data = ['invitee_name' => 'Mr. Wizard', + 'meeting_title' => 'Fellowship meeting without (!) Boromir', + 'attendee_name' => 'frodo@hobb.it' ]; - } - - /** - * @dataProvider dataIncludeResponseButtons - */ - public function testIncludeResponseButtons(string $config_setting, string $recipient, bool $has_buttons): void { - $message = $this->_testMessage([], $recipient); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $this->_expectSend($recipient, true, $has_buttons); - $this->config - ->expects($this->any()) + $this->plugin->setVCalendar($oldVcalendar); + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(true); + $this->eventComparisonService->expects(self::once()) + ->method('findModified') + ->willReturn(['old' => [] ,'new' => [$newVevent]]); + $this->service->expects(self::once()) + ->method('buildBodyData') + ->with($newVevent, null) + ->willReturn($data); + $this->userManager->expects(self::never()) + ->method('getDisplayName'); + $this->service->expects(self::once()) + ->method('getFrom'); + $this->service->expects(self::once()) + ->method('addSubjectAndHeading') + ->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting without (!) Boromir'); + $this->service->expects(self::once()) + ->method('addBulletList') + ->with($this->emailTemplate, $newVevent, $data); + $this->service->expects(self::once()) + ->method('getAttendeeRsvpOrReqForParticipant') + ->willReturn(true); + $this->config->expects(self::once()) ->method('getAppValue') - ->willReturnMap([ - ['dav', 'invitation_link_recipients', 'yes', $config_setting], - ]); - + ->with('dav', 'invitation_link_recipients', 'yes') + ->willReturn('yes'); + $this->service->expects(self::once()) + ->method('createInvitationToken') + ->with($message, $newVevent, '1496912700') + ->willReturn('token'); + $this->service->expects(self::once()) + ->method('addResponseButtons') + ->with($this->emailTemplate, 'token'); + $this->service->expects(self::once()) + ->method('addMoreOptionsButton') + ->with($this->emailTemplate, 'token'); + $this->mailer->expects(self::once()) + ->method('send') + ->willReturn([]); + $this->mailer + ->method('send') + ->willThrowException(new \Exception()); + $this->logger->expects(self::once()) + ->method('error'); $this->plugin->schedule($message); - $this->assertEquals('1.1', $message->getScheduleStatus()); + $this->assertEquals('5.0', $message->getScheduleStatus()); } - public function dataIncludeResponseButtons() { - return [ - // dav.invitation_link_recipients, recipient, $has_buttons - [ 'yes', 'joe@internal.com', true], - [ 'joe@internal.com', 'joe@internal.com', true], - [ 'internal.com', 'joe@internal.com', true], - [ 'pete@otherinternal.com,internal.com', 'joe@internal.com', true], - [ 'no', 'joe@internal.com', false], - [ 'internal.com', 'joe@external.com', false], - [ 'jane@otherinternal.com,internal.com', 'joe@otherinternal.com', false], + public function testNoOldEvent(): void { + $message = new Message(); + $message->method = 'REQUEST'; + $newVCalendar = new VCalendar(); + $newVevent = new VEvent($newVCalendar, 'VEVENT', array_merge([ + 'UID' => 'uid-1234', + 'SEQUENCE' => 1, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $message->message = $newVCalendar; + $message->sender = 'mailto:gandalf@wiz.ard'; + $message->senderName = 'Mr. Wizard'; + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + $data = ['invitee_name' => 'Mr. Wizard', + 'meeting_title' => 'Fellowship meeting', + 'attendee_name' => 'frodo@hobb.it' ]; - } - public function testMessageSendWhenEventWithoutName(): void { - $this->config + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(true); + $this->eventComparisonService->expects(self::once()) + ->method('findModified') + ->with($newVCalendar, null) + ->willReturn(['old' => [] ,'new' => [$newVevent]]); + $this->service->expects(self::once()) + ->method('buildBodyData') + ->with($newVevent, null) + ->willReturn($data); + $this->userManager->expects(self::never()) + ->method('getDisplayName'); + $this->service->expects(self::once()) + ->method('getFrom'); + $this->service->expects(self::once()) + ->method('addSubjectAndHeading') + ->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting'); + $this->service->expects(self::once()) + ->method('addBulletList') + ->with($this->emailTemplate, $newVevent, $data); + $this->service->expects(self::once()) + ->method('getAttendeeRsvpOrReqForParticipant') + ->willReturn(true); + $this->config->expects(self::once()) ->method('getAppValue') + ->with('dav', 'invitation_link_recipients', 'yes') ->willReturn('yes'); - $this->mailer->method('validateMailAddress')->willReturn(true); - - $message = $this->_testMessage(['SUMMARY' => '']); - $this->_expectSend('frodo@hobb.it', true, true, 'Invitation: Untitled event'); - $this->emailTemplate->expects($this->once()) - ->method('addHeading') - ->with('Invitation'); + $this->service->expects(self::once()) + ->method('createInvitationToken') + ->with($message, $newVevent, '1496912700') + ->willReturn('token'); + $this->service->expects(self::once()) + ->method('addResponseButtons') + ->with($this->emailTemplate, 'token'); + $this->service->expects(self::once()) + ->method('addMoreOptionsButton') + ->with($this->emailTemplate, 'token'); + $this->mailer->expects(self::once()) + ->method('send') + ->willReturn([]); + $this->mailer + ->method('send') + ->willReturn([]); $this->plugin->schedule($message); $this->assertEquals('1.1', $message->getScheduleStatus()); } - private function _testMessage(array $attrs = [], string $recipient = 'frodo@hobb.it') { + public function testNoButtons(): void { $message = new Message(); $message->method = 'REQUEST'; - $message->message = new VCalendar(); - $message->message->add('VEVENT', array_merge([ + $newVCalendar = new VCalendar(); + $newVevent = new VEvent($newVCalendar, 'VEVENT', array_merge([ 'UID' => 'uid-1234', - 'SEQUENCE' => 0, + 'SEQUENCE' => 1, 'SUMMARY' => 'Fellowship meeting', - 'DTSTART' => new \DateTime('2018-01-01 00:00:00') - ], $attrs)); - $message->message->VEVENT->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); - $message->message->VEVENT->add('ATTENDEE', 'mailto:'.$recipient, [ 'RSVP' => 'TRUE' ]); + 'DTSTART' => new \DateTime('2016-01-01 00:00:00') + ], [])); + $newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard'); + $newVevent->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']); + $message->message = $newVCalendar; $message->sender = 'mailto:gandalf@wiz.ard'; - $message->senderName = 'Mr. Wizard'; - $message->recipient = 'mailto:'.$recipient; - return $message; - } - + $message->recipient = 'mailto:' . 'frodo@hobb.it'; + $data = ['invitee_name' => 'Mr. Wizard', + 'meeting_title' => 'Fellowship meeting', + 'attendee_name' => 'frodo@hobb.it' + ]; - private function _expectSend(string $recipient = 'frodo@hobb.it', bool $expectSend = true, bool $expectButtons = true, string $subject = 'Invitation: Fellowship meeting'): void { - // if the event is in the past, we skip out - if (!$expectSend) { - $this->mailer - ->expects($this->never()) - ->method('send'); - return; - } - - $this->emailTemplate->expects($this->once()) - ->method('setSubject') - ->with($subject); - $this->mailMessage->expects($this->once()) - ->method('setTo') - ->with([$recipient => null]); - $this->mailMessage->expects($this->once()) - ->method('setReplyTo') - ->with(['gandalf@wiz.ard' => 'Mr. Wizard']); - $this->mailMessage->expects($this->once()) - ->method('setFrom') - ->with(['invitations-noreply@localhost' => 'Mr. Wizard via Instance Name 123']); + $this->service->expects(self::once()) + ->method('getLastOccurrence') + ->willReturn('1496912700'); + $this->mailer->expects(self::once()) + ->method('validateMailAddress') + ->with('frodo@hobb.it') + ->willReturn(true); + $this->eventComparisonService->expects(self::once()) + ->method('findModified') + ->with($newVCalendar, null) + ->willReturn(['old' => [] ,'new' => [$newVevent]]); + $this->service->expects(self::once()) + ->method('buildBodyData') + ->with($newVevent, null) + ->willReturn($data); + $this->userManager->expects(self::once()) + ->method('getDisplayName') + ->willReturn('Mr. Wizard'); + $this->service->expects(self::once()) + ->method('getFrom'); + $this->service->expects(self::once()) + ->method('addSubjectAndHeading') + ->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting'); + $this->service->expects(self::once()) + ->method('addBulletList') + ->with($this->emailTemplate, $newVevent, $data); + $this->service->expects(self::once()) + ->method('getAttendeeRsvpOrReqForParticipant') + ->willReturn(true); + $this->config->expects(self::once()) + ->method('getAppValue') + ->with('dav', 'invitation_link_recipients', 'yes') + ->willReturn('no'); + $this->service->expects(self::never()) + ->method('createInvitationToken'); + $this->service->expects(self::never()) + ->method('addResponseButtons'); + $this->service->expects(self::never()) + ->method('addMoreOptionsButton'); + $this->mailer->expects(self::once()) + ->method('send') + ->willReturn([]); $this->mailer - ->expects($this->once()) - ->method('send'); - - if ($expectButtons) { - $this->queryBuilder->expects($this->once()) - ->method('insert') - ->with('calendar_invitations') - ->willReturn($this->queryBuilder); - $this->queryBuilder->expects($this->once()) - ->method('values') - ->willReturn($this->queryBuilder); - $this->queryBuilder->expects($this->once()) - ->method('execute'); - } else { - $this->queryBuilder->expects($this->never()) - ->method('insert') - ->with('calendar_invitations'); - } + ->method('send') + ->willReturn([]); + $this->plugin->schedule($message); + $this->assertEquals('1.1', $message->getScheduleStatus()); } } diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php new file mode 100644 index 00000000000..000476050c7 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php @@ -0,0 +1,284 @@ +<?php +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @copyright Copyright (c) 2017, Georg Ehrke + * + * @author brad2014 <brad2014@users.noreply.github.com> + * @author Brad Rubenstein <brad@wbr.tech> + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Georg Ehrke <oc.list@georgehrke.com> + * @author Joas Schilling <coding@schilljs.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Citharel <nextcloud@tcit.fr> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV\Schedule; + +use OC\L10N\L10N; +use OC\L10N\LazyL10N; +use OC\URLGenerator; +use OCA\DAV\CalDAV\Schedule\IMipService; +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\Property\ICalendar\DateTime; +use Test\TestCase; + +class IMipServiceTest extends TestCase +{ + /** @var URLGenerator|MockObject */ + private $urlGenerator; + + /** @var IConfig|MockObject */ + private $config; + + /** @var IDBConnection|MockObject */ + private $db; + + /** @var ISecureRandom|MockObject */ + private $random; + + /** @var L10NFactory|MockObject */ + private $l10nFactory; + + /** @var L10N|MockObject */ + private $l10n; + + /** @var IMipService */ + private $service; + + protected function setUp(): void + { + $this->urlGenerator = $this->createMock(URLGenerator::class); + $this->config = $this->createMock(IConfig::class); + $this->db = $this->createMock(IDBConnection::class); + $this->random = $this->createMock(ISecureRandom::class); + $this->l10nFactory = $this->createMock(L10NFactory::class); + $this->l10n = $this->createMock(LazyL10N::class); + $this->l10nFactory->expects(self::once()) + ->method('findGenericLanguage') + ->willReturn('en'); + $this->l10nFactory->expects(self::once()) + ->method('get') + ->with('dav', 'en') + ->willReturn($this->l10n); + $this->service = new IMipService( + $this->urlGenerator, + $this->config, + $this->db, + $this->random, + $this->l10nFactory + ); + } + + public function testGetFrom(): void + { + $senderName = "Detective McQueen"; + $default = "Twin Lakes Police Department - Darkside Division"; + $expected = "Detective McQueen via Twin Lakes Police Department - Darkside Division"; + + $this->l10n->expects(self::once()) + ->method('t') + ->willReturn($expected); + + $actual = $this->service->getFrom($senderName, $default); + $this->assertEquals($expected, $actual); + } + + 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') + ]); + + $expected = [ + 'meeting_when' => $this->service->generateWhenString($newVevent), + 'meeting_description' => '', + 'meeting_title' => 'Second Breakfast', + 'meeting_location' => '', + 'meeting_url' => '', + 'meeting_url_html' => '', + ]; + + $actual = $this->service->buildBodyData($newVevent, $oldVevent); + + $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') + ]); + + $expected = [ + 'meeting_when' => $this->service->generateWhenString($newVevent), + 'meeting_description' => '', + 'meeting_title' => 'Second Breakfast', + '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_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); + $this->assertEquals($expected, $actual); + } + + public function testGetLastOccurrenceRRULE(): void + { + $vCalendar = new VCalendar(); + $vCalendar->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'RRULE' => 'FREQ=DAILY;INTERVAL=1;UNTIL=20160201T000000Z', + ]); + + $occurrence = $this->service->getLastOccurrence($vCalendar); + $this->assertEquals(1454284800, $occurrence); + } + + public function testGetLastOccurrenceEndDate(): void + { + $vCalendar = new VCalendar(); + $vCalendar->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'DTEND' => new \DateTime('2017-01-01 00:00:00'), + ]); + + $occurrence = $this->service->getLastOccurrence($vCalendar); + $this->assertEquals(1483228800, $occurrence); + } + + public function testGetLastOccurrenceDuration(): void + { + $vCalendar = new VCalendar(); + $vCalendar->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + 'DURATION' => 'P12W', + ]); + + $occurrence = $this->service->getLastOccurrence($vCalendar); + $this->assertEquals(1458864000, $occurrence); + } + + public function testGetLastOccurrenceAllDay(): void + { + $vCalendar = new VCalendar(); + $vEvent = $vCalendar->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + + // rewrite from DateTime to Date + $vEvent->DTSTART['VALUE'] = 'DATE'; + + $occurrence = $this->service->getLastOccurrence($vCalendar); + $this->assertEquals(1451692800, $occurrence); + } + + public function testGetLastOccurrenceFallback(): void + { + $vCalendar = new VCalendar(); + $vCalendar->add('VEVENT', [ + 'UID' => 'uid-1234', + 'LAST-MODIFIED' => 123456, + 'SEQUENCE' => 2, + 'SUMMARY' => 'Fellowship meeting', + 'DTSTART' => new \DateTime('2016-01-01 00:00:00'), + ]); + + $occurrence = $this->service->getLastOccurrence($vCalendar); + $this->assertEquals(1451606400, $occurrence); + } +} |