aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/lib/Service/AbsenceService.php41
-rw-r--r--apps/dav/tests/unit/Service/AbsenceServiceTest.php471
2 files changed, 492 insertions, 20 deletions
diff --git a/apps/dav/lib/Service/AbsenceService.php b/apps/dav/lib/Service/AbsenceService.php
index 874e86f6e1c..7c0d6eec082 100644
--- a/apps/dav/lib/Service/AbsenceService.php
+++ b/apps/dav/lib/Service/AbsenceService.php
@@ -34,9 +34,7 @@ use OCA\DAV\Db\AbsenceMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
-use OCP\Calendar\IManager;
use OCP\EventDispatcher\IEventDispatcher;
-use OCP\IConfig;
use OCP\IUser;
use OCP\User\Events\OutOfOfficeChangedEvent;
use OCP\User\Events\OutOfOfficeClearedEvent;
@@ -50,8 +48,6 @@ class AbsenceService {
private IJobList $jobList,
private TimezoneService $timezoneService,
private ITimeFactory $timeFactory,
- private IConfig $appConfig,
- private IManager $calendarManager,
) {
}
@@ -97,22 +93,27 @@ class AbsenceService {
$this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent($eventData));
}
- $this->jobList->scheduleAfter(
- OutOfOfficeEventDispatcherJob::class,
- $eventData->getStartDate(),
- [
- 'id' => $absence->getId(),
- 'event' => OutOfOfficeEventDispatcherJob::EVENT_START,
- ],
- );
- $this->jobList->scheduleAfter(
- OutOfOfficeEventDispatcherJob::class,
- $eventData->getEndDate(),
- [
- 'id' => $absence->getId(),
- 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
- ],
- );
+ $now = $this->timeFactory->getTime();
+ if ($eventData->getStartDate() > $now) {
+ $this->jobList->scheduleAfter(
+ OutOfOfficeEventDispatcherJob::class,
+ $eventData->getStartDate(),
+ [
+ 'id' => $absence->getId(),
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_START,
+ ],
+ );
+ }
+ if ($eventData->getEndDate() > $now) {
+ $this->jobList->scheduleAfter(
+ OutOfOfficeEventDispatcherJob::class,
+ $eventData->getEndDate(),
+ [
+ 'id' => $absence->getId(),
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
+ ],
+ );
+ }
return $absence;
}
diff --git a/apps/dav/tests/unit/Service/AbsenceServiceTest.php b/apps/dav/tests/unit/Service/AbsenceServiceTest.php
new file mode 100644
index 00000000000..f6f16e28a23
--- /dev/null
+++ b/apps/dav/tests/unit/Service/AbsenceServiceTest.php
@@ -0,0 +1,471 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
+ *
+ * @author Richard Steinmetz <richard@steinmetz.cloud>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\dav\tests\unit\Service;
+
+use DateTimeImmutable;
+use DateTimeZone;
+use OCA\DAV\BackgroundJob\OutOfOfficeEventDispatcherJob;
+use OCA\DAV\CalDAV\TimezoneService;
+use OCA\DAV\Db\Absence;
+use OCA\DAV\Db\AbsenceMapper;
+use OCA\DAV\Service\AbsenceService;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IUser;
+use OCP\User\Events\OutOfOfficeChangedEvent;
+use OCP\User\Events\OutOfOfficeScheduledEvent;
+use PHPUnit\Framework\TestCase;
+
+class AbsenceServiceTest extends TestCase {
+ private AbsenceService $absenceService;
+
+ /** @var MockObject|AbsenceMapper */
+ private $absenceMapper;
+
+ /** @var MockObject|IEventDispatcher */
+ private $eventDispatcher;
+
+ /** @var MockObject|IJobList */
+ private $jobList;
+
+ /** @var MockObject|TimezoneService */
+ private $timezoneService;
+
+ /** @var MockObject|ITimeFactory */
+ private $timeFactory;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->absenceMapper = $this->createMock(AbsenceMapper::class);
+ $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
+ $this->jobList = $this->createMock(IJobList::class);
+ $this->timezoneService = $this->createMock(TimezoneService::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+
+ $this->absenceService = new AbsenceService(
+ $this->absenceMapper,
+ $this->eventDispatcher,
+ $this->jobList,
+ $this->timezoneService,
+ $this->timeFactory,
+ );
+ }
+
+ public function testCreateAbsenceEmitsScheduledEvent() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willThrowException(new DoesNotExistException('foo bar'));
+ $this->absenceMapper->expects(self::once())
+ ->method('insert')
+ ->willReturnCallback(function (Absence $absence): Absence {
+ $absence->setId(1);
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn('Europe/Berlin');
+ $this->eventDispatcher->expects(self::once())
+ ->method('dispatchTyped')
+ ->with(self::callback(static function (Event $event) use ($user, $tz): bool {
+ self::assertInstanceOf(OutOfOfficeScheduledEvent::class, $event);
+ /** @var OutOfOfficeScheduledEvent $event */
+ $data = $event->getData();
+ self::assertEquals('1', $data->getId());
+ self::assertEquals($user, $data->getUser());
+ self::assertEquals(
+ (new DateTimeImmutable('2023-01-05', $tz))->getTimeStamp(),
+ $data->getStartDate(),
+ );
+ self::assertEquals(
+ (new DateTimeImmutable('2023-01-10', $tz))->getTimeStamp() + 3600 * 23 + 59 * 60,
+ $data->getEndDate(),
+ );
+ self::assertEquals('status', $data->getShortMessage());
+ self::assertEquals('message', $data->getMessage());
+ return true;
+ }));
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn(PHP_INT_MAX);
+ $this->jobList->expects(self::never())
+ ->method('scheduleAfter');
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ '2023-01-10',
+ 'status',
+ 'message',
+ );
+ }
+
+ public function testUpdateAbsenceEmitsChangedEvent() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+ $absence = new Absence();
+ $absence->setId(1);
+ $absence->setFirstDay('1970-01-01');
+ $absence->setLastDay('1970-01-10');
+ $absence->setStatus('old status');
+ $absence->setMessage('old message');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->absenceMapper->expects(self::once())
+ ->method('update')
+ ->willReturnCallback(static function (Absence $absence): Absence {
+ self::assertEquals('2023-01-05', $absence->getFirstDay());
+ self::assertEquals('2023-01-10', $absence->getLastDay());
+ self::assertEquals('status', $absence->getStatus());
+ self::assertEquals('message', $absence->getMessage());
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn('Europe/Berlin');
+ $this->eventDispatcher->expects(self::once())
+ ->method('dispatchTyped')
+ ->with(self::callback(static function (Event $event) use ($user, $tz): bool {
+ self::assertInstanceOf(OutOfOfficeChangedEvent::class, $event);
+ /** @var OutOfOfficeChangedEvent $event */
+ $data = $event->getData();
+ self::assertEquals('1', $data->getId());
+ self::assertEquals($user, $data->getUser());
+ self::assertEquals(
+ (new DateTimeImmutable('2023-01-05', $tz))->getTimeStamp(),
+ $data->getStartDate(),
+ );
+ self::assertEquals(
+ (new DateTimeImmutable('2023-01-10', $tz))->getTimeStamp() + 3600 * 23 + 59 * 60,
+ $data->getEndDate(),
+ );
+ self::assertEquals('status', $data->getShortMessage());
+ self::assertEquals('message', $data->getMessage());
+ return true;
+ }));
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn(PHP_INT_MAX);
+ $this->jobList->expects(self::never())
+ ->method('scheduleAfter');
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ '2023-01-10',
+ 'status',
+ 'message',
+ );
+ }
+
+ public function testCreateAbsenceSchedulesBothJobs() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $startDateString = '2023-01-05';
+ $startDate = new DateTimeImmutable($startDateString, $tz);
+ $endDateString = '2023-01-10';
+ $endDate = new DateTimeImmutable($endDateString, $tz);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willThrowException(new DoesNotExistException('foo bar'));
+ $this->absenceMapper->expects(self::once())
+ ->method('insert')
+ ->willReturnCallback(function (Absence $absence): Absence {
+ $absence->setId(1);
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-01', $tz))->getTimestamp());
+ $this->jobList->expects(self::exactly(2))
+ ->method('scheduleAfter')
+ ->willReturnMap([
+ [OutOfOfficeEventDispatcherJob::class, $startDate->getTimestamp(), [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_START,
+ ]],
+ [OutOfOfficeEventDispatcherJob::class, $endDate->getTimestamp() + 3600 * 23 + 59 * 60, [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
+ ]],
+ ]);
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ $startDateString,
+ $endDateString,
+ '',
+ '',
+ );
+ }
+
+ public function testCreateAbsenceSchedulesOnlyEndJob() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $endDateString = '2023-01-10';
+ $endDate = new DateTimeImmutable($endDateString, $tz);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willThrowException(new DoesNotExistException('foo bar'));
+ $this->absenceMapper->expects(self::once())
+ ->method('insert')
+ ->willReturnCallback(function (Absence $absence): Absence {
+ $absence->setId(1);
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-07', $tz))->getTimestamp());
+ $this->jobList->expects(self::once())
+ ->method('scheduleAfter')
+ ->with(OutOfOfficeEventDispatcherJob::class, $endDate->getTimestamp() + 3600 * 23 + 59 * 60, [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
+ ]);
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ $endDateString,
+ '',
+ '',
+ );
+ }
+
+ public function testCreateAbsenceSchedulesNoJob() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willThrowException(new DoesNotExistException('foo bar'));
+ $this->absenceMapper->expects(self::once())
+ ->method('insert')
+ ->willReturnCallback(function (Absence $absence): Absence {
+ $absence->setId(1);
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-12', $tz))->getTimestamp());
+ $this->jobList->expects(self::never())
+ ->method('scheduleAfter');
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ '2023-01-10',
+ '',
+ '',
+ );
+ }
+
+ public function testUpdateAbsenceSchedulesBothJobs() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $startDateString = '2023-01-05';
+ $startDate = new DateTimeImmutable($startDateString, $tz);
+ $endDateString = '2023-01-10';
+ $endDate = new DateTimeImmutable($endDateString, $tz);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+ $absence = new Absence();
+ $absence->setId(1);
+ $absence->setFirstDay('1970-01-01');
+ $absence->setLastDay('1970-01-10');
+ $absence->setStatus('old status');
+ $absence->setMessage('old message');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->absenceMapper->expects(self::once())
+ ->method('update')
+ ->willReturnCallback(static function (Absence $absence) use ($startDateString, $endDateString): Absence {
+ self::assertEquals($startDateString, $absence->getFirstDay());
+ self::assertEquals($endDateString, $absence->getLastDay());
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-01', $tz))->getTimestamp());
+ $this->jobList->expects(self::exactly(2))
+ ->method('scheduleAfter')
+ ->willReturnMap([
+ [OutOfOfficeEventDispatcherJob::class, $startDate->getTimestamp(), [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_START,
+ ]],
+ [OutOfOfficeEventDispatcherJob::class, $endDate->getTimestamp() + 3600 * 23 + 59 * 60, [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
+ ]],
+ ]);
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ $startDateString,
+ $endDateString,
+ '',
+ '',
+ );
+ }
+
+ public function testUpdateSchedulesOnlyEndJob() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $endDateString = '2023-01-10';
+ $endDate = new DateTimeImmutable($endDateString, $tz);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+ $absence = new Absence();
+ $absence->setId(1);
+ $absence->setFirstDay('1970-01-01');
+ $absence->setLastDay('1970-01-10');
+ $absence->setStatus('old status');
+ $absence->setMessage('old message');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->absenceMapper->expects(self::once())
+ ->method('update')
+ ->willReturnCallback(static function (Absence $absence) use ($endDateString): Absence {
+ self::assertEquals('2023-01-05', $absence->getFirstDay());
+ self::assertEquals($endDateString, $absence->getLastDay());
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-07', $tz))->getTimestamp());
+ $this->jobList->expects(self::once())
+ ->method('scheduleAfter')
+ ->with(OutOfOfficeEventDispatcherJob::class, $endDate->getTimestamp() + 23 * 3600 + 59 * 60, [
+ 'id' => '1',
+ 'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
+ ]);
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ $endDateString,
+ '',
+ '',
+ );
+ }
+
+ public function testUpdateAbsenceSchedulesNoJob() {
+ $tz = new DateTimeZone('Europe/Berlin');
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+ $absence = new Absence();
+ $absence->setId(1);
+ $absence->setFirstDay('1970-01-01');
+ $absence->setLastDay('1970-01-10');
+ $absence->setStatus('old status');
+ $absence->setMessage('old message');
+
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->absenceMapper->expects(self::once())
+ ->method('update')
+ ->willReturnCallback(static function (Absence $absence): Absence {
+ self::assertEquals('2023-01-05', $absence->getFirstDay());
+ self::assertEquals('2023-01-10', $absence->getLastDay());
+ return $absence;
+ });
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user')
+ ->willReturn($tz->getName());
+ $this->timeFactory->expects(self::once())
+ ->method('getTime')
+ ->willReturn((new DateTimeImmutable('2023-01-12', $tz))->getTimestamp());
+ $this->jobList->expects(self::never())
+ ->method('scheduleAfter');
+
+ $this->absenceService->createOrUpdateAbsence(
+ $user,
+ '2023-01-05',
+ '2023-01-10',
+ '',
+ '',
+ );
+ }
+}