diff options
Diffstat (limited to 'tests/lib/Calendar')
-rw-r--r-- | tests/lib/Calendar/CalendarEventBuilderTest.php | 151 | ||||
-rw-r--r-- | tests/lib/Calendar/ManagerTest.php | 1674 | ||||
-rw-r--r-- | tests/lib/Calendar/Resource/ManagerTest.php | 36 | ||||
-rw-r--r-- | tests/lib/Calendar/ResourcesRoomsUpdaterTest.php | 439 | ||||
-rw-r--r-- | tests/lib/Calendar/Room/ManagerTest.php | 36 |
5 files changed, 2259 insertions, 77 deletions
diff --git a/tests/lib/Calendar/CalendarEventBuilderTest.php b/tests/lib/Calendar/CalendarEventBuilderTest.php new file mode 100644 index 00000000000..b01c209cd31 --- /dev/null +++ b/tests/lib/Calendar/CalendarEventBuilderTest.php @@ -0,0 +1,151 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Calendar; + +use DateTimeImmutable; +use InvalidArgumentException; +use OC\Calendar\CalendarEventBuilder; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Calendar\CalendarEventStatus; +use OCP\Calendar\ICreateFromString; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class CalendarEventBuilderTest extends TestCase { + private CalendarEventBuilder $calendarEventBuilder; + private ITimeFactory&MockObject $timeFactory; + + protected function setUp(): void { + parent::setUp(); + + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->timeFactory->method('now') + ->willReturn(new DateTimeImmutable('20250105T000000Z')); + + $this->calendarEventBuilder = new CalendarEventBuilder( + 'event-uid-123', + $this->timeFactory, + ); + } + + public function testToIcs(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('mailto:organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics'); + $actual = $this->calendarEventBuilder->toIcs(); + $this->assertEquals($expected, $actual); + } + + public function testToIcsWithoutOrganizerAndAttendees(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + + $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-without-attendees.ics'); + $actual = $this->calendarEventBuilder->toIcs(); + $this->assertEquals($expected, $actual); + } + + public function testToIcsWithoutMailtoPrefix(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee2@domain.tld'); + + $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics'); + $actual = $this->calendarEventBuilder->toIcs(); + $this->assertEquals($expected, $actual); + } + + public function testCreateInCalendar(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $expectedIcs = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics'); + $calendar = $this->createMock(ICreateFromString::class); + $calendar->expects(self::once()) + ->method('createFromString') + ->with('event-uid-123.ics', $expectedIcs); + + $actual = $this->calendarEventBuilder->createInCalendar($calendar); + $this->assertEquals('event-uid-123.ics', $actual); + } + + public function testToIcsWithoutStartDate(): void { + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/start date/i'); + $this->calendarEventBuilder->toIcs(); + } + + public function testToIcsWithoutEndDate(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/end date/i'); + $this->calendarEventBuilder->toIcs(); + } + + public function testToIcsWithoutSummary(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->setOrganizer('organizer@domain.tld'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/summary/i'); + $this->calendarEventBuilder->toIcs(); + } + + public function testToIcsWithoutOrganizerWithAttendees(): void { + $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z')); + $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z')); + $this->calendarEventBuilder->setSummary('My event'); + $this->calendarEventBuilder->setDescription('Foo bar baz'); + $this->calendarEventBuilder->addAttendee('attendee1@domain.tld'); + $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/organizer/i'); + $this->calendarEventBuilder->toIcs(); + } +} diff --git a/tests/lib/Calendar/ManagerTest.php b/tests/lib/Calendar/ManagerTest.php index a4d9d45fdb2..eeaca0dfeb6 100644 --- a/tests/lib/Calendar/ManagerTest.php +++ b/tests/lib/Calendar/ManagerTest.php @@ -1,68 +1,151 @@ <?php + /** - * @copyright 2017, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Calendar; +use DateTimeImmutable; use OC\AppFramework\Bootstrap\Coordinator; +use OC\Calendar\AvailabilityResult; use OC\Calendar\Manager; +use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin; +use OCA\DAV\Connector\Sabre\Server; +use OCA\DAV\ServerFactory; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Calendar\ICalendar; +use OCP\Calendar\ICalendarIsShared; +use OCP\Calendar\ICalendarIsWritable; +use OCP\Calendar\ICreateFromString; +use OCP\Calendar\IHandleImipMessage; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; +use Sabre\VObject\Component\VCalendar; use Test\TestCase; -class ManagerTest extends TestCase { +/* + * This allows us to create Mock object supporting both interfaces + */ +interface ITestCalendar extends ICreateFromString, IHandleImipMessage, ICalendarIsShared, ICalendarIsWritable { +} - /** @var Coordinator|MockObject */ +class ManagerTest extends TestCase { + /** @var Coordinator&MockObject */ private $coordinator; - /** @var MockObject|ContainerInterface */ + /** @var ContainerInterface&MockObject */ private $container; - /** @var MockObject|LoggerInterface */ + /** @var LoggerInterface&MockObject */ private $logger; /** @var Manager */ private $manager; + /** @var ITimeFactory&MockObject */ + private $time; + + /** @var ISecureRandom&MockObject */ + private ISecureRandom $secureRandom; + + private IUserManager&MockObject $userManager; + private ServerFactory&MockObject $serverFactory; + + private VCalendar $vCalendar1a; + private VCalendar $vCalendar2a; + private VCalendar $vCalendar3a; + protected function setUp(): void { parent::setUp(); $this->coordinator = $this->createMock(Coordinator::class); $this->container = $this->createMock(ContainerInterface::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->time = $this->createMock(ITimeFactory::class); + $this->secureRandom = $this->createMock(ISecureRandom::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->serverFactory = $this->createMock(ServerFactory::class); $this->manager = new Manager( $this->coordinator, $this->container, - $this->logger + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, ); + + // construct calendar with a 1 hour event and same start/end time zones + $this->vCalendar1a = new VCalendar(); + /** @var VEvent $vEvent */ + $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 Event'); + $vEvent->add('SEQUENCE', 3); + $vEvent->add('STATUS', 'CONFIRMED'); + $vEvent->add('TRANSP', 'OPAQUE'); + $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 event for reply + $this->vCalendar2a = new VCalendar(); + /** @var VEvent $vEvent */ + $vEvent = $this->vCalendar2a->add('VEVENT', []); + $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff'); + $vEvent->add('DTSTART', '20210820'); + $vEvent->add('DTEND', '20220821'); + $vEvent->add('SUMMARY', 'berry basket'); + $vEvent->add('SEQUENCE', 3); + $vEvent->add('STATUS', 'CONFIRMED'); + $vEvent->add('TRANSP', 'OPAQUE'); + $vEvent->add('ORGANIZER', 'mailto:linus@stardew-tent-living.com', ['CN' => 'admin']); + $vEvent->add('ATTENDEE', 'mailto:pierre@general-store.com', [ + 'CN' => 'pierre@general-store.com', + 'CUTYPE' => 'INDIVIDUAL', + 'ROLE' => 'REQ-PARTICIPANT', + 'PARTSTAT' => 'ACCEPTED', + ]); + + // construct calendar with a event for reply + $this->vCalendar3a = new VCalendar(); + /** @var VEvent $vEvent */ + $vEvent = $this->vCalendar3a->add('VEVENT', []); + $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff'); + $vEvent->add('DTSTART', '20210820'); + $vEvent->add('DTEND', '20220821'); + $vEvent->add('SUMMARY', 'berry basket'); + $vEvent->add('SEQUENCE', 3); + $vEvent->add('STATUS', 'CANCELLED'); + $vEvent->add('TRANSP', 'OPAQUE'); + $vEvent->add('ORGANIZER', 'mailto:linus@stardew-tent-living.com', ['CN' => 'admin']); + $vEvent->add('ATTENDEE', 'mailto:pierre@general-store.com', [ + 'CN' => 'pierre@general-store.com', + 'CUTYPE' => 'INDIVIDUAL', + 'ROLE' => 'REQ-PARTICIPANT', + 'PARTSTAT' => 'ACCEPTED', + ]); + } - /** - * @dataProvider searchProvider - */ - public function testSearch($search1, $search2, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')] + public function testSearch($search1, $search2, $expected): void { /** @var ICalendar | MockObject $calendar1 */ $calendar1 = $this->createMock(ICalendar::class); $calendar1->method('getKey')->willReturn('simple:1'); @@ -86,10 +169,8 @@ class ManagerTest extends TestCase { $this->assertEquals($expected, $result); } - /** - * @dataProvider searchProvider - */ - public function testSearchOptions($search1, $search2, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')] + public function testSearchOptions($search1, $search2, $expected): void { /** @var ICalendar | MockObject $calendar1 */ $calendar1 = $this->createMock(ICalendar::class); $calendar1->method('getKey')->willReturn('simple:1'); @@ -116,7 +197,7 @@ class ManagerTest extends TestCase { $this->assertEquals($expected, $result); } - public function searchProvider() { + public static function searchProvider(): array { $search1 = [ [ 'id' => 1, @@ -170,7 +251,7 @@ class ManagerTest extends TestCase { ]; } - public function testRegisterUnregister() { + public function testRegisterUnregister(): void { /** @var ICalendar | MockObject $calendar1 */ $calendar1 = $this->createMock(ICalendar::class); $calendar1->method('getKey')->willReturn('key1'); @@ -194,7 +275,7 @@ class ManagerTest extends TestCase { $this->assertContains($calendar2, $result); } - public function testGetCalendars() { + public function testGetCalendars(): void { /** @var ICalendar | MockObject $calendar1 */ $calendar1 = $this->createMock(ICalendar::class); $calendar1->method('getKey')->willReturn('key1'); @@ -218,12 +299,12 @@ class ManagerTest extends TestCase { $this->assertCount(0, $result); } - public function testEnabledIfNot() { + public function testEnabledIfNot(): void { $isEnabled = $this->manager->isEnabled(); $this->assertFalse($isEnabled); } - public function testIfEnabledIfSo() { + public function testIfEnabledIfSo(): void { /** @var ICalendar | MockObject $calendar */ $calendar = $this->createMock(ICalendar::class); $this->manager->registerCalendar($calendar); @@ -231,4 +312,1523 @@ class ManagerTest extends TestCase { $isEnabled = $this->manager->isEnabled(); $this->assertTrue($isEnabled); } + + public function testHandleImipRequestWithNoCalendars(): void { + // construct calendar manager returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message could not be processed because user has no calendars'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithInvalidData(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('error') + ->with('iMip message could not be processed because an error occurred while parsing the iMip message'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, 'Invalid data'); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithInvalidMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'CANCEL'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoEvent(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains no event'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->remove('VEVENT'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoUid(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains a UID'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->VEVENT->remove('UID'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoOrganizer(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains an organizer'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->VEVENT->remove('ORGANIZER'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoAttendee(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains any attendees'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->VEVENT->remove('ATTENDEE'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithInvalidAttendee(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event does not contain a attendee that matches the recipient'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee2@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequestWithNoMatch(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + $userCalendar->expects(self::once()) + ->method('isDeleted') + ->willReturn(false); + $userCalendar->expects(self::once()) + ->method('isWritable') + ->willReturn(true); + $userCalendar->expects(self::once()) + ->method('isShared') + ->willReturn(false); + $userCalendar->expects(self::once()) + ->method('search') + ->willReturn([]); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because no corresponding event was found in any calendar'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipRequest(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + $userCalendar->expects(self::once()) + ->method('isDeleted') + ->willReturn(false); + $userCalendar->expects(self::once()) + ->method('isWritable') + ->willReturn(true); + $userCalendar->expects(self::once()) + ->method('isShared') + ->willReturn(false); + $userCalendar->expects(self::once()) + ->method('search') + ->willReturn([['uri' => 'principals/user/attendee1/personal']]); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + // construct user calendar returns + $userCalendar->expects(self::once()) + ->method('handleIMipMessage') + ->with('', $calendar->serialize()); + // Act + $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertTrue($result); + } + + public function testHandleImipReplyWithNoCalendars(): void { + // construct calendar manager returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message could not be processed because user has no calendars'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'REPLY'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithInvalidData(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('error') + ->with('iMip message could not be processed because an error occurred while parsing the iMip message'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, 'Invalid data'); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithNoMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithInvalidMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'UNKNOWN'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithNoEvent(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains no event'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'REPLY'); + $calendar->remove('VEVENT'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithNoUid(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains a UID'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'REPLY'); + $calendar->VEVENT->remove('UID'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithNoOrganizer(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains an organizer'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'REPLY'); + $calendar->VEVENT->remove('ORGANIZER'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyWithNoAttendee(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains any attendees'); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendar = $this->vCalendar2a; + $calendar->add('METHOD', 'REPLY'); + $calendar->VEVENT->remove('ATTENDEE'); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyDateInThePast(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger and time returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because the event is in the past'); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(time()); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendarData = clone $this->vCalendar2a; + $calendarData->add('METHOD', 'REPLY'); + $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReplyEventNotFound(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + $userCalendar->expects(self::once()) + ->method('search') + ->willReturn([]); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct time returns + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(1628374233); + // construct parameters + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendarData = clone $this->vCalendar2a; + $calendarData->add('METHOD', 'REPLY'); + // construct logger return + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because no corresponding event was found in any calendar', ['principalUri' => $principalUri, 'eventUid' => $calendarData->VEVENT->UID->getValue()]); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipReply(): void { + /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods([ + 'getCalendarsForPrincipal' + ]) + ->getMock(); + $calendar = $this->createMock(ITestCalendar::class); + $principalUri = 'principals/user/linus'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $calendarData = clone $this->vCalendar2a; + $calendarData->add('METHOD', 'REPLY'); + + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(1628374233); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$calendar]); + $calendar->expects(self::once()) + ->method('search') + ->willReturn([['uri' => 'testname.ics']]); + $calendar->expects(self::once()) + ->method('handleIMipMessage') + ->with('testname.ics', $calendarData->serialize()); + // Act + $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize()); + // Assert + $this->assertTrue($result); + } + + public function testHandleImipCancelWithNoCalendars(): void { + // construct calendar manager returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message could not be processed because user has no calendars'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'CANCEL'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithInvalidData(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('error') + ->with('iMip message could not be processed because an error occurred while parsing the iMip message'); + // construct parameters + $principalUri = 'principals/user/attendee1'; + $sender = 'organizer@testing.com'; + $recipient = 'attendee1@testing.com'; + $replyTo = null; + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, 'Invalid data'); + // Assert + $this->assertFalse($result); + } + + + public function testHandleImipCancelWithNoMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithInvalidMethod(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains an incorrect or invalid method'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'UNKNOWN'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithNoEvent(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message contains no event'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'CANCEL'); + $calendar->remove('VEVENT'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithNoUid(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains a UID'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'CANCEL'); + $calendar->VEVENT->remove('UID'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithNoOrganizer(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains an organizer'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'CANCEL'); + $calendar->VEVENT->remove('ORGANIZER'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelWithNoAttendee(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event dose not contains any attendees'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'pierre@general-store.com'; + $recipient = 'linus@stardew-tent-living.com'; + $replyTo = null; + $calendar = $this->vCalendar3a; + $calendar->add('METHOD', 'CANCEL'); + $calendar->VEVENT->remove('ATTENDEE'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelAttendeeNotRecipient(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because recipient must be an ATTENDEE of this event'); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'leah@general-store.com'; + $replyTo = null; + $calendarData = clone $this->vCalendar3a; + $calendarData->add('METHOD', 'CANCEL'); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelDateInThePast(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct logger and time returns + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because the event is in the past'); + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(time()); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendarData = clone $this->vCalendar3a; + $calendarData->add('METHOD', 'CANCEL'); + $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelEventNotFound(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + $userCalendar->expects(self::once()) + ->method('search') + ->willReturn([]); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct time returns + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(1628374233); + // construct parameters + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendarData = clone $this->vCalendar3a; + $calendarData->add('METHOD', 'CANCEL'); + // construct logger return + $this->logger->expects(self::once())->method('warning') + ->with('iMip message event could not be processed because no corresponding event was found in any calendar', ['principalUri' => $principalUri, 'eventUid' => $calendarData->VEVENT->UID->getValue()]); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize()); + // Assert + $this->assertFalse($result); + } + + public function testHandleImipCancelOrganiserInReplyTo(): void { + /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods([ + 'getCalendarsForPrincipal' + ]) + ->getMock(); + + $principalUri = 'principals/user/pierre'; + $sender = 'clint@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = 'linus@stardew-tent-living.com'; + $calendar = $this->createMock(ITestCalendar::class); + $calendarData = clone $this->vCalendar3a; + $calendarData->add('METHOD', 'CANCEL'); + + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(1628374233); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->with($principalUri) + ->willReturn([$calendar]); + $calendar->expects(self::once()) + ->method('search') + ->willReturn([['uri' => 'testname.ics']]); + $calendar->expects(self::once()) + ->method('handleIMipMessage') + ->with('testname.ics', $calendarData->serialize()); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize()); + // Assert + $this->assertTrue($result); + } + + public function testHandleImipCancel(): void { + /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + ]) + ->onlyMethods([ + 'getCalendarsForPrincipal' + ]) + ->getMock(); + $principalUri = 'principals/user/pierre'; + $sender = 'linus@stardew-tent-living.com'; + $recipient = 'pierre@general-store.com'; + $replyTo = null; + $calendar = $this->createMock(ITestCalendar::class); + $calendarData = clone $this->vCalendar3a; + $calendarData->add('METHOD', 'CANCEL'); + + $this->time->expects(self::once()) + ->method('getTime') + ->willReturn(1628374233); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->with($principalUri) + ->willReturn([$calendar]); + $calendar->expects(self::once()) + ->method('search') + ->willReturn([['uri' => 'testname.ics']]); + $calendar->expects(self::once()) + ->method('handleIMipMessage') + ->with('testname.ics', $calendarData->serialize()); + // Act + $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize()); + // Assert + $this->assertTrue($result); + } + + private function getFreeBusyResponse(): string { + return <<<EOF +<?xml version="1.0" encoding="utf-8"?> +<cal:schedule-response xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns"> + <cal:response> + <cal:recipient> + <d:href>mailto:admin@imap.localhost</d:href> + </cal:recipient> + <cal:request-status>2.0;Success</cal:request-status> + <cal:calendar-data>BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject 4.5.6//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTART:20250116T060000Z +DTEND:20250117T060000Z +DTSTAMP:20250111T125634Z +FREEBUSY:20250116T060000Z/20250116T230000Z +FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20250116T230000Z/20250117T060000Z +ATTENDEE:mailto:admin@imap.localhost +UID:6099eab3-9bf1-4c7a-809e-4d46957cc372 +ORGANIZER;CN=admin:mailto:admin@imap.localhost +END:VFREEBUSY +END:VCALENDAR +</cal:calendar-data> + </cal:response> + <cal:response> + <cal:recipient> + <d:href>mailto:empty@imap.localhost</d:href> + </cal:recipient> + <cal:request-status>2.0;Success</cal:request-status> + <cal:calendar-data>BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject 4.5.6//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTART:20250116T060000Z +DTEND:20250117T060000Z +DTSTAMP:20250111T125634Z +ATTENDEE:mailto:empty@imap.localhost +UID:6099eab3-9bf1-4c7a-809e-4d46957cc372 +ORGANIZER;CN=admin:mailto:admin@imap.localhost +END:VFREEBUSY +END:VCALENDAR +</cal:calendar-data> + </cal:response> + <cal:response> + <cal:recipient> + <d:href>mailto:user@imap.localhost</d:href> + </cal:recipient> + <cal:request-status>2.0;Success</cal:request-status> + <cal:calendar-data>BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject 4.5.6//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTART:20250116T060000Z +DTEND:20250117T060000Z +DTSTAMP:20250111T125634Z +FREEBUSY:20250116T060000Z/20250116T230000Z +FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20250116T230000Z/20250117T060000Z +ATTENDEE:mailto:user@imap.localhost +UID:6099eab3-9bf1-4c7a-809e-4d46957cc372 +ORGANIZER;CN=admin:mailto:admin@imap.localhost +END:VFREEBUSY +END:VCALENDAR +</cal:calendar-data> + </cal:response> + <cal:response> + <cal:recipient> + <d:href>mailto:nouser@domain.tld</d:href> + </cal:recipient> + <cal:request-status>3.7;Could not find principal</cal:request-status> + </cal:response> +</cal:schedule-response> +EOF; + } + + public function testCheckAvailability(): void { + $organizer = $this->createMock(IUser::class); + $organizer->expects(self::once()) + ->method('getUID') + ->willReturn('admin'); + $organizer->expects(self::once()) + ->method('getEMailAddress') + ->willReturn('admin@imap.localhost'); + + $user1 = $this->createMock(IUser::class); + $user2 = $this->createMock(IUser::class); + + $this->userManager->expects(self::exactly(3)) + ->method('getByEmail') + ->willReturnMap([ + ['user@imap.localhost', [$user1]], + ['empty@imap.localhost', [$user2]], + ['nouser@domain.tld', []], + ]); + + $authPlugin = $this->createMock(CustomPrincipalPlugin::class); + $authPlugin->expects(self::once()) + ->method('setCurrentPrincipal') + ->with('principals/users/admin'); + + $server = $this->createMock(Server::class); + $server->expects(self::once()) + ->method('getPlugin') + ->with('auth') + ->willReturn($authPlugin); + $server->expects(self::once()) + ->method('invokeMethod') + ->willReturnCallback(function ( + RequestInterface $request, + ResponseInterface $response, + bool $sendResponse, + ): void { + $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics'); + $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('calendars/admin/outbox', $request->getPath()); + $this->assertEquals('text/calendar', $request->getHeader('Content-Type')); + $this->assertEquals('0', $request->getHeader('Depth')); + $this->assertEquals($requestBody, $request->getBodyAsString()); + $this->assertFalse($sendResponse); + $response->setStatus(200); + $response->setBody($this->getFreeBusyResponse()); + }); + + $this->serverFactory->expects(self::once()) + ->method('createAttendeeAvailabilityServer') + ->willReturn($server); + + $start = new DateTimeImmutable('2025-01-16T06:00:00Z'); + $end = new DateTimeImmutable('2025-01-17T06:00:00Z'); + $actual = $this->manager->checkAvailability($start, $end, $organizer, [ + 'user@imap.localhost', + 'empty@imap.localhost', + 'nouser@domain.tld', + ]); + $expected = [ + new AvailabilityResult('admin@imap.localhost', false), + new AvailabilityResult('empty@imap.localhost', true), + new AvailabilityResult('user@imap.localhost', false), + ]; + $this->assertEquals($expected, $actual); + } + + public function testCheckAvailabilityWithMailtoPrefix(): void { + $organizer = $this->createMock(IUser::class); + $organizer->expects(self::once()) + ->method('getUID') + ->willReturn('admin'); + $organizer->expects(self::once()) + ->method('getEMailAddress') + ->willReturn('admin@imap.localhost'); + + $user1 = $this->createMock(IUser::class); + $user2 = $this->createMock(IUser::class); + + $this->userManager->expects(self::exactly(3)) + ->method('getByEmail') + ->willReturnMap([ + ['user@imap.localhost', [$user1]], + ['empty@imap.localhost', [$user2]], + ['nouser@domain.tld', []], + ]); + + $authPlugin = $this->createMock(CustomPrincipalPlugin::class); + $authPlugin->expects(self::once()) + ->method('setCurrentPrincipal') + ->with('principals/users/admin'); + + $server = $this->createMock(Server::class); + $server->expects(self::once()) + ->method('getPlugin') + ->with('auth') + ->willReturn($authPlugin); + $server->expects(self::once()) + ->method('invokeMethod') + ->willReturnCallback(function ( + RequestInterface $request, + ResponseInterface $response, + bool $sendResponse, + ): void { + $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics'); + $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('calendars/admin/outbox', $request->getPath()); + $this->assertEquals('text/calendar', $request->getHeader('Content-Type')); + $this->assertEquals('0', $request->getHeader('Depth')); + $this->assertEquals($requestBody, $request->getBodyAsString()); + $this->assertFalse($sendResponse); + $response->setStatus(200); + $response->setBody($this->getFreeBusyResponse()); + }); + + $this->serverFactory->expects(self::once()) + ->method('createAttendeeAvailabilityServer') + ->willReturn($server); + + $start = new DateTimeImmutable('2025-01-16T06:00:00Z'); + $end = new DateTimeImmutable('2025-01-17T06:00:00Z'); + $actual = $this->manager->checkAvailability($start, $end, $organizer, [ + 'mailto:user@imap.localhost', + 'mailto:empty@imap.localhost', + 'mailto:nouser@domain.tld', + ]); + $expected = [ + new AvailabilityResult('admin@imap.localhost', false), + new AvailabilityResult('empty@imap.localhost', true), + new AvailabilityResult('user@imap.localhost', false), + ]; + $this->assertEquals($expected, $actual); + } } diff --git a/tests/lib/Calendar/Resource/ManagerTest.php b/tests/lib/Calendar/Resource/ManagerTest.php index 8753f547fb2..15977765da7 100644 --- a/tests/lib/Calendar/Resource/ManagerTest.php +++ b/tests/lib/Calendar/Resource/ManagerTest.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Calendar\Resource; @@ -30,19 +13,22 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\Bootstrap\ServiceRegistration; use OC\Calendar\Resource\Manager; +use OC\Calendar\ResourcesRoomsUpdater; use OCP\Calendar\Resource\IBackend; use OCP\IServerContainer; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class ManagerTest extends TestCase { - /** @var Coordinator|MockObject */ private $coordinator; /** @var IServerContainer|MockObject */ private $server; + /** @var ResourcesRoomsUpdater|MockObject */ + private $resourcesRoomsUpdater; + /** @var Manager */ private $manager; @@ -51,9 +37,12 @@ class ManagerTest extends TestCase { $this->coordinator = $this->createMock(Coordinator::class); $this->server = $this->createMock(IServerContainer::class); + $this->resourcesRoomsUpdater = $this->createMock(ResourcesRoomsUpdater::class); + $this->manager = new Manager( $this->coordinator, $this->server, + $this->resourcesRoomsUpdater, ); } @@ -144,4 +133,11 @@ class ManagerTest extends TestCase { self::assertEquals([], $this->manager->getBackends()); } + + public function testUpdate(): void { + $this->resourcesRoomsUpdater->expects(self::once()) + ->method('updateResources'); + + $this->manager->update(); + } } diff --git a/tests/lib/Calendar/ResourcesRoomsUpdaterTest.php b/tests/lib/Calendar/ResourcesRoomsUpdaterTest.php new file mode 100644 index 00000000000..9e54d21fda4 --- /dev/null +++ b/tests/lib/Calendar/ResourcesRoomsUpdaterTest.php @@ -0,0 +1,439 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Calendar; + +use OC\Calendar\ResourcesRoomsUpdater; +use OCA\DAV\CalDAV\CalDavBackend; +use OCP\Calendar\BackendTemporarilyUnavailableException; +use OCP\Calendar\IMetadataProvider; +use OCP\Calendar\Resource\IBackend; +use OCP\Calendar\Resource\IManager as IResourceManager; +use OCP\Calendar\Resource\IResource; +use OCP\Calendar\Room\IManager as IRoomManager; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Container\ContainerInterface; +use Test\TestCase; + +interface tmpI extends IResource, IMetadataProvider { +} + +class ResourcesRoomsUpdaterTest extends TestCase { + private ResourcesRoomsUpdater $updater; + + /** @var IResourceManager|MockObject */ + private $resourceManager; + + /** @var IRoomManager|MockObject */ + private $roomManager; + + /** @var ContainerInterface|MockObject */ + private $container; + + /** @var CalDavBackend|MockObject */ + private $calDavBackend; + + protected function setUp(): void { + parent::setUp(); + + $this->resourceManager = $this->createMock(IResourceManager::class); + $this->roomManager = $this->createMock(IRoomManager::class); + $this->container = $this->createMock(ContainerInterface::class); + $this->calDavBackend = $this->createMock(CalDavBackend::class); + + $this->container->method('get') + ->willReturnMap([ + [IResourceManager::class, $this->resourceManager], + [IRoomManager::class, $this->roomManager], + ]); + + $this->updater = new ResourcesRoomsUpdater( + $this->container, + self::$realDatabase, + $this->calDavBackend + ); + } + + protected function tearDown(): void { + $query = self::$realDatabase->getQueryBuilder(); + $query->delete('calendar_resources')->execute(); + $query->delete('calendar_resources_md')->execute(); + $query->delete('calendar_rooms')->execute(); + $query->delete('calendar_rooms_md')->execute(); + } + + /** + * Data in Cache: + * resources: + * [backend1, res1, Beamer1, {}] - [] + * [backend1, res2, TV1, {}] - [] + * [backend2, res3, Beamer2, {}] - ['meta1' => 'value1', 'meta2' => 'value2'] + * [backend2, res4, TV2, {}] - ['meta1' => 'value1', 'meta3' => 'value3-old'] + * [backend3, res5, Beamer3, {}] - [] + * [backend3, res6, Pointer, {foo, bar}] - ['meta99' => 'value99'] + * + * Data in Backend: + * backend1 gone + * backend2 throws BackendTemporarilyUnavailableException + * [backend3, res6, Pointer123, {foo, biz}] - ['meta99' => 'value99-new', 'meta123' => 'meta456'] + * [backend3, res7, Resource4, {biz}] - ['meta1' => 'value1'] + * [backend4, res8, Beamer, {}] - ['meta2' => 'value2'] + * [backend4, res9, Beamer2, {}] - [] + * + * Expected after run: + * [backend1, res1, Beamer1, {}] - [] + * [backend1, res2, TV1, {}] - [] + * [backend2, res3, Beamer2, {}] - ['meta1' => 'value1', 'meta2' => 'value2'] + * [backend2, res4, TV2, {}] - ['meta1' => 'value1', 'meta3' => 'value3-old'] + * [backend3, res6, Pointer123, {foo, biz}] - ['meta99' => 'value99-new', 'meta123' => 'meta456'] + * [backend3, res7, Resource4, {biz}] - ['meta1' => 'value1'] + * [backend4, res8, Beamer, {}] - ['meta2' => 'value2'] + * [backend4, res9, Beamer2, {}] - [] + */ + + public function testUpdateBoth(): void { + $this->createTestResourcesInCache(); + + $backend2 = $this->createMock(IBackend::class); + $backend3 = $this->createMock(IBackend::class); + $backend4 = $this->createMock(IBackend::class); + + $res6 = $this->createMock(tmpI::class); + $res7 = $this->createMock(tmpI::class); + $res8 = $this->createMock(tmpI::class); + $res9 = $this->createMock(IResource::class); + + $backend2->method('getBackendIdentifier') + ->willReturn('backend2'); + $backend2->method('listAllResources') + ->willThrowException(new BackendTemporarilyUnavailableException()); + $backend2->method('getResource') + ->willThrowException(new BackendTemporarilyUnavailableException()); + $backend2->method('getAllResources') + ->willThrowException(new BackendTemporarilyUnavailableException()); + $backend3->method('getBackendIdentifier') + ->willReturn('backend3'); + $backend3->method('listAllResources') + ->willReturn(['res6', 'res7']); + $backend3->method('getResource') + ->willReturnMap([ + ['res6', $res6], + ['res7', $res7], + ]); + $backend4->method('getBackendIdentifier') + ->willReturn('backend4'); + $backend4->method('listAllResources') + ->willReturn(['res8', 'res9']); + $backend4->method('getResource') + ->willReturnMap([ + ['res8', $res8], + ['res9', $res9], + ]); + + $res6->method('getId')->willReturn('res6'); + $res6->method('getDisplayName')->willReturn('Pointer123'); + $res6->method('getGroupRestrictions')->willReturn(['foo', 'biz']); + $res6->method('getEMail')->willReturn('res6@foo.bar'); + $res6->method('getBackend')->willReturn($backend3); + + $res6->method('getAllAvailableMetadataKeys')->willReturn(['meta99', 'meta123']); + $res6->method('getMetadataForKey')->willReturnCallback(function ($key) { + switch ($key) { + case 'meta99': + return 'value99-new'; + + case 'meta123': + return 'meta456'; + + default: + return null; + } + }); + + $res7->method('getId')->willReturn('res7'); + $res7->method('getDisplayName')->willReturn('Resource4'); + $res7->method('getGroupRestrictions')->willReturn(['biz']); + $res7->method('getEMail')->willReturn('res7@foo.bar'); + $res7->method('getBackend')->willReturn($backend3); + $res7->method('getAllAvailableMetadataKeys')->willReturn(['meta1']); + $res7->method('getMetadataForKey')->willReturnCallback(function ($key) { + switch ($key) { + case 'meta1': + return 'value1'; + + default: + return null; + } + }); + + $res8->method('getId')->willReturn('res8'); + $res8->method('getDisplayName')->willReturn('Beamer'); + $res8->method('getGroupRestrictions')->willReturn([]); + $res8->method('getEMail')->willReturn('res8@foo.bar'); + $res8->method('getBackend')->willReturn($backend4); + $res8->method('getAllAvailableMetadataKeys')->willReturn(['meta2']); + $res8->method('getMetadataForKey')->willReturnCallback(function ($key) { + switch ($key) { + case 'meta2': + return 'value2'; + + default: + return null; + } + }); + + $res9->method('getId')->willReturn('res9'); + $res9->method('getDisplayName')->willReturn('Beamer2'); + $res9->method('getGroupRestrictions')->willReturn([]); + $res9->method('getEMail')->willReturn('res9@foo.bar'); + $res9->method('getBackend')->willReturn($backend4); + + $this->resourceManager + ->method('getBackends') + ->willReturn([ + $backend2, $backend3, $backend4 + ]); + $this->resourceManager + ->method('getBackend') + ->willReturnMap([ + ['backend2', $backend2], + ['backend3', $backend3], + ['backend4', $backend4], + ]); + + $this->updater->updateResources(); + $this->updater->updateRooms(); + + $query = self::$realDatabase->getQueryBuilder(); + $query->select('*')->from('calendar_resources'); + + $rows = []; + $ids = []; + $stmt = $query->execute(); + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $ids[$row['backend_id'] . '::' . $row['resource_id']] = $row['id']; + unset($row['id']); + $rows[] = $row; + } + + $this->assertEquals([ + [ + 'backend_id' => 'backend1', + 'resource_id' => 'res1', + 'displayname' => 'Beamer1', + 'email' => 'res1@foo.bar', + 'group_restrictions' => '[]', + ], + [ + 'backend_id' => 'backend1', + 'resource_id' => 'res2', + 'displayname' => 'TV1', + 'email' => 'res2@foo.bar', + 'group_restrictions' => '[]', + ], + [ + 'backend_id' => 'backend2', + 'resource_id' => 'res3', + 'displayname' => 'Beamer2', + 'email' => 'res3@foo.bar', + 'group_restrictions' => '[]', + ], + [ + 'backend_id' => 'backend2', + 'resource_id' => 'res4', + 'displayname' => 'TV2', + 'email' => 'res4@foo.bar', + 'group_restrictions' => '[]', + ], + [ + 'backend_id' => 'backend3', + 'resource_id' => 'res6', + 'displayname' => 'Pointer123', + 'email' => 'res6@foo.bar', + 'group_restrictions' => '["foo","biz"]', + ], + [ + 'backend_id' => 'backend3', + 'resource_id' => 'res7', + 'displayname' => 'Resource4', + 'email' => 'res7@foo.bar', + 'group_restrictions' => '["biz"]', + ], + [ + 'backend_id' => 'backend4', + 'resource_id' => 'res8', + 'displayname' => 'Beamer', + 'email' => 'res8@foo.bar', + 'group_restrictions' => '[]', + ], + [ + 'backend_id' => 'backend4', + 'resource_id' => 'res9', + 'displayname' => 'Beamer2', + 'email' => 'res9@foo.bar', + 'group_restrictions' => '[]', + ], + ], $rows); + + $query2 = self::$realDatabase->getQueryBuilder(); + $query2->select('*')->from('calendar_resources_md'); + + $rows2 = []; + $stmt = $query2->execute(); + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + unset($row['id']); + $rows2[] = $row; + } + + $this->assertEquals([ + [ + 'resource_id' => $ids['backend2::res3'], + 'key' => 'meta1', + 'value' => 'value1', + ], + [ + 'resource_id' => $ids['backend2::res3'], + 'key' => 'meta2', + 'value' => 'value2', + ], + [ + 'resource_id' => $ids['backend2::res4'], + 'key' => 'meta1', + 'value' => 'value1', + ], + [ + 'resource_id' => $ids['backend2::res4'], + 'key' => 'meta3', + 'value' => 'value3-old', + ], + [ + 'resource_id' => $ids['backend3::res6'], + 'key' => 'meta99', + 'value' => 'value99-new', + ], + [ + 'resource_id' => $ids['backend3::res7'], + 'key' => 'meta1', + 'value' => 'value1', + ], + [ + 'resource_id' => $ids['backend3::res6'], + 'key' => 'meta123', + 'value' => 'meta456', + ], + [ + 'resource_id' => $ids['backend4::res8'], + 'key' => 'meta2', + 'value' => 'value2', + ] + ], $rows2); + } + + protected function createTestResourcesInCache() { + $query = self::$realDatabase->getQueryBuilder(); + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend1'), + 'resource_id' => $query->createNamedParameter('res1'), + 'email' => $query->createNamedParameter('res1@foo.bar'), + 'displayname' => $query->createNamedParameter('Beamer1'), + 'group_restrictions' => $query->createNamedParameter('[]'), + ]) + ->execute(); + + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend1'), + 'resource_id' => $query->createNamedParameter('res2'), + 'email' => $query->createNamedParameter('res2@foo.bar'), + 'displayname' => $query->createNamedParameter('TV1'), + 'group_restrictions' => $query->createNamedParameter('[]'), + ]) + ->execute(); + + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend2'), + 'resource_id' => $query->createNamedParameter('res3'), + 'email' => $query->createNamedParameter('res3@foo.bar'), + 'displayname' => $query->createNamedParameter('Beamer2'), + 'group_restrictions' => $query->createNamedParameter('[]'), + ]) + ->execute(); + $id3 = $query->getLastInsertId(); + + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend2'), + 'resource_id' => $query->createNamedParameter('res4'), + 'email' => $query->createNamedParameter('res4@foo.bar'), + 'displayname' => $query->createNamedParameter('TV2'), + 'group_restrictions' => $query->createNamedParameter('[]'), + ]) + ->execute(); + $id4 = $query->getLastInsertId(); + + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend3'), + 'resource_id' => $query->createNamedParameter('res5'), + 'email' => $query->createNamedParameter('res5@foo.bar'), + 'displayname' => $query->createNamedParameter('Beamer3'), + 'group_restrictions' => $query->createNamedParameter('[]'), + ]) + ->execute(); + + $query->insert('calendar_resources') + ->values([ + 'backend_id' => $query->createNamedParameter('backend3'), + 'resource_id' => $query->createNamedParameter('res6'), + 'email' => $query->createNamedParameter('res6@foo.bar'), + 'displayname' => $query->createNamedParameter('Pointer'), + 'group_restrictions' => $query->createNamedParameter('["foo", "bar"]'), + ]) + ->execute(); + $id6 = $query->getLastInsertId(); + + $query->insert('calendar_resources_md') + ->values([ + 'resource_id' => $query->createNamedParameter($id3), + 'key' => $query->createNamedParameter('meta1'), + 'value' => $query->createNamedParameter('value1') + ]) + ->execute(); + $query->insert('calendar_resources_md') + ->values([ + 'resource_id' => $query->createNamedParameter($id3), + 'key' => $query->createNamedParameter('meta2'), + 'value' => $query->createNamedParameter('value2') + ]) + ->execute(); + $query->insert('calendar_resources_md') + ->values([ + 'resource_id' => $query->createNamedParameter($id4), + 'key' => $query->createNamedParameter('meta1'), + 'value' => $query->createNamedParameter('value1') + ]) + ->execute(); + $query->insert('calendar_resources_md') + ->values([ + 'resource_id' => $query->createNamedParameter($id4), + 'key' => $query->createNamedParameter('meta3'), + 'value' => $query->createNamedParameter('value3-old') + ]) + ->execute(); + $query->insert('calendar_resources_md') + ->values([ + 'resource_id' => $query->createNamedParameter($id6), + 'key' => $query->createNamedParameter('meta99'), + 'value' => $query->createNamedParameter('value99') + ]) + ->execute(); + } +} diff --git a/tests/lib/Calendar/Room/ManagerTest.php b/tests/lib/Calendar/Room/ManagerTest.php index 598415246dc..e0f2de970ed 100644 --- a/tests/lib/Calendar/Room/ManagerTest.php +++ b/tests/lib/Calendar/Room/ManagerTest.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\Calendar\Room; @@ -29,6 +12,7 @@ namespace Test\Calendar\Room; use OC\AppFramework\Bootstrap\Coordinator; use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\Bootstrap\ServiceRegistration; +use OC\Calendar\ResourcesRoomsUpdater; use OC\Calendar\Room\Manager; use OCP\Calendar\Room\IBackend; use OCP\IServerContainer; @@ -36,13 +20,15 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class ManagerTest extends TestCase { - /** @var Coordinator|MockObject */ private $coordinator; /** @var IServerContainer|MockObject */ private $server; + /** @var ResourcesRoomsUpdater|MockObject */ + private $resourcesRoomsUpdater; + /** @var Manager */ private $manager; @@ -51,9 +37,12 @@ class ManagerTest extends TestCase { $this->coordinator = $this->createMock(Coordinator::class); $this->server = $this->createMock(IServerContainer::class); + $this->resourcesRoomsUpdater = $this->createMock(ResourcesRoomsUpdater::class); + $this->manager = new Manager( $this->coordinator, $this->server, + $this->resourcesRoomsUpdater, ); } @@ -146,4 +135,11 @@ class ManagerTest extends TestCase { self::assertEquals([], $this->manager->getBackends()); } + + public function testUpdate(): void { + $this->resourcesRoomsUpdater->expects(self::once()) + ->method('updateRooms'); + + $this->manager->update(); + } } |