Browse Source

Final fixes

Signed-off-by: Georg Ehrke <developer@georgehrke.com>
Georg Ehrke 4 years ago
parent
commit
4d28a4544e

+ 1 - 1
3rdparty

@@ -1 +1 @@
-Subproject commit ef289bc27eae0cdfc3f74f419ace8dda8dd84ef0
+Subproject commit 49ccfbb28661b9ef7743c1725cd2571259215929

+ 1 - 1
apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php

@@ -23,7 +23,7 @@ declare(strict_types=1);
  */
 namespace OCA\DAV\BackgroundJob;
 
-use OC\BackgroundJob\QueuedJob;
+use OCP\BackgroundJob\QueuedJob;
 use OCA\DAV\CalDAV\Reminder\ReminderService;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\BackgroundJob\IJobList;

+ 1 - 1
apps/dav/lib/CalDAV/Reminder/Backend.php

@@ -63,7 +63,7 @@ class Backend {
 		$query = $this->db->getQueryBuilder();
 		$query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
 			->from('calendar_reminders', 'cr')
-//			->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
+			->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
 			->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
 			->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
 		$stmt = $query->execute();

+ 18 - 21
apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php

@@ -25,8 +25,6 @@ declare(strict_types=1);
  */
 namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
 
-use \DateTime;
-use \DateTimeImmutable;
 use OCA\DAV\CalDAV\Reminder\INotificationProvider;
 use OCP\IConfig;
 use OCP\IL10N;
@@ -36,7 +34,6 @@ use OCP\L10N\IFactory as L10NFactory;
 use OCP\IUser;
 use Sabre\VObject\Component\VEvent;
 use Sabre\VObject\DateTimeParser;
-use Sabre\VObject\Parameter;
 use Sabre\VObject\Property;
 
 /**
@@ -49,22 +46,22 @@ abstract class AbstractProvider implements INotificationProvider  {
 	/** @var string */
 	public const NOTIFICATION_TYPE = '';
 
-    /** @var ILogger */
-    protected $logger;
+	/** @var ILogger */
+	protected $logger;
 
-    /** @var L10NFactory */
-    private $l10nFactory;
+	/** @var L10NFactory */
+	private $l10nFactory;
 
-    /** @var IL10N[] */
+	/** @var IL10N[] */
 	private $l10ns;
 
 	/** @var string */
 	private $fallbackLanguage;
 
-    /** @var IURLGenerator */
-    protected $urlGenerator;
+	/** @var IURLGenerator */
+	protected $urlGenerator;
 
-    /** @var IConfig */
+	/** @var IConfig */
 	protected $config;
 
 	/**
@@ -79,9 +76,9 @@ abstract class AbstractProvider implements INotificationProvider  {
 								IConfig $config) {
 		$this->logger = $logger;
 		$this->l10nFactory = $l10nFactory;
-        $this->urlGenerator = $urlGenerator;
+		$this->urlGenerator = $urlGenerator;
 		$this->config = $config;
-    }
+	}
 
 	/**
 	 * Send notification
@@ -91,22 +88,22 @@ abstract class AbstractProvider implements INotificationProvider  {
 	 * @param IUser[] $users
 	 * @return void
 	 */
-    abstract public function send(VEvent $vevent,
+	abstract public function send(VEvent $vevent,
 						   string $calendarDisplayName,
 						   array $users=[]): void;
 
 	/**
 	 * @return string
 	 */
-    protected function getFallbackLanguage():string {
-    	if ($this->fallbackLanguage) {
-    		return $this->fallbackLanguage;
+	protected function getFallbackLanguage():string {
+		if ($this->fallbackLanguage) {
+			return $this->fallbackLanguage;
 		}
 
-    	$fallbackLanguage = $this->l10nFactory->findLanguage();
-    	$this->fallbackLanguage = $fallbackLanguage;
+		$fallbackLanguage = $this->l10nFactory->findLanguage();
+		$this->fallbackLanguage = $fallbackLanguage;
 
-    	return $fallbackLanguage;
+		return $fallbackLanguage;
 	}
 
 	/**
@@ -114,7 +111,7 @@ abstract class AbstractProvider implements INotificationProvider  {
 	 * @return bool
 	 */
 	protected function hasL10NForLang(string $lang):bool {
-    	return $this->l10nFactory->languageExists('dav', $lang);
+		return $this->l10nFactory->languageExists('dav', $lang);
 	}
 
 	/**

+ 25 - 52
apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php

@@ -25,8 +25,7 @@ declare(strict_types=1);
  */
 namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
 
-use DateTime;
-use DateTimeImmutable;
+use \DateTime;
 use OCP\IConfig;
 use OCP\IL10N;
 use OCP\ILogger;
@@ -97,36 +96,35 @@ class EmailProvider extends AbstractProvider {
 		$organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
 
 		foreach($sortedByLanguage as $lang => $emailAddresses) {
-			if ($this->hasL10NForLang($lang)) {
+			if (!$this->hasL10NForLang($lang)) {
 				$lang = $fallbackLanguage;
 			}
 			$l10n = $this->getL10NForLang($lang);
 			$fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
 
-			$message = $this->mailer->createMessage();
-			$message->setFrom([$fromEMail]);
-			if ($organizer) {
-				$message->setReplyTo($organizer);
-			}
-			$message->setTo([])
-				->setBcc($emailAddresses);
-
 			$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
 			$template->addHeader();
-
 			$this->addSubjectAndHeading($template, $l10n, $vevent);
 			$this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
-
 			$template->addFooter();
-			$message->useTemplate($template);
 
-			try {
-				$failed = $this->mailer->send($message);
-				if ($failed) {
-					$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+			foreach ($emailAddresses as $emailAddress) {
+				$message = $this->mailer->createMessage();
+				$message->setFrom([$fromEMail]);
+				if ($organizer) {
+					$message->setReplyTo($organizer);
+				}
+				$message->setTo([$emailAddress]);
+				$message->useTemplate($template);
+
+				try {
+					$failed = $this->mailer->send($message);
+					if ($failed) {
+						$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+					}
+				} catch (\Exception $ex) {
+					$this->logger->logException($ex, ['app' => 'dav']);
 				}
-			} catch (\Exception $ex) {
-				$this->logger->logException($ex, ['app' => 'dav']);
 			}
 		}
 	}
@@ -165,9 +163,9 @@ class EmailProvider extends AbstractProvider {
 			$template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
 				$this->getAbsoluteImagePath('actions/more.svg'));
 		}
-    }
+	}
 
-    /**
+	/**
 	 * @param string $path
 	 * @return string
 	 */
@@ -201,35 +199,6 @@ class EmailProvider extends AbstractProvider {
 		return [$organizerEMail];
 	}
 
-	/**
-	 * @param array $sortedByLanguage
-	 * @param IUser[] $users
-	 * @param string $defaultLanguage
-	 */
-	private function sortUsersByLanguage(array &$sortedByLanguage,
-										 array $users,
-										 string $defaultLanguage):void {
-		/**
-		 * @var array $sortedByLanguage
-		 * [
-		 *   'de' => ['a@b.com', 'c@d.com'],
-		 *   ...
-		 * ]
-		 */
-		foreach($users as $user) {
-			/** @var IUser $user */
-			$emailAddress = $user->getEMailAddress();
-			$lang = $this->config->getUserValue($user->getUID(),
-				'core', 'lang', $defaultLanguage);
-
-			if (!isset($sortedByLanguage[$lang])) {
-				$sortedByLanguage[$lang] = [];
-			}
-
-			$sortedByLanguage[$lang][] = $emailAddress;
-		}
-	}
-
 	/**
 	 * @param array $emails
 	 * @param string $defaultLanguage
@@ -386,7 +355,7 @@ class EmailProvider extends AbstractProvider {
 			}
 		}
 
-		return array_unique($emailAddresses);
+		return $emailAddresses;
 	}
 
 	/**
@@ -415,7 +384,9 @@ class EmailProvider extends AbstractProvider {
 
 		$diff = $dtstartDt->diff($dtendDt);
 
+		/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 		$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
+		/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 		$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
 
 		if ($isAllDay) {
@@ -432,7 +403,9 @@ class EmailProvider extends AbstractProvider {
 
 		$startTimezone = $endTimezone = null;
 		if (!$vevent->DTSTART->isFloating()) {
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			$startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			$endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
 		}
 

+ 9 - 5
apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php

@@ -47,7 +47,7 @@ class PushProvider extends AbstractProvider {
 	/** @var string */
 	public const NOTIFICATION_TYPE = 'DISPLAY';
 
-    /** @var IManager */
+	/** @var IManager */
 	private $manager;
 
 	/** @var ITimeFactory */
@@ -70,7 +70,7 @@ class PushProvider extends AbstractProvider {
 		parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
 		$this->manager = $manager;
 		$this->timeFactory = $timeFactory;
-    }
+	}
 
 	/**
 	 * Send push notification to all users.
@@ -80,13 +80,13 @@ class PushProvider extends AbstractProvider {
 	 * @param IUser[] $users
 	 * @throws \Exception
 	 */
-    public function send(VEvent $vevent,
+	public function send(VEvent $vevent,
 						 string $calendarDisplayName=null,
 						 array $users=[]):void {
 		$eventDetails = $this->extractEventDetails($vevent);
 		$eventDetails['calendar_displayname'] = $calendarDisplayName;
 
-    	foreach($users as $user) {
+		foreach($users as $user) {
 			/** @var INotification $notification */
 			$notification = $this->manager->createNotification();
 			$notification->setApp(Application::APP_ID)
@@ -101,7 +101,7 @@ class PushProvider extends AbstractProvider {
 
 			$this->manager->notify($notification);
 		}
-    }
+	}
 
 	/**
 	 * @var VEvent $vevent
@@ -124,11 +124,15 @@ class PushProvider extends AbstractProvider {
 				? ((string) $vevent->LOCATION)
 				: null,
 			'all_day' => $start instanceof Property\ICalendar\Date,
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			'start_atom' => $start->getDateTime()->format(\DateTime::ATOM),
 			'start_is_floating' => $start->isFloating(),
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			'start_timezone' => $start->getDateTime()->getTimezone()->getName(),
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			'end_atom' => $end->getDateTime()->format(\DateTime::ATOM),
 			'end_is_floating' => $end->isFloating(),
+			/** @phan-suppress-next-line PhanUndeclaredClassMethod */
 			'end_timezone' => $end->getDateTime()->getTimezone()->getName(),
 		];
 	}

+ 18 - 18
apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php

@@ -31,8 +31,8 @@ namespace OCA\DAV\CalDAV\Reminder;
  */
 class NotificationProviderManager {
 
-    /** @var INotificationProvider[] */
-    private $providers = [];
+	/** @var INotificationProvider[] */
+	private $providers = [];
 
 	/**
 	 * Checks whether a provider for a given ACTION exists
@@ -45,23 +45,23 @@ class NotificationProviderManager {
 			&& isset($this->providers[$type]));
 	}
 
-    /**
+	/**
 	 * Get provider for a given ACTION
 	 *
-     * @param string $type
-     * @return INotificationProvider
-     * @throws NotificationProvider\ProviderNotAvailableException
-     * @throws NotificationTypeDoesNotExistException
-     */
-    public function getProvider(string $type):INotificationProvider {
-        if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
-            if (isset($this->providers[$type])) {
-                return $this->providers[$type];
-            }
-            throw new NotificationProvider\ProviderNotAvailableException($type);
-        }
-        throw new NotificationTypeDoesNotExistException($type);
-    }
+	 * @param string $type
+	 * @return INotificationProvider
+	 * @throws NotificationProvider\ProviderNotAvailableException
+	 * @throws NotificationTypeDoesNotExistException
+	 */
+	public function getProvider(string $type):INotificationProvider {
+		if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
+			if (isset($this->providers[$type])) {
+				return $this->providers[$type];
+			}
+			throw new NotificationProvider\ProviderNotAvailableException($type);
+		}
+		throw new NotificationTypeDoesNotExistException($type);
+	}
 
 	/**
 	 * Registers a new provider
@@ -69,7 +69,7 @@ class NotificationProviderManager {
 	 * @param string $providerClassName
 	 * @throws \OCP\AppFramework\QueryException
 	 */
-    public function registerProvider(string $providerClassName):void {
+	public function registerProvider(string $providerClassName):void {
 		$provider = \OC::$server->query($providerClassName);
 
 		if (!$provider instanceof INotificationProvider) {

+ 4 - 8
apps/dav/lib/CalDAV/Reminder/Notifier.php

@@ -25,7 +25,7 @@ declare(strict_types=1);
 
 namespace OCA\DAV\CalDAV\Reminder;
 
-use DateTime;
+use \DateTime;
 use OCA\DAV\AppInfo\Application;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\IL10N;
@@ -85,10 +85,6 @@ class Notifier implements INotifier {
 	 * @since 17.0.0
 	 */
 	public function getName():string {
-		if ($this->l10n) {
-			return $this->l10n->t('Calendar');
-		}
-
 		return $this->l10nFactory->get('dav')->t('Calendar');
 	}
 
@@ -143,7 +139,7 @@ class Notifier implements INotifier {
 	private function prepareNotificationSubject(INotification $notification): void {
 		$parameters = $notification->getSubjectParameters();
 
-		$startTime = \DateTime::createFromFormat(\DateTimeInterface::ATOM, $parameters['start_atom']);
+		$startTime = \DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
 		$now = $this->timeFactory->getDateTime();
 		$title = $this->getTitleFromParameters($parameters);
 
@@ -220,8 +216,8 @@ class Notifier implements INotifier {
 	 * @throws \Exception
 	 */
 	private function generateDateString(array $parameters):string {
-		$startDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['start_atom']);
-		$endDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['end_atom']);
+		$startDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
+		$endDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['end_atom']);
 		$isAllDay = $parameters['all_day'];
 		$diff = $startDateTime->diff($endDateTime);
 

+ 19 - 19
apps/dav/lib/CalDAV/Reminder/ReminderService.php

@@ -24,7 +24,7 @@ declare(strict_types=1);
  */
 namespace OCA\DAV\CalDAV\Reminder;
 
-use DateTimeImmutable;
+use \DateTimeImmutable;
 use OCA\DAV\CalDAV\CalDavBackend;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\IGroup;
@@ -40,11 +40,11 @@ use Sabre\VObject\Recur\NoInstancesException;
 
 class ReminderService {
 
-    /** @var Backend */
-    private $backend;
+	/** @var Backend */
+	private $backend;
 
-    /** @var NotificationProviderManager */
-    private $notificationProviderManager;
+	/** @var NotificationProviderManager */
+	private $notificationProviderManager;
 
 	/** @var IUserManager */
 	private $userManager;
@@ -83,19 +83,19 @@ class ReminderService {
 	 * @param CalDavBackend $caldavBackend
 	 * @param ITimeFactory $timeFactory
 	 */
-    public function __construct(Backend $backend,
-                                NotificationProviderManager $notificationProviderManager,
+	public function __construct(Backend $backend,
+								NotificationProviderManager $notificationProviderManager,
 								IUserManager $userManager,
 								IGroupManager $groupManager,
 								CalDavBackend $caldavBackend,
 								ITimeFactory $timeFactory) {
-        $this->backend = $backend;
-        $this->notificationProviderManager = $notificationProviderManager;
+		$this->backend = $backend;
+		$this->notificationProviderManager = $notificationProviderManager;
 		$this->userManager = $userManager;
 		$this->groupManager = $groupManager;
 		$this->caldavBackend = $caldavBackend;
 		$this->timeFactory = $timeFactory;
-    }
+	}
 
 	/**
 	 * Process reminders to activate
@@ -103,12 +103,12 @@ class ReminderService {
 	 * @throws NotificationProvider\ProviderNotAvailableException
 	 * @throws NotificationTypeDoesNotExistException
 	 */
-    public function processReminders():void {
-        $reminders = $this->backend->getRemindersToProcess();
+	public function processReminders():void {
+		$reminders = $this->backend->getRemindersToProcess();
 
-        foreach($reminders as $reminder) {
-        	$vcalendar = $this->parseCalendarData($reminder['calendardata']);
-        	if (!$vcalendar) {
+		foreach($reminders as $reminder) {
+			$vcalendar = $this->parseCalendarData($reminder['calendardata']);
+			if (!$vcalendar) {
 				$this->backend->removeReminder($reminder['id']);
 				continue;
 			}
@@ -140,7 +140,7 @@ class ReminderService {
 
 			$this->deleteOrProcessNext($reminder, $vevent);
 		}
-    }
+	}
 
 	/**
 	 * @param string $action
@@ -175,7 +175,7 @@ class ReminderService {
 	/**
 	 * @param array $objectData
 	 */
-    private function onCalendarObjectCreate(array $objectData):void {
+	private function onCalendarObjectCreate(array $objectData):void {
 		/** @var VObject\Component\VCalendar $vcalendar */
 		$vcalendar = $this->parseCalendarData($objectData['calendardata']);
 		if (!$vcalendar) {
@@ -343,7 +343,7 @@ class ReminderService {
 			'is_repeat_based' => false,
 		];
 
-		$repeat = $valarm->REPEAT ? (int) $valarm->REPEAT : 0;
+		$repeat = isset($valarm->REPEAT) ? (int) $valarm->REPEAT->getValue() : 0;
 		for($i = 0; $i < $repeat; $i++) {
 			if ($valarm->DURATION === null) {
 				continue;
@@ -671,7 +671,7 @@ class ReminderService {
 			return null;
 		}
 
-		if (strcasecmp($principalUri, 'principals/users/') !== 0) {
+		if (stripos($principalUri, 'principals/users/') !== 0) {
 			return null;
 		}
 

+ 2 - 1
apps/dav/lib/Migration/Version1004Date20170825134824.php

@@ -324,7 +324,8 @@ class Version1004Date20170825134824 extends SimpleMigrationStep {
 				'length' => 1,
 			]);
 			$table->addColumn('stripattachments', 'smallint', [
-
+				'notnull' => false,
+				'length' => 1,
 			]);
 			$table->addColumn('lastmodified', 'integer', [
 				'notnull' => false,

+ 30 - 5
apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php

@@ -51,17 +51,42 @@ class EventReminderJobTest extends TestCase {
 
 	public function data(): array
 	{
-		return [[true], [false]];
+		return [
+			[true, true, true],
+			[true, false, false],
+			[false, true, false],
+			[false, false, false],
+		];
 	}
 
 	/**
 	 * @dataProvider data
+	 *
 	 * @param bool $sendEventReminders
+	 * @param bool $sendEventRemindersMode
+	 * @param bool $expectCall
 	 */
-	public function testRun(bool $sendEventReminders): void
-	{
-		$this->config->expects($this->once())->method('getAppValue')->with('dav', 'sendEventReminders', 'yes')->willReturn($sendEventReminders ? 'yes' : 'no');
-		$this->reminderService->expects($this->exactly($sendEventReminders ? 1 : 0))->method('processReminders');
+	public function testRun(bool $sendEventReminders, bool $sendEventRemindersMode, bool $expectCall): void {
+		$this->config->expects($this->at(0))
+			->method('getAppValue')
+			->with('dav', 'sendEventReminders', 'yes')
+			->willReturn($sendEventReminders ? 'yes' : 'no');
+
+		if ($sendEventReminders) {
+			$this->config->expects($this->at(1))
+				->method('getAppValue')
+				->with('dav', 'sendEventRemindersMode', 'backgroundjob')
+				->willReturn($sendEventRemindersMode ? 'backgroundjob' : 'cron');
+
+		}
+
+		if ($expectCall) {
+			$this->reminderService->expects($this->once())
+				->method('processReminders');
+		} else {
+			$this->reminderService->expects($this->never())
+				->method('processReminders');
+		}
 
 		$this->backgroundJob->run([]);
 	}

+ 340 - 256
apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php

@@ -1,8 +1,11 @@
 <?php
+declare(strict_types=1);
 /**
- * @copyright Copyright (c) 2018, Thomas Citharel
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
  *
  * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
  *
  * @license AGPL-3.0
  *
@@ -21,7 +24,6 @@
  */
 namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
 
-use OCP\IDBConnection;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend;
@@ -29,285 +31,367 @@ use Test\TestCase;
 
 class BackendTest extends TestCase {
 
-    /**
-     * Reminder Backend
-     *
-     * @var ReminderBackend|\PHPUnit\Framework\MockObject\MockObject
-     */
-    private $reminderBackend;
-
-    /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
-	private $dbConnection;
+	/**
+	 * Reminder Backend
+	 *
+	 * @var ReminderBackend|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $reminderBackend;
 
 	/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
-    private $timeFactory;
+	private $timeFactory;
 
-    public function setUp() {
+	public function setUp() {
 		parent::setUp();
 
-		$this->dbConnection = $this->createMock(IDBConnection::class);
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->delete('calendar_reminders')->execute();
+		$query->delete('calendarobjects')->execute();
+		$query->delete('calendars')->execute();
+
 		$this->timeFactory = $this->createMock(ITimeFactory::class);
-        $this->reminderBackend = new ReminderBackend($this->dbConnection, $this->timeFactory);
-    }
-
-    public function testCleanRemindersForEvent(): void
-    {
-		/** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
-        $queryBuilder = $this->createMock(IQueryBuilder::class);
-        $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
-		$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
-
-		$this->dbConnection->expects($this->once())
-			->method('getQueryBuilder')
-			->with()
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->method('expr')
-			->will($this->returnValue($expr));
-
-		$expr->method('eq')
-			->will($this->returnValueMap([
-				['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
-				['objecturi', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
-			]));
-		$queryBuilder->method('createNamedParameter')
-			->will($this->returnValueMap([
-				[1, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
-				['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
-			]));
-
-		$queryBuilder->expects($this->at(0))
-			->method('delete')
-			->with('calendar_reminders')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(3))
-			->method('where')
-			->with('WHERE_CLAUSE_1')
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->expects($this->at(6))
-			->method('andWhere')
-			->with('WHERE_CLAUSE_2')
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->expects($this->at(7))
-			->method('execute')
-			->with()
-			->willReturn($stmt);
+		$this->reminderBackend = new ReminderBackend(self::$realDatabase, $this->timeFactory);
 
-		$this->reminderBackend->cleanRemindersForEvent(1, 'object.ics');
+		$this->createRemindersTestSet();
 	}
 
-	public function testCleanRemindersForCalendar(): void
-    {
-		/** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
-        $queryBuilder = $this->createMock(IQueryBuilder::class);
-        $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
-		$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+	protected function tearDown() {
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->delete('calendar_reminders')->execute();
+		$query->delete('calendarobjects')->execute();
+		$query->delete('calendars')->execute();
+	}
 
-		$this->dbConnection->expects($this->once())
-			->method('getQueryBuilder')
-			->with()
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->method('expr')
-			->will($this->returnValue($expr));
-
-		$expr->method('eq')
-			->will($this->returnValueMap([
-				['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
-			]));
-		$queryBuilder->method('createNamedParameter')
-			->will($this->returnValueMap([
-				[1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
-			]));
-
-		$queryBuilder->expects($this->at(0))
-			->method('delete')
-			->with('calendar_reminders')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(3))
-			->method('where')
-			->with('WHERE_CLAUSE_1')
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->expects($this->at(4))
-			->method('execute')
-			->with()
-			->willReturn($stmt);
 
-		$this->reminderBackend->cleanRemindersForCalendar(1337);
+	public function testCleanRemindersForEvent(): void {
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(4, $rows);
+
+		$this->reminderBackend->cleanRemindersForEvent(1);
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(2, $rows);
 	}
 
-	public function testRemoveReminder(): void
-	{
-		/** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
-        $queryBuilder = $this->createMock(IQueryBuilder::class);
-        $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
-		$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+	public function testCleanRemindersForCalendar(): void {
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
 
-		$this->dbConnection->expects($this->once())
-			->method('getQueryBuilder')
-			->with()
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->method('expr')
-			->will($this->returnValue($expr));
-
-		$expr->method('eq')
-			->will($this->returnValueMap([
-				['id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
-			]));
-		$queryBuilder->method('createNamedParameter')
-			->will($this->returnValueMap([
-				[16, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
-			]));
-
-		$queryBuilder->expects($this->at(0))
-			->method('delete')
-			->with('calendar_reminders')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(3))
-			->method('where')
-			->with('WHERE_CLAUSE_1')
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->expects($this->at(4))
-			->method('execute')
-			->with()
-			->willReturn($stmt);
+		$this->assertCount(4, $rows);
+
+		$this->reminderBackend->cleanRemindersForCalendar(1);
 
-		$this->reminderBackend->removeReminder(16);
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(1, $rows);
 	}
 
-	public function testGetRemindersToProcess(): void
-    {
-		$dbData = [[
-				'cr.id' => 30,
-				'cr.calendarid' => 3,
-				'cr.objecturi' => 'object.ics',
-				'cr.type' => 'EMAIL',
-				'cr.notificationdate' => 1337,
-				'cr.uid' => 'user1',
-				'co.calendardata' => 'BEGIN:VCALENDAR',
-				'c.displayname' => 'My Calendar'
-			]];
-
-		$this->timeFactory->expects($this->exactly(2))
+	public function testRemoveReminder(): void {
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(4, $rows);
+
+		$this->reminderBackend->removeReminder((int) $rows[3]['id']);
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(3, $rows);
+	}
+
+	public function testGetRemindersToProcess(): void {
+		$this->timeFactory->expects($this->exactly(1))
 			->method('getTime')
 			->with()
-			->willReturn(1337);
+			->willReturn(123457);
+
+		$rows = $this->reminderBackend->getRemindersToProcess();
+
+		$this->assertCount(2, $rows);
+		unset($rows[0]['id']);
+		unset($rows[1]['id']);
+
+		$this->assertEquals($rows[0], [
+			'calendar_id' => 1,
+			'object_id' => 1,
+			'uid' => 'asd',
+			'is_recurring' => false,
+			'recurrence_id' => 123458,
+			'is_recurrence_exception' => false,
+			'event_hash' => 'asd123',
+			'alarm_hash' => 'asd567',
+			'type' => 'EMAIL',
+			'is_relative' => true,
+			'notification_date' => 123456,
+			'is_repeat_based' => false,
+			'calendardata' => 'Calendar data 123',
+			'displayname' => 'Displayname 123',
+			'principaluri' => 'principals/users/user001',
+		]);
+		$this->assertEquals($rows[1], [
+			'calendar_id' => 1,
+			'object_id' => 1,
+			'uid' => 'asd',
+			'is_recurring' => false,
+			'recurrence_id' => 123458,
+			'is_recurrence_exception' => false,
+			'event_hash' => 'asd123',
+			'alarm_hash' => 'asd567',
+			'type' => 'AUDIO',
+			'is_relative' => true,
+			'notification_date' => 123456,
+			'is_repeat_based' => false,
+			'calendardata' => 'Calendar data 123',
+			'displayname' => 'Displayname 123',
+			'principaluri' => 'principals/users/user001',
+		]);
+	}
 
-		/** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
-        $queryBuilder = $this->createMock(IQueryBuilder::class);
-        $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
-		$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+	public function testGetAllScheduledRemindersForEvent(): void {
+		$rows = $this->reminderBackend->getAllScheduledRemindersForEvent(1);
+
+		$this->assertCount(2, $rows);
+		unset($rows[0]['id']);
+		unset($rows[1]['id']);
+
+		$this->assertEquals($rows[0], [
+			'calendar_id' => 1,
+			'object_id' => 1,
+			'uid' => 'asd',
+			'is_recurring' => false,
+			'recurrence_id' => 123458,
+			'is_recurrence_exception' => false,
+			'event_hash' => 'asd123',
+			'alarm_hash' => 'asd567',
+			'type' => 'EMAIL',
+			'is_relative' => true,
+			'notification_date' => 123456,
+			'is_repeat_based' => false,
+		]);
+		$this->assertEquals($rows[1], [
+			'calendar_id' => 1,
+			'object_id' => 1,
+			'uid' => 'asd',
+			'is_recurring' => false,
+			'recurrence_id' => 123458,
+			'is_recurrence_exception' => false,
+			'event_hash' => 'asd123',
+			'alarm_hash' => 'asd567',
+			'type' => 'AUDIO',
+			'is_relative' => true,
+			'notification_date' => 123456,
+			'is_repeat_based' => false,
+		]);
+	}
 
-		$this->dbConnection->expects($this->once())
-			->method('getQueryBuilder')
-			->with()
-			->willReturn($queryBuilder);
-		$queryBuilder->method('expr')
-			->willReturn($expr);
-
-		$expr->method('eq')
-			->willReturnMap([
-				['cr.calendarid', 'c.id', null, 'EQ_CLAUSE_1'],
-				['co.uri', 'cr.objecturi', null, 'EQ_CLAUSE_2'],
-			]);
-		$expr->method('andX')
-			->willReturnMap([
-				['EQ_CLAUSE_1', 'EQ_CLAUSE_2', 'ANDX_CLAUSE'],
-			]);
-
-		$expr->method('lte')
-			->with('cr.notificationdate', 'createNamedParameter-1', null)
-			->willReturn('LTE_CLAUSE_1');
-
-		$expr->method('gte')
-			->with('cr.eventstartdate', 'createNamedParameter-1', null)
-			->willReturn('GTE_CLAUSE_2');
-
-		$queryBuilder->method('createNamedParameter')
-			->willReturnMap([
-				[1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
-			]);
-
-		$queryBuilder->expects($this->at(0))
-			->method('select')
-			->with(['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'])
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(1))
-			->method('from')
-			->with('calendar_reminders', 'cr')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(4))
-			->method('where')
-			->with('LTE_CLAUSE_1')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(7))
-			->method('andWhere')
-			->with('GTE_CLAUSE_2')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(9))
-			->method('leftJoin')
-			->with('cr', 'calendars', 'c', 'EQ_CLAUSE_1')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(13))
-			->method('leftJoin')
-			->with('cr', 'calendarobjects', 'co', 'ANDX_CLAUSE')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(14))
-			->method('execute')
-			->with()
-			->willReturn($stmt);
+	public function testInsertReminder(): void {
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(4, $rows);
+
+		$this->reminderBackend->insertReminder(42, 1337, 'uid99', true, 12345678,
+			true, 'hash99', 'hash42', 'AUDIO', false, 12345670, false);
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(5, $rows);
+
+		unset($rows[4]['id']);
+
+		$this->assertEquals($rows[4], [
+			'calendar_id' => '42',
+			'object_id' => '1337',
+			'is_recurring' => '1',
+			'uid' => 'uid99',
+			'recurrence_id' => '12345678',
+			'is_recurrence_exception' => '1',
+			'event_hash' => 'hash99',
+			'alarm_hash' => 'hash42',
+			'type' => 'AUDIO',
+			'is_relative' => '0',
+			'notification_date' => '12345670',
+			'is_repeat_based' => '0',
+		]);
+	}
 
-		$stmt->expects($this->once())
-			->method('fetchAll')
-			->with()
-			->willReturn($dbData);
+	public function testUpdateReminder() {
+		$query = self::$realDatabase->getQueryBuilder();
+		$rows = $query->select('*')
+			->from('calendar_reminders')
+			->execute()
+			->fetchAll();
+
+		$this->assertCount(4, $rows);
 
-		$actual = $this->reminderBackend->getRemindersToProcess();
-		$this->assertEquals($dbData, $actual);
+		$this->assertEquals($rows[3]['notification_date'], 123600);
+
+		$reminderId = (int)  $rows[3]['id'];
+		$newNotificationDate = 123700;
+
+		$this->reminderBackend->updateReminder($reminderId, $newNotificationDate);
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$row = $query->select('notification_date')
+			->from('calendar_reminders')
+			->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
+			->execute()
+			->fetch();
+
+		$this->assertEquals((int) $row['notification_date'], 123700);
 	}
 
-	public function testInsertReminder(): void
-    {
-		/** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
-        $queryBuilder = $this->createMock(IQueryBuilder::class);
-        $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
-		$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
 
-		$this->dbConnection->expects($this->once())
-			->method('getQueryBuilder')
-			->with()
-			->will($this->returnValue($queryBuilder));
-		$queryBuilder->method('expr')
-			->will($this->returnValue($expr));
-
-		$queryBuilder->method('createNamedParameter')
-			->will($this->returnValueMap([
-				['user1', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
-				['1', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
-				['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-3'],
-				['EMAIL', \PDO::PARAM_STR, null, 'createNamedParameter-4'],
-				[1227, \PDO::PARAM_STR, null, 'createNamedParameter-5'],
-				[1337, \PDO::PARAM_STR, null, 'createNamedParameter-6'],
-			]));
-
-		$queryBuilder->expects($this->at(0))
-			->method('insert')
-			->with('calendar_reminders')
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(7))
-			->method('values')
-			->with([
-				'uid' => 'createNamedParameter-1',
-				'calendarid' => 'createNamedParameter-2',
-				'objecturi' => 'createNamedParameter-3',
-				'type' => 'createNamedParameter-4',
-				'notificationdate' => 'createNamedParameter-5',
-				'eventstartdate' => 'createNamedParameter-6',
+	private function createRemindersTestSet(): void {
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendars')
+			->values([
+				'id' => $query->createNamedParameter(1),
+				'principaluri' => $query->createNamedParameter('principals/users/user001'),
+				'displayname' => $query->createNamedParameter('Displayname 123'),
 			])
-			->willReturn($queryBuilder);
-		$queryBuilder->expects($this->at(8))
-			->method('execute')
-			->with()
-			->willReturn($stmt);
-
-		$actual = $this->reminderBackend->insertReminder('user1', '1', 'object.ics', 'EMAIL', 1227, 1337);
-    }
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendars')
+			->values([
+				'id' => $query->createNamedParameter(99),
+				'principaluri' => $query->createNamedParameter('principals/users/user002'),
+				'displayname' => $query->createNamedParameter('Displayname 99'),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendarobjects')
+			->values([
+				'id' => $query->createNamedParameter(1),
+				'calendardata' => $query->createNamedParameter('Calendar data 123'),
+				'calendarid' => $query->createNamedParameter(1),
+				'size' => $query->createNamedParameter(42),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendarobjects')
+			->values([
+				'id' => $query->createNamedParameter(2),
+				'calendardata' => $query->createNamedParameter('Calendar data 456'),
+				'calendarid' => $query->createNamedParameter(1),
+				'size' => $query->createNamedParameter(42),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendarobjects')
+			->values([
+				'id' => $query->createNamedParameter(10),
+				'calendardata' => $query->createNamedParameter('Calendar data 789'),
+				'calendarid' => $query->createNamedParameter(99),
+				'size' => $query->createNamedParameter(42),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendar_reminders')
+			->values([
+				'calendar_id' => $query->createNamedParameter(1),
+				'object_id' => $query->createNamedParameter(1),
+				'uid' => $query->createNamedParameter('asd'),
+				'is_recurring' => $query->createNamedParameter(0),
+				'recurrence_id' => $query->createNamedParameter(123458),
+				'is_recurrence_exception' => $query->createNamedParameter(0),
+				'event_hash' => $query->createNamedParameter('asd123'),
+				'alarm_hash' => $query->createNamedParameter('asd567'),
+				'type' => $query->createNamedParameter('EMAIL'),
+				'is_relative' => $query->createNamedParameter(1),
+				'notification_date' => $query->createNamedParameter(123456),
+				'is_repeat_based' => $query->createNamedParameter(0),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendar_reminders')
+			->values([
+				'calendar_id' => $query->createNamedParameter(1),
+				'object_id' => $query->createNamedParameter(1),
+				'uid' => $query->createNamedParameter('asd'),
+				'is_recurring' => $query->createNamedParameter(0),
+				'recurrence_id' => $query->createNamedParameter(123458),
+				'is_recurrence_exception' => $query->createNamedParameter(0),
+				'event_hash' => $query->createNamedParameter('asd123'),
+				'alarm_hash' => $query->createNamedParameter('asd567'),
+				'type' => $query->createNamedParameter('AUDIO'),
+				'is_relative' => $query->createNamedParameter(1),
+				'notification_date' => $query->createNamedParameter(123456),
+				'is_repeat_based' => $query->createNamedParameter(0),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendar_reminders')
+			->values([
+				'calendar_id' => $query->createNamedParameter(1),
+				'object_id' => $query->createNamedParameter(2),
+				'uid' => $query->createNamedParameter('asd'),
+				'is_recurring' => $query->createNamedParameter(0),
+				'recurrence_id' => $query->createNamedParameter(123900),
+				'is_recurrence_exception' => $query->createNamedParameter(0),
+				'event_hash' => $query->createNamedParameter('asd123'),
+				'alarm_hash' => $query->createNamedParameter('asd567'),
+				'type' => $query->createNamedParameter('EMAIL'),
+				'is_relative' => $query->createNamedParameter(1),
+				'notification_date' => $query->createNamedParameter(123499),
+				'is_repeat_based' => $query->createNamedParameter(0),
+			])
+			->execute();
+
+		$query = self::$realDatabase->getQueryBuilder();
+		$query->insert('calendar_reminders')
+			->values([
+				'calendar_id' => $query->createNamedParameter(99),
+				'object_id' => $query->createNamedParameter(10),
+				'uid' => $query->createNamedParameter('asd'),
+				'is_recurring' => $query->createNamedParameter(0),
+				'recurrence_id' => $query->createNamedParameter(123900),
+				'is_recurrence_exception' => $query->createNamedParameter(0),
+				'event_hash' => $query->createNamedParameter('asd123'),
+				'alarm_hash' => $query->createNamedParameter('asd567'),
+				'type' => $query->createNamedParameter('DISPLAY'),
+				'is_relative' => $query->createNamedParameter(1),
+				'notification_date' => $query->createNamedParameter(123600),
+				'is_repeat_based' => $query->createNamedParameter(0),
+			])
+			->execute();
+	}
 }

+ 26 - 24
apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php → apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AbstractNotificationProviderTest.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2019, Thomas Citharel
  *
@@ -19,9 +20,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  *
  */
-namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
+namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
 
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\AbstractProvider;
 use OCP\IConfig;
 use OCP\IL10N;
 use OCP\ILogger;
@@ -33,23 +34,23 @@ use Sabre\VObject\Component\VCalendar;
 
 abstract class AbstractNotificationProviderTest extends TestCase {
 
-    /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
-    protected $logger;
+	/** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+	protected $logger;
 
-    /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
-    protected $l10nFactory;
+	/** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+	protected $l10nFactory;
 
-    /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
-    protected $l10n;
+	/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+	protected $l10n;
 
-    /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
-    protected $urlGenerator;
+	/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+	protected $urlGenerator;
 
-    /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
-    protected $config;
+	/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+	protected $config;
 
-    /** @var AbstractNotificationProvider|\PHPUnit\Framework\MockObject\MockObject */
-    protected $provider;
+	/** @var AbstractProvider|\PHPUnit\Framework\MockObject\MockObject */
+	protected $provider;
 
 	/**
 	 * @var VCalendar
@@ -69,19 +70,20 @@ abstract class AbstractNotificationProviderTest extends TestCase {
 	public function setUp() {
 		parent::setUp();
 
-        $this->logger = $this->createMock(ILogger::class);
-        $this->l10nFactory = $this->createMock(L10NFactory::class);
-        $this->l10n = $this->createMock(IL10N::class);
-        $this->urlGenerator = $this->createMock(IURLGenerator::class);
-        $this->config = $this->createMock(IConfig::class);
+		$this->logger = $this->createMock(ILogger::class);
+		$this->l10nFactory = $this->createMock(L10NFactory::class);
+		$this->l10n = $this->createMock(IL10N::class);
+		$this->urlGenerator = $this->createMock(IURLGenerator::class);
+		$this->config = $this->createMock(IConfig::class);
 
-        $this->vcalendar = new VCalendar();
+		$this->vcalendar = new VCalendar();
 		$this->vcalendar->add('VEVENT', [
 			'SUMMARY' => 'Fellowship meeting',
-			'DTSTART' => new \DateTime('2017-01-01 00:00:00') // 1483228800
-        ]);
+			'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+			'UID' => 'uid1234',
+		]);
 		$this->calendarDisplayName = 'Personal';
 
-        $this->user = $this->createMock(IUser::class);
-    }
+		$this->user = $this->createMock(IUser::class);
+	}
 }

+ 33 - 0
apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AudioProviderTest.php

@@ -0,0 +1,33 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2019, Georg Ehrke
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider;
+
+class AudioProviderTest extends PushProviderTest {
+
+	public function testNotificationType():void {
+		$this->assertEquals(AudioProvider::NOTIFICATION_TYPE, 'AUDIO');
+	}
+
+}

+ 498 - 178
apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php

@@ -1,8 +1,11 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
  *
  * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
  *
  * @license AGPL-3.0
  *
@@ -22,7 +25,6 @@
 namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
 
 use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
 use OCP\IConfig;
 use OCP\IL10N;
 use OCP\ILogger;
@@ -33,194 +35,512 @@ use OCP\Mail\IEMailTemplate;
 use OCP\Mail\IMailer;
 use OCP\Mail\IAttachment;
 use OCP\Mail\IMessage;
+use Sabre\VObject\Component\VCalendar;
 use Test\TestCase;
-use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
 
 class EmailProviderTest extends AbstractNotificationProviderTest {
 
-    const USER_EMAIL = 'frodo@hobb.it';
+	const USER_EMAIL = 'frodo@hobb.it';
 
-    /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
-    protected $logger;
+	/** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+	protected $logger;
 
-    /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
-    protected $l10nFactory;
+	/** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+	protected $l10nFactory;
 
-    /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
-    protected $l10n;
+	/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+	protected $l10n;
 
-    /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
-    protected $urlGenerator;
+	/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+	protected $urlGenerator;
 
-    /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
-    protected $config;
+	/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+	protected $config;
 
-    /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
+	/** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
 	private $mailer;
 
-    public function setUp() {
-        parent::setUp();
-
-        $this->mailer = $this->createMock(IMailer::class);
-
-        $this->provider = new EmailProvider(
-            $this->config,
-            $this->mailer,
-            $this->logger,
-            $this->l10nFactory,
-            $this->urlGenerator
-        );
-    }
-
-    public function testSendWithNoUserEmail(): void
-    {
-        $this->user->expects($this->once())
-            ->method('getEMailAddress')
-            ->with()
-            ->willReturn(null);
-
-        $this->mailer
-            ->expects($this->never())
-            ->method('send');
-
-        $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
-    }
-
-    public function testSendWithFailedRecipients(): void
-    {
-        $this->user->expects($this->exactly(2))
-            ->method('getEMailAddress')
-            ->with()
-            ->willReturn(self::USER_EMAIL);
-
-        $this->mailer
-            ->expects($this->once())
-            ->method('send')
-            ->willReturn([self::USER_EMAIL])
-        ;
-
-        $this->logger
-            ->expects($this->once())
-            ->method('error');
-
-        $l10n = $this->createMock(IL10N::class);
-        $this->l10nFactory
-            ->method('get')
-            ->willReturn($l10n);
-
-		$this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
-    }
-
-    public function testSendWithMailerFailure(): void
-    {
-        $this->user->expects($this->exactly(2))
-            ->method('getEMailAddress')
-            ->with()
-            ->willReturn(self::USER_EMAIL);
-
-        $ex = new \Exception();
-
-        $this->mailer
-            ->expects($this->once())
-            ->method('send')
-            ->will($this->throwException($ex))
-        ;
-
-        $this->logger
-            ->expects($this->once())
-            ->method('logException')
-            ->with($ex, ['app' => 'dav']);
-
-        $l10n = $this->createMock(IL10N::class);
-        $this->l10nFactory
-            ->method('get')
-            ->willReturn($l10n);
-
-		$this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
-    }
-
-    public function testSend(): void
-    {
-        $this->user->expects($this->exactly(2))
-            ->method('getEMailAddress')
-            ->with()
-            ->willReturn(self::USER_EMAIL);
-
-        $this->user->expects($this->once())
-            ->method('getDisplayName')
-            ->with()
-            ->willReturn('Frodo');
-
-        $this->urlGenerator
-            ->expects($this->exactly(2))
-            ->method('getAbsoluteURL');
-
-        $this->urlGenerator
-            ->expects($this->exactly(2))
-            ->method('imagePath');
-
-        $mailMessage = $this->createMock(IMessage::class);
-        $mailMessage->expects($this->once())
-            ->method('setFrom')
-            ->with([\OCP\Util::getDefaultEmailAddress('invitations-noreply') => 'Nextcloud'])
-            ->willReturn($mailMessage);
-
-        $mailMessage->expects($this->once())
-			->method('setTo')
-            ->with([self::USER_EMAIL => 'Frodo'])
-            ->willReturn($mailMessage);
-
-        $mailMessage
-            ->expects($this->never())
-            ->method('setReplyTo')
-            ->willReturn($mailMessage);
-
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
-        $this->mailer
-            ->expects($this->once())
-            ->method('createEMailTemplate')
-            ->willReturn($emailTemplate);
-
-        $emailTemplate->expects($this->once())
+	public function setUp() {
+		parent::setUp();
+
+		$this->mailer = $this->createMock(IMailer::class);
+
+		$this->provider = new EmailProvider(
+			$this->config,
+			$this->mailer,
+			$this->logger,
+			$this->l10nFactory,
+			$this->urlGenerator
+		);
+	}
+
+	public function testSendWithoutAttendees():void {
+		$user1 = $this->createMock(IUser::class);
+		$user1->method('getUID')
+			->willReturn('uid1');
+		$user1->method('getEMailAddress')
+			->willReturn('uid1@example.com');
+		$user2 = $this->createMock(IUser::class);
+		$user2->method('getUID')
+			->willReturn('uid2');
+		$user2->method('getEMailAddress')
+			->willReturn('uid2@example.com');
+		$user3 = $this->createMock(IUser::class);
+		$user3->method('getUID')
+			->willReturn('uid3');
+		$user3->method('getEMailAddress')
+			->willReturn('uid3@example.com');
+		$user4 = $this->createMock(IUser::class);
+		$user4->method('getUID')
+			->willReturn('uid4');
+		$user4->method('getEMailAddress')
+			->willReturn(null);
+
+		$users = [$user1, $user2, $user3, $user4];
+
+		$this->config->expects($this->at(0))
+			->method('getUserValue')
+			->with('uid1', 'core', 'lang', null)
+			->willReturn(null);
+		$this->config->expects($this->at(1))
+			->method('getUserValue')
+			->with('uid2', 'core', 'lang', null)
+			->willReturn('de');
+		$this->config->expects($this->at(2))
+			->method('getUserValue')
+			->with('uid3', 'core', 'lang', null)
+			->willReturn('de');
+
+		$enL10N = $this->createMock(IL10N::class);
+		$enL10N->method('t')
+			->will($this->returnArgument(0));
+		$enL10N->method('l')
+			->will($this->returnArgument(0));
+
+		$deL10N = $this->createMock(IL10N::class);
+		$deL10N->method('t')
+			->will($this->returnArgument(0));
+		$deL10N->method('l')
+			->will($this->returnArgument(0));
+
+		$this->l10nFactory->expects($this->at(0))
+			->method('findLanguage')
+			->with()
+			->willReturn('en');
+
+		$this->l10nFactory->expects($this->at(1))
+			->method('languageExists')
+			->with('dav', 'en')
+			->willReturn(true);
+
+		$this->l10nFactory->expects($this->at(2))
+			->method('get')
+			->with('dav', 'en')
+			->willReturn($enL10N);
+
+		$this->l10nFactory->expects($this->at(3))
+			->method('languageExists')
+			->with('dav', 'de')
+			->willReturn(true);
+
+		$this->l10nFactory->expects($this->at(4))
+			->method('get')
+			->with('dav', 'de')
+			->willReturn($deL10N);
+
+		$template1 = $this->getTemplateMock();
+		$message11 = $this->getMessageMock('uid1@example.com', $template1);
+		$template2 = $this->getTemplateMock();
+		$message21 = $this->getMessageMock('uid2@example.com', $template2);
+		$message22 = $this->getMessageMock('uid3@example.com', $template2);
+
+		$this->mailer->expects($this->at(0))
+			->method('createEMailTemplate')
+			->with('dav.calendarReminder')
+			->willReturn($template1);
+
+		$this->mailer->expects($this->at(1))
+			->method('createMessage')
+			->with()
+			->willReturn($message11);
+		$this->mailer->expects($this->at(2))
+			->method('send')
+			->with($message11)
+			->willReturn([]);
+
+		$this->mailer->expects($this->at(3))
+			->method('createEMailTemplate')
+			->with('dav.calendarReminder')
+			->willReturn($template2);
+
+		$this->mailer->expects($this->at(4))
+			->method('createMessage')
+			->with()
+			->willReturn($message21);
+		$this->mailer->expects($this->at(5))
+			->method('send')
+			->with($message21)
+			->willReturn([]);
+		$this->mailer->expects($this->at(6))
+			->method('createMessage')
+			->with()
+			->willReturn($message22);
+		$this->mailer->expects($this->at(7))
+			->method('send')
+			->with($message22)
+			->willReturn([]);
+
+		$this->setupURLGeneratorMock(2);
+
+		$vcalendar = $this->getNoAttendeeVCalendar();
+		$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
+	}
+
+	public function testSendWithAttendees(): void {
+		$user1 = $this->createMock(IUser::class);
+		$user1->method('getUID')
+			->willReturn('uid1');
+		$user1->method('getEMailAddress')
+			->willReturn('uid1@example.com');
+		$user2 = $this->createMock(IUser::class);
+		$user2->method('getUID')
+			->willReturn('uid2');
+		$user2->method('getEMailAddress')
+			->willReturn('uid2@example.com');
+		$user3 = $this->createMock(IUser::class);
+		$user3->method('getUID')
+			->willReturn('uid3');
+		$user3->method('getEMailAddress')
+			->willReturn('uid3@example.com');
+		$user4 = $this->createMock(IUser::class);
+		$user4->method('getUID')
+			->willReturn('uid4');
+		$user4->method('getEMailAddress')
+			->willReturn(null);
+
+		$users = [$user1, $user2, $user3, $user4];
+
+		$this->config->expects($this->at(0))
+			->method('getUserValue')
+			->with('uid1', 'core', 'lang', null)
+			->willReturn(null);
+		$this->config->expects($this->at(1))
+			->method('getUserValue')
+			->with('uid2', 'core', 'lang', null)
+			->willReturn('de');
+		$this->config->expects($this->at(2))
+			->method('getUserValue')
+			->with('uid3', 'core', 'lang', null)
+			->willReturn('de');
+
+		$enL10N = $this->createMock(IL10N::class);
+		$enL10N->method('t')
+			->will($this->returnArgument(0));
+		$enL10N->method('l')
+			->will($this->returnArgument(0));
+
+		$deL10N = $this->createMock(IL10N::class);
+		$deL10N->method('t')
+			->will($this->returnArgument(0));
+		$deL10N->method('l')
+			->will($this->returnArgument(0));
+
+		$this->l10nFactory->expects($this->at(0))
+			->method('findLanguage')
+			->with()
+			->willReturn('en');
+
+		$this->l10nFactory->expects($this->at(1))
+			->method('languageExists')
+			->with('dav', 'de')
+			->willReturn(true);
+
+		$this->l10nFactory->expects($this->at(2))
+			->method('get')
+			->with('dav', 'de')
+			->willReturn($enL10N);
+
+		$this->l10nFactory->expects($this->at(3))
+			->method('languageExists')
+			->with('dav', 'en')
+			->willReturn(true);
+
+		$this->l10nFactory->expects($this->at(4))
+			->method('get')
+			->with('dav', 'en')
+			->willReturn($deL10N);
+
+		$template1 = $this->getTemplateMock();
+		$message11 = $this->getMessageMock('foo1@example.org', $template1);
+		$message12 = $this->getMessageMock('uid2@example.com', $template1);
+		$message13 = $this->getMessageMock('uid3@example.com', $template1);
+		$template2 = $this->getTemplateMock();
+		$message21 = $this->getMessageMock('foo3@example.org', $template2);
+		$message22 = $this->getMessageMock('foo4@example.org', $template2);
+		$message23 = $this->getMessageMock('uid1@example.com', $template2);
+
+		$this->mailer->expects($this->at(0))
+			->method('createEMailTemplate')
+			->with('dav.calendarReminder')
+			->willReturn($template1);
+
+		$this->mailer->expects($this->at(1))
+			->method('createMessage')
+			->with()
+			->willReturn($message11);
+		$this->mailer->expects($this->at(2))
+			->method('send')
+			->with($message11)
+			->willReturn([]);
+		$this->mailer->expects($this->at(3))
+			->method('createMessage')
+			->with()
+			->willReturn($message12);
+		$this->mailer->expects($this->at(4))
+			->method('send')
+			->with($message12)
+			->willReturn([]);
+		$this->mailer->expects($this->at(5))
+			->method('createMessage')
+			->with()
+			->willReturn($message13);
+		$this->mailer->expects($this->at(6))
+			->method('send')
+			->with($message13)
+			->willReturn([]);
+
+		$this->mailer->expects($this->at(7))
+			->method('createEMailTemplate')
+			->with('dav.calendarReminder')
+			->willReturn($template2);
+
+		$this->mailer->expects($this->at(8))
+			->method('createMessage')
+			->with()
+			->willReturn($message21);
+		$this->mailer->expects($this->at(9))
+			->method('send')
+			->with($message21)
+			->willReturn([]);
+		$this->mailer->expects($this->at(10))
+			->method('createMessage')
+			->with()
+			->willReturn($message22);
+		$this->mailer->expects($this->at(11))
+			->method('send')
+			->with($message22)
+			->willReturn([]);
+		$this->mailer->expects($this->at(12))
+			->method('createMessage')
+			->with()
+			->willReturn($message23);
+		$this->mailer->expects($this->at(13))
+			->method('send')
+			->with($message23)
+			->willReturn([]);
+
+		$this->setupURLGeneratorMock(2);
+
+		$vcalendar = $this->getAttendeeVCalendar();
+		$this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
+	}
+
+	/**
+	 * @return IEMailTemplate
+	 */
+	private function getTemplateMock():IEMailTemplate {
+		$template = $this->createMock(IEMailTemplate::class);
+
+		$template->expects($this->at(0))
+			->method('addHeader')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(1))
 			->method('setSubject')
-            ->with('Notification: Fellowship meeting');
-
-        $emailTemplate->expects($this->once())
-            ->method('addHeader');
-
-        $emailTemplate->expects($this->once())
-            ->method('addHeading');
-
-        $emailTemplate->expects($this->exactly(2))
-            ->method('addBodyListItem');
-
-        $emailTemplate->expects($this->once())
-            ->method('addFooter');
-
-        $mailMessage->expects($this->once())
-            ->method('useTemplate')
-            ->with($emailTemplate);
-
-        $this->mailer
-            ->expects($this->once())
-            ->method('createMessage')
-            ->willReturn($mailMessage);
-
-        $emailAttachment = $this->createMock(IAttachment::class);
-        $this->mailer
-            ->expects($this->once())
-            ->method('createAttachment')
-            ->willReturn($emailAttachment);
-
-        $this->mailer
-            ->expects($this->once())
-            ->method('send');
-
-		$l10n = $this->createMock(IL10N::class);
-        $this->l10nFactory
-            ->method('get')
-            ->willReturn($l10n);
-
-		$this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
-    }
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(2))
+			->method('addHeading')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(3))
+			->method('addBodyListItem')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(4))
+			->method('addBodyListItem')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(5))
+			->method('addBodyListItem')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(6))
+			->method('addBodyListItem')
+			->with()
+			->willReturn($template);
+
+		$template->expects($this->at(7))
+			->method('addFooter')
+			->with()
+			->willReturn($template);
+
+		return $template;
+	}
+
+	/**
+	 * @param array $toMail
+	 * @param IEMailTemplate $templateMock
+	 * @param array $replyTo
+	 * @return IMessage
+	 */
+	private function getMessageMock(string $toMail, IEMailTemplate $templateMock, array $replyTo=null):IMessage {
+		$message = $this->createMock(IMessage::class);
+		$i = 0;
+
+		$message->expects($this->at($i++))
+			->method('setFrom')
+			->with([\OCP\Util::getDefaultEmailAddress('reminders-noreply')])
+			->willReturn($message);
+
+		if ($replyTo) {
+			$message->expects($this->at($i++))
+				->method('setReplyTo')
+				->with($replyTo)
+				->willReturn($message);
+		}
+
+		$message->expects($this->at($i++))
+			->method('setTo')
+			->with([$toMail])
+			->willReturn($message);
+
+		$message->expects($this->at($i++))
+			->method('useTemplate')
+			->with($templateMock)
+			->willReturn($message);
+
+		return $message;
+	}
+
+	private function getNoAttendeeVCalendar():VCalendar {
+		$vcalendar = new VCalendar();
+		$vcalendar->add('VEVENT', [
+			'SUMMARY' => 'Fellowship meeting',
+			'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+			'UID' => 'uid1234',
+			'LOCATION' => 'Location 123',
+			'DESCRIPTION' => 'DESCRIPTION 456',
+		]);
+
+		return $vcalendar;
+	}
+
+	private function getAttendeeVCalendar():VCalendar {
+		$vcalendar = new VCalendar();
+		$vcalendar->add('VEVENT', [
+			'SUMMARY' => 'Fellowship meeting',
+			'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+			'UID' => 'uid1234',
+			'LOCATION' => 'Location 123',
+			'DESCRIPTION' => 'DESCRIPTION 456',
+		]);
+
+		$vcalendar->VEVENT->add(
+			'ATTENDEE',
+			'mailto:foo1@example.org',
+			[
+				'LANG' => 'de',
+				'PARTSTAT' => 'NEEDS-ACTION',
+			]
+		);
+
+		$vcalendar->VEVENT->add(
+			'ATTENDEE',
+			'mailto:foo2@example.org',
+			[
+				'LANG' => 'de',
+				'PARTSTAT' => 'DECLINED',
+			]
+		);
+
+		$vcalendar->VEVENT->add(
+			'ATTENDEE',
+			'mailto:foo3@example.org',
+			[
+				'LANG' => 'en',
+				'PARTSTAT' => 'CONFIRMED',
+			]
+		);
+
+		$vcalendar->VEVENT->add(
+			'ATTENDEE',
+			'mailto:foo4@example.org'
+		);
+
+		$vcalendar->VEVENT->add(
+			'ATTENDEE',
+			'tomail:foo5@example.org'
+		);
+
+		return $vcalendar;
+	}
+
+	private function setupURLGeneratorMock(int $times=1):void {
+		for ($i = 0; $i < $times; $i++) {
+			$this->urlGenerator
+				->expects($this->at(8 * $i))
+				->method('imagePath')
+				->with('core', 'actions/info.svg')
+				->willReturn('imagePath1');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 1))
+				->method('getAbsoluteURL')
+				->with('imagePath1')
+				->willReturn('AbsURL1');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 2))
+				->method('imagePath')
+				->with('core', 'places/calendar.svg')
+				->willReturn('imagePath2');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 3))
+				->method('getAbsoluteURL')
+				->with('imagePath2')
+				->willReturn('AbsURL2');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 4))
+				->method('imagePath')
+				->with('core', 'actions/address.svg')
+				->willReturn('imagePath3');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 5))
+				->method('getAbsoluteURL')
+				->with('imagePath3')
+				->willReturn('AbsURL3');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 6))
+				->method('imagePath')
+				->with('core', 'actions/more.svg')
+				->willReturn('imagePath4');
+
+			$this->urlGenerator
+				->expects($this->at(8 * $i + 7))
+				->method('getAbsoluteURL')
+				->with('imagePath4')
+				->willReturn('AbsURL4');
+		}
+	}
 }

+ 107 - 60
apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php

@@ -1,8 +1,11 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
  *
  * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
  *
  * @license AGPL-3.0
  *
@@ -23,7 +26,6 @@ namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
 
 use OCA\DAV\AppInfo\Application;
 use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
 use OCP\IConfig;
 use OCP\IL10N;
 use OCP\ILogger;
@@ -34,7 +36,6 @@ use OCP\Notification\IManager;
 use OCP\Notification\INotification;
 use OCP\AppFramework\Utility\ITimeFactory;
 use Test\TestCase;
-use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
 
 class PushProviderTest extends AbstractNotificationProviderTest {
 
@@ -75,65 +76,111 @@ class PushProviderTest extends AbstractNotificationProviderTest {
         );
     }
 
-    public function testSend(): void
-    {
-        $notification = $this->createMock(INotification::class);
-        $notification
-            ->expects($this->once())
-            ->method('setApp')
-            ->with(Application::APP_ID)
-            ->willReturn($notification);
-
-        $notification
-            ->expects($this->once())
-            ->method('setUser')
-            ->willReturn($notification)
-        ;
-
-        $notification
-            ->expects($this->once())
-            ->method('setDateTime')
-            ->willReturn($notification)
-        ;
-
-        $notification
-            ->expects($this->once())
-            ->method('setObject')
-            ->willReturn($notification)
-        ;
-
-        $notification
-            ->expects($this->once())
-            ->method('setSubject')
-            ->willReturn($notification)
-        ;
-
-        $notification
-            ->expects($this->once())
-            ->method('setMessage')
-            ->willReturn($notification)
-        ;
-
-        $this->manager
-            ->expects($this->once())
-            ->method('createNotification')
-            ->willReturn($notification);
-
-        $this->manager
-            ->expects($this->once())
-            ->method('notify')
-            ->with($notification);
-
-        $l10n = $this->createMock(IL10N::class);
-        $this->l10nFactory
-            ->method('get')
-            ->willReturn($l10n);
-
-        $this->timeFactory->expects($this->once())
-			->method('getDateTime')
+    public function testNotificationType():void  {
+    	$this->assertEquals(PushProvider::NOTIFICATION_TYPE, 'DISPLAY');
+	}
+
+    public function testSend(): void {
+    	$user1 = $this->createMock(IUser::class);
+    	$user1->method('getUID')
+			->willReturn('uid1');
+    	$user2 = $this->createMock(IUser::class);
+		$user2->method('getUID')
+			->willReturn('uid2');
+    	$user3 = $this->createMock(IUser::class);
+		$user3->method('getUID')
+			->willReturn('uid3');
+
+		$users = [$user1, $user2, $user3];
+
+		$dateTime = new \DateTime('@946684800');
+		$this->timeFactory->method('getDateTime')
 			->with()
-			->willReturn(new \DateTime());
+			->willReturn($dateTime);
 
-		$this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+		$notification1 = $this->createNotificationMock('uid1', $dateTime);
+		$notification2 = $this->createNotificationMock('uid2', $dateTime);
+		$notification3 = $this->createNotificationMock('uid3', $dateTime);
+
+		$this->manager->expects($this->at(0))
+			->method('createNotification')
+			->with()
+			->willReturn($notification1);
+		$this->manager->expects($this->at(2))
+			->method('createNotification')
+			->with()
+			->willReturn($notification2);
+		$this->manager->expects($this->at(4))
+			->method('createNotification')
+			->with()
+			->willReturn($notification3);
+
+		$this->manager->expects($this->at(1))
+			->method('notify')
+			->with($notification1);
+		$this->manager->expects($this->at(3))
+			->method('notify')
+			->with($notification2);
+		$this->manager->expects($this->at(5))
+			->method('notify')
+			->with($notification3);
+
+		$this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, $users);
     }
+
+	/**
+	 * @param string $uid
+	 * @param \DateTime $dt
+	 */
+    private function createNotificationMock(string $uid, \DateTime $dt):INotification {
+		$notification = $this->createMock(INotification::class);
+		$notification
+			->expects($this->once())
+			->method('setApp')
+			->with('dav')
+			->willReturn($notification);
+
+		$notification->expects($this->once())
+			->method('setUser')
+			->with($uid)
+			->willReturn($notification);
+
+		$notification->expects($this->once())
+			->method('setDateTime')
+			->with($dt)
+			->willReturn($notification);
+
+		$notification->expects($this->once())
+			->method('setObject')
+			->with('dav', 'uid1234')
+			->willReturn($notification);
+
+		$notification->expects($this->once())
+			->method('setSubject')
+			->with('calendar_reminder', [
+				'title' => 'Fellowship meeting',
+				'start_atom' => '2017-01-01T00:00:00+00:00',
+			])
+			->willReturn($notification);
+
+		$notification
+			->expects($this->once())
+			->method('setMessage')
+			->with('calendar_reminder', [
+				'title' => 'Fellowship meeting',
+				'start_atom' => '2017-01-01T00:00:00+00:00',
+				'description' => null,
+				'location' => null,
+				'all_day' => false,
+				'start_is_floating' => false,
+				'start_timezone' => 'UTC',
+				'end_atom' => '2017-01-01T00:00:00+00:00',
+				'end_is_floating' => false,
+				'end_timezone' => 'UTC',
+				'calendar_displayname' => 'Personal',
+			])
+			->willReturn($notification);
+
+		return $notification;
+	}
 }

+ 25 - 31
apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php

@@ -1,8 +1,11 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
  *
  * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
  *
  * @license AGPL-3.0
  *
@@ -31,8 +34,8 @@ use Test\TestCase;
 
 class NotificationProviderManagerTest extends TestCase {
 
-    /** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
-    private $providerManager;
+	/** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
+	private $providerManager;
 
 	/**
 	 * @throws \OCP\AppFramework\QueryException
@@ -40,9 +43,9 @@ class NotificationProviderManagerTest extends TestCase {
 	public function setUp() {
 		parent::setUp();
 
-        $this->providerManager = new NotificationProviderManager();
-        $this->providerManager->registerProvider(EmailProvider::class);
-    }
+		$this->providerManager = new NotificationProviderManager();
+		$this->providerManager->registerProvider(EmailProvider::class);
+	}
 
 	/**
 	 * @expectedException OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
@@ -50,10 +53,9 @@ class NotificationProviderManagerTest extends TestCase {
 	 * @throws ProviderNotAvailableException
 	 * @throws NotificationTypeDoesNotExistException
 	 */
-    public function testGetProviderForUnknownType(): void
-    {
-        $this->providerManager->getProvider('NOT EXISTENT');
-    }
+	public function testGetProviderForUnknownType(): void{
+		$this->providerManager->getProvider('NOT EXISTENT');
+	}
 
 	/**
 	 * @expectedException OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
@@ -61,28 +63,16 @@ class NotificationProviderManagerTest extends TestCase {
 	 * @throws NotificationTypeDoesNotExistException
 	 * @throws ProviderNotAvailableException
 	 */
-    public function testGetProviderForUnRegisteredType(): void
-    {
-        $this->providerManager->getProvider('AUDIO');
-    }
+	public function testGetProviderForUnRegisteredType(): void{
+		$this->providerManager->getProvider('AUDIO');
+	}
 
-	/**
-	 * @throws NotificationTypeDoesNotExistException
-	 * @throws ProviderNotAvailableException
-	 */
-	public function testGetProvider(): void
-    {
-        $provider = $this->providerManager->getProvider('EMAIL');
-        $this->assertInstanceOf(EmailProvider::class, $provider);
-    }
+	public function testGetProvider(): void{
+		$provider = $this->providerManager->getProvider('EMAIL');
+		$this->assertInstanceOf(EmailProvider::class, $provider);
+	}
 
-	/**
-	 * @throws NotificationTypeDoesNotExistException
-	 * @throws ProviderNotAvailableException
-	 * @throws \OCP\AppFramework\QueryException
-	 */
-	public function testRegisterProvider(): void
-	{
+	public function testRegisterProvider(): void{
 		$this->providerManager->registerProvider(PushProvider::class);
 		$provider = $this->providerManager->getProvider('DISPLAY');
 		$this->assertInstanceOf(PushProvider::class, $provider);
@@ -93,8 +83,12 @@ class NotificationProviderManagerTest extends TestCase {
 	 * @expectedException \InvalidArgumentException
 	 * @throws \OCP\AppFramework\QueryException
 	 */
-	public function testRegisterBadProvider(): void
-	{
+	public function testRegisterBadProvider(): void{
 		$this->providerManager->registerProvider(Capabilities::class);
 	}
+
+	public function testHasProvider(): void {
+		$this->assertTrue($this->providerManager->hasProvider('EMAIL'));
+		$this->assertFalse($this->providerManager->hasProvider('EMAIL123'));
+	}
 }

+ 50 - 14
apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php

@@ -1,8 +1,11 @@
 <?php
+declare(strict_types=1);
 /**
- * @copyright Copyright (c) 2019 Thomas Citharel <tcit@tcit.fr>
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
  *
  * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
  *
  * @license AGPL-3.0
  *
@@ -24,6 +27,7 @@ namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
 
 use OCA\DAV\AppInfo\Application;
 use OCA\DAV\CalDAV\Reminder\Notifier;
+use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\IL10N;
 use OCP\IURLGenerator;
 use OCP\L10N\IFactory;
@@ -36,22 +40,33 @@ class NotifierTest extends TestCase {
 
 	/** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */
 	protected $factory;
+
 	/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
 	protected $urlGenerator;
+
 	/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
-	protected $l;
+	protected $l10n;
+
+	/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+	protected $timeFactory;
 
 	protected function setUp() {
 		parent::setUp();
 
 		$this->urlGenerator = $this->createMock(IURLGenerator::class);
-		$this->l = $this->createMock(IL10N::class);
-		$this->l->expects($this->any())
+		$this->l10n = $this->createMock(IL10N::class);
+		$this->l10n->expects($this->any())
 			->method('t')
 			->willReturnCallback(function($string, $args) {
 				return vsprintf($string, $args);
 			});
-		$this->l->expects($this->any())
+		$this->l10n->expects($this->any())
+			->method('l')
+			->willReturnCallback(function($string, $args) {
+				/** \DateTime $args */
+				return $args->format(\DateTime::ATOM);
+			});
+		$this->l10n->expects($this->any())
 			->method('n')
 			->willReturnCallback(function($textSingular, $textPlural, $count, $args) {
 				$text = $count === 1 ? $textSingular : $textPlural;
@@ -61,14 +76,28 @@ class NotifierTest extends TestCase {
 		$this->factory = $this->createMock(IFactory::class);
 		$this->factory->expects($this->any())
 			->method('get')
-			->willReturn($this->l);
+			->willReturn($this->l10n);
+
+		$this->timeFactory = $this->createMock(ITimeFactory::class);
+		$this->timeFactory
+			->method('getDateTime')
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2005-08-15T14:00:00+02:00'));
 
 		$this->notifier = new Notifier(
 			$this->factory,
-			$this->urlGenerator
+			$this->urlGenerator,
+			$this->timeFactory
 		);
 	}
 
+	public function testGetId():void {
+		$this->assertEquals($this->notifier->getID(), 'dav');
+	}
+
+	public function testGetName():void {
+		$this->assertEquals($this->notifier->getName(), 'Calendar');
+	}
+
 	/**
 	 * @expectedException \InvalidArgumentException
 	 * @expectedExceptionMessage Notification not from this app
@@ -111,17 +140,24 @@ class NotifierTest extends TestCase {
 			[
 				'calendar_reminder',
 				[
-					'title' => 'foo',
-					'start' => time() - 60 * 60 * 24
+					'title' => 'Title of this event',
+					'start_atom' => '2005-08-15T15:52:01+02:00'
 				],
-				'foo (one day ago)',
+				'Title of this event (in 1 hour, 52 minutes)',
 				[
-					'when' => 'foo',
-					'description' => 'bar',
+					'title' => 'Title of this event',
+					'description' => null,
 					'location' => 'NC Headquarters',
-					'calendar' => 'Personal'
+					'all_day' => false,
+					'start_atom' => '2005-08-15T15:52:01+02:00',
+					'start_is_floating' => false,
+					'start_timezone' => 'Europe/Berlin',
+					'end_atom' => '2005-08-15T17:52:01+02:00',
+					'end_is_floating' => false,
+					'end_timezone' => 'Europe/Berlin',
+					'calendar_displayname' => 'Personal',
 				],
-				'Calendar: Personal<br>Date: foo<br>Description: bar<br>Where: NC Headquarters'
+				"Calendar: Personal\r\nDate: 2005-08-15T15:52:01+02:00, 2005-08-15T15:52:01+02:00 - 2005-08-15T17:52:01+02:00 (Europe/Berlin)\r\nWhere: NC Headquarters"
 			],
 		];
 	}

+ 453 - 174
apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2019, Thomas Citharel
  *
@@ -21,12 +22,15 @@
  */
 namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
 
+use OCA\DAV\CalDAV\CalDavBackend;
 use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
 use OCA\DAV\CalDAV\Reminder\Backend;
+use OCA\DAV\CalDAV\Reminder\INotificationProvider;
 use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
 use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
 use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
 use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\IGroup;
 use OCP\IGroupManager;
 use OCP\IUser;
@@ -51,6 +55,15 @@ class ReminderServiceTest extends TestCase {
 	/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
 	private $userSession;
 
+	/** @var CalDavBackend|\PHPUnit\Framework\MockObject\MockObject */
+	private $caldavBackend;
+
+	/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject  */
+	private $timeFactory;
+
+	/** @var ReminderService */
+	private $reminderService;
+
 	public const CALENDAR_DATA = <<<EOD
 BEGIN:VCALENDAR
 PRODID:-//Nextcloud calendar v1.6.4
@@ -68,6 +81,87 @@ BEGIN:VALARM
 ACTION:EMAIL
 TRIGGER:-PT15M
 END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DATE-TIME:20160608T000000Z
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+EOD;
+
+	public const CALENDAR_DATA_REPEAT = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//Nextcloud calendar v1.6.4
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:20160609
+DTEND;TZID=Europe/Berlin;VALUE=DATE:20160610
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-PT15M
+REPEAT:4
+DURATION:PT2M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+EOD;
+
+	public const CALENDAR_DATA_RECURRING = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//Nextcloud calendar v1.6.4
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:20160609
+DTEND;TZID=Europe/Berlin;VALUE=DATE:20160610
+RRULE:FREQ=WEEKLY
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-PT15M
+END:VALARM
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-P8D
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+EOD;
+
+	public const CALENDAR_DATA_RECURRING_REPEAT = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//Nextcloud calendar v1.6.4
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:20160609
+DTEND;TZID=Europe/Berlin;VALUE=DATE:20160610
+RRULE:FREQ=WEEKLY
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-PT15M
+REPEAT:4
+DURATION:PT2M
+END:VALARM
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-P8D
+END:VALARM
 END:VEVENT
 END:VCALENDAR
 EOD;
@@ -79,198 +173,383 @@ EOD;
         $this->notificationProviderManager = $this->createMock(NotificationProviderManager::class);
         $this->userManager = $this->createMock(IUserManager::class);
 		$this->groupManager = $this->createMock(IGroupManager::class);
-		$this->userSession = $this->createMock(IUserSession::class);
-    }
+		$this->caldavBackend = $this->createMock(CalDavBackend::class);
+		$this->timeFactory = $this->createMock(ITimeFactory::class);
 
-    public function dataTestProcessReminders(): array
-    {
-        return [
-            [
-                [], null
-            ],
-            [
-                [
-                    [
-                        'calendardata' => self::CALENDAR_DATA,
-						'displayname' => 'Personal',
-                        'type' => 'EMAIL',
-                        'uid' => 1,
-                        'id' => 1,
-                    ],
-                ],
-                $this->createMock(EmailProvider::class),
-            ],
-            [
-                [
-                    [
-                        'calendardata' => self::CALENDAR_DATA,
-						'displayname' => 'Personal',
-                        'type' => 'DISPLAY',
-                        'uid' => 1,
-                        'id' => 1,
-                    ],
-                ],
-                $this->createMock(PushProvider::class),
-            ]
-        ];
-    }
+		$this->caldavBackend->method('getShares')->willReturn([]);
 
-	/**
-	 * @dataProvider dataTestProcessReminders
-	 * @param array $reminders
-	 * @param AbstractNotificationProvider|null $notificationProvider
-	 * @throws \OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
-	 * @throws \OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
-	 * @throws \OC\User\NoUserException
-	 */
-    public function testProcessReminders(array $reminders, ?AbstractNotificationProvider $notificationProvider): void
-    {
-        $user = $this->createMock(IUser::class);
-
-        $this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn($reminders);
-        if (count($reminders) > 0) {
-            $this->userManager->expects($this->exactly(count($reminders)))->method('get')->willReturn($user);
-            $this->backend->expects($this->exactly(count($reminders)))->method('removeReminder');
-            $this->notificationProviderManager->expects($this->exactly(count($reminders)))->method('getProvider')->willReturn($notificationProvider);
-        }
-
-        $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
-        $reminderService->processReminders();
+		$this->reminderService = new ReminderService($this->backend,
+			$this->notificationProviderManager,
+			$this->userManager,
+			$this->groupManager,
+			$this->caldavBackend,
+			$this->timeFactory);
     }
 
-	/**
-	 * @expectedException OC\User\NoUserException
-	 */
-    public function testProcessReminderWithBadUser(): void
-	{
-		$this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn([
-			[
-				'calendardata' => self::CALENDAR_DATA,
-				'type' => 'DISPLAY',
-				'uid' => 1,
-				'id' => 1,
-			]
-		]);
-		$this->userManager->expects($this->once())->method('get')->with(1)->willReturn(null);
-		$reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
-		$reminderService->processReminders();
+	public function testOnCalendarObjectDelete():void {
+    	$this->backend->expects($this->once())
+			->method('cleanRemindersForEvent')
+			->with(44);
+
+    	$action = '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject';
+		$objectData = [
+			'id' => '44',
+			'component' => 'vevent',
+		];
+
+		$this->reminderService->onTouchCalendarObject($action, $objectData);
 	}
 
-	public function providesTouchCalendarObject(): array
-	{
-		return [
-			[
-				'\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject',
-				[
-					'principaluri' => 'principals/users/personal'
-				],
-				[],
-				[
-					'calendarid' => 1,
-					'uri' => 'something.ics',
-				],
-				0
-			],
-			[
-				'\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
-				[
-					'principaluri' => 'principals/users/personal'
-				],
-				[],
-				[
-					'calendarid' => 1,
-					'uri' => 'something.ics',
-					'calendardata' => self::CALENDAR_DATA
-				],
-				0
-			],
-			[
-				'\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject',
-				[
-					'principaluri' => 'principals/users/someone',
-					'uri' => 'personal'
-				],
+	public function testOnCalendarObjectCreateSingleEntry():void {
+		$action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+    	$objectData = [
+    		'calendardata' => self::CALENDAR_DATA,
+			'id' => '42',
+			'calendarid' => '1337',
+			'component' => 'vevent',
+		];
+
+    	$this->backend->expects($this->exactly(2))
+			->method('insertReminder')
+			->withConsecutive(
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'de919af7429d3b5c11e8b9d289b411a6', 'EMAIL', true, 1465429500, false],
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', '35b3eae8e792aa2209f0b4e1a302f105', 'DISPLAY', false, 1465344000, false]
+			)
+			->willReturn(1);
+
+    	$this->timeFactory->expects($this->once())
+			->method('getDateTime')
+			->with()
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
+
+    	$this->reminderService->onTouchCalendarObject($action, $objectData);
+	}
+
+	public function testOnCalendarObjectCreateSingleEntryWithRepeat(): void {
+		$action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+		$objectData = [
+			'calendardata' => self::CALENDAR_DATA_REPEAT,
+			'id' => '42',
+			'calendarid' => '1337',
+			'component' => 'vevent',
+		];
+
+		$this->backend->expects($this->exactly(5))
+			->method('insertReminder')
+			->withConsecutive(
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429500, false],
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429620, true],
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429740, true],
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429860, true],
+				[1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429980, true]
+			)
+			->willReturn(1);
+
+		$this->timeFactory->expects($this->once())
+			->method('getDateTime')
+			->with()
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
+
+		$this->reminderService->onTouchCalendarObject($action, $objectData);
+	}
+
+	public function testOnCalendarObjectCreateRecurringEntry(): void {
+		$action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+		$objectData = [
+			'calendardata' => self::CALENDAR_DATA_RECURRING,
+			'id' => '42',
+			'calendarid' => '1337',
+			'component' => 'vevent',
+		];
+
+		$this->backend->expects($this->exactly(2))
+			->method('insertReminder')
+			->withConsecutive(
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'de919af7429d3b5c11e8b9d289b411a6', 'EMAIL', true, 1467243900, false],
+				[1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467158400, false]
+			)
+			->willReturn(1);
+
+		$this->timeFactory->expects($this->once())
+			->method('getDateTime')
+			->with()
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-29T00:00:00+00:00'));
+
+		$this->reminderService->onTouchCalendarObject($action, $objectData);
+	}
+
+	public function testOnCalendarObjectCreateRecurringEntryWithRepeat():void {
+		$action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+		$objectData = [
+			'calendardata' => self::CALENDAR_DATA_RECURRING_REPEAT,
+			'id' => '42',
+			'calendarid' => '1337',
+			'component' => 'vevent',
+		];
+
+		$this->backend->expects($this->exactly(6))
+			->method('insertReminder')
+			->withConsecutive(
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467243900, false],
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244020, true],
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244140, true],
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244260, true],
+				[1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244380, true],
+				[1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467158400, false]
+			)
+			->willReturn(1);
+
+		$this->timeFactory->expects($this->once())
+			->method('getDateTime')
+			->with()
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-29T00:00:00+00:00'));
+
+		$this->reminderService->onTouchCalendarObject($action, $objectData);
+	}
+
+	public function testProcessReminders():void {
+		$this->backend->expects($this->at(0))
+			->method('getRemindersToProcess')
+			->with()
+			->willReturn([
 				[
-					[
-						'{http://owncloud.org/ns}principal' => 'principals/users/someone'
-					]
+					'id' => 1,
+					'calendar_id' => 1337,
+					'object_id' => 42,
+					'uid' => 'wej2z68l9h',
+					'is_recurring' => false,
+					'recurrence_id' => 1465430400,
+					'is_recurrence_exception' => false,
+					'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+					'alarm_hash' => 'de919af7429d3b5c11e8b9d289b411a6',
+					'type' => 'EMAIL',
+					'is_relative' => true,
+					'notification_date' => 1465429500,
+					'is_repeat_based' => false,
+					'calendardata' => self::CALENDAR_DATA,
+					'displayname' => 'Displayname 123',
+					'principaluri' => 'principals/users/user001',
 				],
 				[
-					'calendarid' => 1,
-					'uri' => 'something.ics',
-					'calendardata' => self::CALENDAR_DATA
+					'id' => 2,
+					'calendar_id' => 1337,
+					'object_id' => 42,
+					'uid' => 'wej2z68l9h',
+					'is_recurring' => false,
+					'recurrence_id' => 1465430400,
+					'is_recurrence_exception' => false,
+					'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+					'alarm_hash' => 'ecacbf07d413c3c78d1ac7ad8c469602',
+					'type' => 'EMAIL',
+					'is_relative' => true,
+					'notification_date' => 1465429740,
+					'is_repeat_based' => true,
+					'calendardata' => self::CALENDAR_DATA_REPEAT,
+					'displayname' => 'Displayname 123',
+					'principaluri' => 'principals/users/user001',
 				],
-				0
-			],
-			[
-				'\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
 				[
-					'principaluri' => 'principals/users/someone',
-					'uri' => 'personal'
+					'id' => 3,
+					'calendar_id' => 1337,
+					'object_id' => 42,
+					'uid' => 'wej2z68l9h',
+					'is_recurring' => false,
+					'recurrence_id' => 1465430400,
+					'is_recurrence_exception' => false,
+					'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+					'alarm_hash' => '35b3eae8e792aa2209f0b4e1a302f105',
+					'type' => 'DISPLAY',
+					'is_relative' => false,
+					'notification_date' => 1465344000,
+					'is_repeat_based' => false,
+					'calendardata' =>  self::CALENDAR_DATA,
+					'displayname' => 'Displayname 123',
+					'principaluri' => 'principals/users/user001',
 				],
 				[
-					[
-						'{http://owncloud.org/ns}principal' => 'principals/groups/somegroup'
-					]
+					'id' => 4,
+					'calendar_id' => 1337,
+					'object_id' => 42,
+					'uid' => 'wej2z68l9h',
+					'is_recurring' => true,
+					'recurrence_id' => 1467244800,
+					'is_recurrence_exception' => false,
+					'event_hash' => 'fbdb2726bc0f7dfacac1d881c1453e20',
+					'alarm_hash' => 'ecacbf07d413c3c78d1ac7ad8c469602',
+					'type' => 'EMAIL',
+					'is_relative' => true,
+					'notification_date' => 1467243900,
+					'is_repeat_based' => false,
+					'calendardata' => self::CALENDAR_DATA_RECURRING_REPEAT,
+					'displayname' => 'Displayname 123',
+					'principaluri' => 'principals/users/user001',
 				],
 				[
-					'calendarid' => 1,
-					'uri' => 'something.ics',
-					'calendardata' => self::CALENDAR_DATA
-				],
-				1
-			]
-		];
-	}
+					'id' => 5,
+					'calendar_id' => 1337,
+					'object_id' => 42,
+					'uid' => 'wej2z68l9h',
+					'is_recurring' => true,
+					'recurrence_id' => 1467849600,
+					'is_recurrence_exception' => false,
+					'event_hash' => 'fbdb2726bc0f7dfacac1d881c1453e20',
+					'alarm_hash' => '8996992118817f9f311ac5cc56d1cc97',
+					'type' => 'EMAIL',
+					'is_relative' => true,
+					'notification_date' => 1467158400,
+					'is_repeat_based' => false,
+					'calendardata' => self::CALENDAR_DATA_RECURRING,
+					'displayname' => 'Displayname 123',
+					'principaluri' => 'principals/users/user001',
+				]
+			]);
 
-	/**
-	 * @dataProvider providesTouchCalendarObject
-	 * @param string $action
-	 * @param array $calendarData
-	 * @param array $shares
-	 * @param array $objectData
-	 * @param int $numberOfGroups
-	 * @throws \OC\User\NoUserException
-	 * @throws \Sabre\VObject\InvalidDataException
-	 */
-	public function testOnTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData, int $numberOfGroups): void
-	{
-		$this->backend->expects($this->once())->method('cleanRemindersForEvent')->with($objectData['calendarid'], $objectData['uri']);
-
-		if ($action !== '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
-			$user = $this->createMock(IUser::class);
-			$user->expects($this->once())->method('getUID')->willReturn('user');
-
-			$this->userSession->expects($this->once())->method('getUser')->willReturn($user);
-			if ($numberOfGroups === 0) {
-				$this->backend->expects($this->exactly(count($shares) + 1))->method('insertReminder');
-			} else {
-				$group = $this->createMock(IGroup::class);
-				$groupUser = $this->createMock(IUser::class);
-				$groupUser->expects($this->once())->method('getUID')->willReturn('groupuser');
-				$group->expects($this->once())->method('getUsers')->willReturn([$groupUser]);
-				$this->groupManager->expects($this->exactly($numberOfGroups))->method('get')->willReturn($group);
-			}
-		}
-		$reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
-		$reminderService->onTouchCalendarObject($action, $calendarData, $shares, $objectData);
-	}
+		$this->notificationProviderManager->expects($this->at(0))
+			->method('hasProvider')
+			->with('EMAIL')
+			->willReturn(true);
 
-	/**
-	 * @expectedException OC\User\NoUserException
-	 */
-	public function testOnTouchCalendarObjectWithNoSession(): void
-	{
-		$this->backend->expects($this->once())->method('cleanRemindersForEvent');
-		$this->userSession->expects($this->once())->method('getUser')->willReturn(null);
+		$provider1 = $this->createMock(INotificationProvider::class);
+		$this->notificationProviderManager->expects($this->at(1))
+			->method('getProvider')
+			->with('EMAIL')
+			->willReturn($provider1);
 
-		$reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
-		$reminderService->onTouchCalendarObject('', ['principaluri' => 'foo'], [], ['calendarid' => 1, 'uri' => 'bar']);
-	}
+		$this->notificationProviderManager->expects($this->at(2))
+			->method('hasProvider')
+			->with('EMAIL')
+			->willReturn(true);
+
+		$provider2 = $this->createMock(INotificationProvider::class);
+		$this->notificationProviderManager->expects($this->at(3))
+			->method('getProvider')
+			->with('EMAIL')
+			->willReturn($provider2);
+
+		$this->notificationProviderManager->expects($this->at(4))
+			->method('hasProvider')
+			->with('DISPLAY')
+			->willReturn(true);
+
+		$provider3 = $this->createMock(INotificationProvider::class);
+		$this->notificationProviderManager->expects($this->at(5))
+			->method('getProvider')
+			->with('DISPLAY')
+			->willReturn($provider3);
+
+		$this->notificationProviderManager->expects($this->at(6))
+			->method('hasProvider')
+			->with('EMAIL')
+			->willReturn(true);
+
+		$provider4 = $this->createMock(INotificationProvider::class);
+		$this->notificationProviderManager->expects($this->at(7))
+			->method('getProvider')
+			->with('EMAIL')
+			->willReturn($provider4);
+
+		$this->notificationProviderManager->expects($this->at(8))
+			->method('hasProvider')
+			->with('EMAIL')
+			->willReturn(true);
+
+		$provider5 = $this->createMock(INotificationProvider::class);
+		$this->notificationProviderManager->expects($this->at(9))
+			->method('getProvider')
+			->with('EMAIL')
+			->willReturn($provider5);
+
+		$user = $this->createMock(IUser::class);
+		$this->userManager->expects($this->exactly(5))
+			->method('get')
+			->with('user001')
+			->willReturn($user);
+
+		$provider1->expects($this->once())
+			->method('send')
+			->with($this->callback(function($vevent) {
+				if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+					return false;
+				}
+				return true;
+			}, 'Displayname 123', $user));
+		$provider2->expects($this->once())
+			->method('send')
+			->with($this->callback(function($vevent) {
+				if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+					return false;
+				}
+				return true;
+			}, 'Displayname 123', $user));
+		$provider3->expects($this->once())
+			->method('send')
+			->with($this->callback(function($vevent) {
+				if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+					return false;
+				}
+				return true;
+			}, 'Displayname 123', $user));
+		$provider4->expects($this->once())
+			->method('send')
+			->with($this->callback(function($vevent) {
+				if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-30T00:00:00+00:00') {
+					return false;
+				}
+				return true;
+			}, 'Displayname 123', $user));
+		$provider5->expects($this->once())
+			->method('send')
+			->with($this->callback(function($vevent) {
+				if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-07-07T00:00:00+00:00') {
+					return false;
+				}
+				return true;
+			}, 'Displayname 123', $user));
+
+		$this->backend->expects($this->at(1))
+			->method('removeReminder')
+			->with(1);
+		$this->backend->expects($this->at(2))
+			->method('removeReminder')
+			->with(2);
+		$this->backend->expects($this->at(3))
+			->method('removeReminder')
+			->with(3);
+		$this->backend->expects($this->at(4))
+			->method('removeReminder')
+			->with(4);
+		$this->backend->expects($this->at(5))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848700, false)
+			->willReturn(99);
+
+		$this->backend->expects($this->at(6))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848820, true)
+			->willReturn(99);
+		$this->backend->expects($this->at(7))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848940, true)
+			->willReturn(99);
+		$this->backend->expects($this->at(8))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467849060, true)
+			->willReturn(99);
+		$this->backend->expects($this->at(9))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467849180, true)
+			->willReturn(99);
+		$this->backend->expects($this->at(10))
+			->method('removeReminder')
+			->with(5);
+		$this->backend->expects($this->at(11))
+			->method('insertReminder')
+			->with(1337, 42, 'wej2z68l9h', true, 1468454400, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467763200, false)
+			->willReturn(99);
+
+		$this->timeFactory->method('getDateTime')
+			->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
 
-	public function testOnTouchCalendarObjectWithNoCalendarURI(): void
-	{
-		$reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
-		$this->assertNull($reminderService->onTouchCalendarObject('', [], [], []));
+		$this->reminderService->processReminders();
 	}
 }