From 912b1bde44be485bb8ef0b3f3ea4b13b30c4d7e0 Mon Sep 17 00:00:00 2001
From: Robin Windey
Date: Sun, 30 Jul 2023 22:26:15 +0200
Subject: [PATCH] Implement Notifications (#213) (#214)
* Implement Notifications
* Notification adjustments
* Add documentation
* Remove code smells
* Update psalm-baseline to ignore IRootFolder dependency
---
README.md | 10 +
doc/img/notifications.png | Bin 0 -> 25576 bytes
lib/AppInfo/Application.php | 5 +
lib/BackgroundJobs/ProcessFileJob.php | 54 ++--
lib/Notification/Notifier.php | 127 +++++++++
lib/Service/EventService.php | 3 +
lib/Service/INotificationService.php | 38 +++
lib/Service/NotificationService.php | 67 +++++
tests/Integration/AppTest.php | 17 +-
tests/Integration/Notification/AppFake.php | 49 ++++
.../Notification/NotificationTest.php | 203 +++++++++++++
.../BackgroundJobs/ProcessFileJobTest.php | 53 +++-
tests/Unit/Notification/NotifierTest.php | 266 ++++++++++++++++++
.../Unit/Service/NotificationServiceTest.php | 129 +++++++++
tests/psalm-baseline.xml | 9 +-
15 files changed, 995 insertions(+), 35 deletions(-)
create mode 100644 doc/img/notifications.png
create mode 100644 lib/Notification/Notifier.php
create mode 100644 lib/Service/INotificationService.php
create mode 100644 lib/Service/NotificationService.php
create mode 100644 tests/Integration/Notification/AppFake.php
create mode 100644 tests/Integration/Notification/NotificationTest.php
create mode 100644 tests/Unit/Notification/NotifierTest.php
create mode 100644 tests/Unit/Service/NotificationServiceTest.php
diff --git a/README.md b/README.md
index b71a2ee..66d5e65 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
- [Per workflow settings](#per-workflow-settings)
- [Global settings](#global-settings)
- [Testing your configuration](#testing-your-configuration)
+ - [Get feedback via Notifications](#get-feedback-via-notifications)
- [How it works](#how-it-works)
- [General](#general)
- [PDF](#pdf)
@@ -165,6 +166,15 @@ To **test** if your file gets processed properly you can do the following steps:
+### Get feedback via Notifications
+
+The Workflow OCR app supports sending notifications to the user in case anything went wrong during the [asynchronous OCR processing](#how-it-works). To enable this feature, you have to install and enable the [`Notifications`](https://github.com/nextcloud/notifications) app in your Nextcloud instance.
+
+
+
+
+
+
## How it works
### General
diff --git a/doc/img/notifications.png b/doc/img/notifications.png
new file mode 100644
index 0000000000000000000000000000000000000000..44867bfcfe63ae8478060641ba41087ba21344e1
GIT binary patch
literal 25576
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::class, GlobalSettingsService::class);
$context->registerServiceAlias(IEventService::class, EventService::class);
$context->registerServiceAlias(IOcrBackendInfoService::class, OcrBackendInfoService::class);
+ $context->registerServiceAlias(INotificationService::class, NotificationService::class);
// BUG #43
$context->registerService(ICommand::class, function () {
@@ -94,6 +98,7 @@ public function register(IRegistrationContext $context): void {
OcrProcessorFactory::registerOcrProcessors($context);
$context->registerEventListener(RegisterOperationsEvent::class, RegisterFlowOperationsListener::class);
+ $context->registerNotifierService(Notifier::class);
}
/**
diff --git a/lib/BackgroundJobs/ProcessFileJob.php b/lib/BackgroundJobs/ProcessFileJob.php
index a07b98a..1a4e06a 100644
--- a/lib/BackgroundJobs/ProcessFileJob.php
+++ b/lib/BackgroundJobs/ProcessFileJob.php
@@ -33,6 +33,7 @@
use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
use OCA\WorkflowOcr\Model\WorkflowSettings;
use OCA\WorkflowOcr\Service\IEventService;
+use OCA\WorkflowOcr\Service\INotificationService;
use OCA\WorkflowOcr\Service\IOcrService;
use OCA\WorkflowOcr\Wrapper\IFilesystem;
use OCA\WorkflowOcr\Wrapper\IViewFactory;
@@ -69,6 +70,8 @@ class ProcessFileJob extends \OCP\BackgroundJob\QueuedJob {
private $userSession;
/** @var IProcessingFileAccessor */
private $processingFileAccessor;
+ /** @var INotificationService */
+ private $notificationService;
public function __construct(
LoggerInterface $logger,
@@ -80,6 +83,7 @@ public function __construct(
IUserManager $userManager,
IUserSession $userSession,
IProcessingFileAccessor $processingFileAccessor,
+ INotificationService $notificationService,
ITimeFactory $timeFactory) {
parent::__construct($timeFactory);
$this->logger = $logger;
@@ -91,6 +95,7 @@ public function __construct(
$this->userManager = $userManager;
$this->userSession = $userSession;
$this->processingFileAccessor = $processingFileAccessor;
+ $this->notificationService = $notificationService;
}
/**
@@ -101,14 +106,16 @@ protected function run($argument) : void {
[$success, $filePath, $uid, $settings] = $this->tryParseArguments($argument);
if (!$success) {
+ $this->notificationService->createErrorNotification($uid, 'Failed to parse arguments inside the OCR process. Please have a look at your servers logfile for more details.');
return;
}
try {
$this->initUserEnvironment($uid);
- $this->processFile($filePath, $settings);
+ $this->processFile($filePath, $settings, $uid);
} catch (\Throwable $ex) {
$this->logger->error($ex->getMessage(), ['exception' => $ex]);
+ $this->notificationService->createErrorNotification($uid, 'An error occured while executing the OCR process. Please have a look at your servers logfile for more details.');
} finally {
$this->shutdownUserEnvironment();
}
@@ -165,26 +172,35 @@ private function tryParseArguments($argument) : array {
/**
* @param string $filePath The file to be processed
* @param WorkflowSettings $settings The settings to be used for processing
+ * @param string $userId The user who triggered the processing
*/
- private function processFile(string $filePath, WorkflowSettings $settings) : void {
- $node = $this->getNode($filePath);
+ private function processFile(string $filePath, WorkflowSettings $settings, string $userId) : void {
+ $node = $this->getNode($filePath, $userId);
if ($node === null) {
return;
}
+ $nodeId = $node->getId();
+
try {
$ocrFile = $this->ocrService->ocrFile($node, $settings);
- } catch (OcrNotPossibleException $ocrNpEx) {
- $this->logger->error('OCR for file ' . $node->getPath() . ' not possible. Message: ' . $ocrNpEx->getMessage());
- return;
- } catch (OcrProcessorNotFoundException $ocrNfEx) {
- $this->logger->error('OCR processor not found for mimetype ' . $node->getMimeType());
+ } catch(\Throwable $throwable) {
+ if ($throwable instanceof(OcrNotPossibleException::class)) {
+ $msg = 'OCR for file ' . $node->getPath() . ' not possible. Message: ' . $throwable->getMessage();
+ } elseif ($throwable instanceof(OcrProcessorNotFoundException::class)) {
+ $msg = 'OCR processor not found for mimetype ' . $node->getMimeType();
+ } else {
+ throw $throwable;
+ }
+
+ $this->logger->error($msg);
+ $this->notificationService->createErrorNotification($userId, $msg, $nodeId);
+
return;
}
$fileContent = $ocrFile->getFileContent();
- $nodeId = $node->getId();
$originalFileExtension = $node->getExtension();
$newFileExtension = $ocrFile->getFileExtension();
@@ -200,17 +216,21 @@ private function processFile(string $filePath, WorkflowSettings $settings) : voi
$this->eventService->textRecognized($ocrFile, $node);
}
- private function getNode(string $filePath) : ?Node {
+ private function getNode(string $filePath, string $userId) : ?Node {
try {
/** @var File */
$node = $this->rootFolder->get($filePath);
} catch (NotFoundException $nfEx) {
- $this->logger->warning('Could not process file \'' . $filePath . '\'. File was not found');
+ $msg = 'Could not process file \'' . $filePath . '\'. File was not found';
+ $this->logger->warning($msg);
+ $this->notificationService->createErrorNotification($userId, $msg);
return null;
}
if (!$node instanceof Node || $node->getType() !== FileInfo::TYPE_FILE) {
- $this->logger->warning('Skipping process for \'' . $filePath . '\'. It is not a file');
+ $msg = 'Skipping process for \'' . $filePath . '\'. It is not a file';
+ $this->logger->warning($msg);
+ $this->notificationService->createErrorNotification($userId, $msg);
return null;
}
@@ -218,17 +238,17 @@ private function getNode(string $filePath) : ?Node {
}
/**
- * * @param string $uid The owners userId of the file to be processed
+ * * @param string $userId The owners userId of the file to be processed
*/
- private function initUserEnvironment(string $uid) : void {
+ private function initUserEnvironment(string $userId) : void {
/** @var IUser */
- $user = $this->userManager->get($uid);
+ $user = $this->userManager->get($userId);
if (!$user) {
- throw new NoUserException("User with uid '$uid' was not found");
+ throw new NoUserException("User with uid '$userId' was not found");
}
$this->userSession->setUser($user);
- $this->filesystem->init($uid, '/' . $uid . '/files');
+ $this->filesystem->init($userId, '/' . $userId . '/files');
}
private function shutdownUserEnvironment() : void {
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
new file mode 100644
index 0000000..46035e7
--- /dev/null
+++ b/lib/Notification/Notifier.php
@@ -0,0 +1,127 @@
+
+ *
+ * @author Robin Windey
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\WorkflowOcr\Notification;
+
+use OCA\WorkflowOcr\AppInfo\Application;
+use OCP\Files\File;
+use OCP\Files\IRootFolder;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory;
+use OCP\Notification\AlreadyProcessedException;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+use Psr\Log\LoggerInterface;
+
+class Notifier implements INotifier {
+ /** @var IFactory*/
+ private $l10nFactory;
+ /** @var IURLGenerator */
+ private $urlGenerator;
+ /** @var IRootFolder */
+ private $rootFolder;
+ /** @var LoggerInterface */
+ private $logger;
+
+ public function __construct(IFactory $factory,
+ IURLGenerator $urlGenerator,
+ IRootFolder $rootFolder,
+ LoggerInterface $logger) {
+ $this->l10nFactory = $factory;
+ $this->urlGenerator = $urlGenerator;
+ $this->rootFolder = $rootFolder;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Identifier of the notifier, only use [a-z0-9_]
+ * @return string
+ */
+ public function getID(): string {
+ return Application::APP_NAME;
+ }
+
+ /**
+ * Human readable name describing the notifier
+ * @return string
+ */
+ public function getName(): string {
+ return $this->l10nFactory->get(Application::APP_NAME)->t('Workflow OCR');
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ */
+ public function prepare(INotification $notification, string $languageCode): INotification {
+ if ($notification->getApp() !== Application::APP_NAME) {
+ throw new \InvalidArgumentException();
+ }
+
+ $notification->setIcon($this->urlGenerator->imagePath(Application::APP_NAME, 'app-dark.svg'));
+ $l = $this->l10nFactory->get(Application::APP_NAME, $languageCode);
+
+ // Currently we only support sending notifications for ocr_error
+ if ($notification->getSubject() !== 'ocr_error') {
+ throw new \InvalidArgumentException();
+ }
+
+ $message = $notification->getSubjectParameters()['message'];
+ $notification
+ ->setParsedSubject($l->t('Workflow OCR error'))
+ ->setParsedMessage($message);
+ // Only add file info if we have one ...
+ if ($notification->getObjectType() === 'file' && $notification->getObjectId()) {
+ $richParams = $this->getRichParamForFile($notification);
+ $notification->setRichSubject($l->t('Workflow OCR error for file {file}'), $richParams);
+ }
+ return $notification;
+ }
+
+ private function getRichParamForFile(INotification $notification) : array {
+ try {
+ $userFolder = $this->rootFolder->getUserFolder($notification->getUser());
+ /** @var File[] */
+ $files = $userFolder->getById($notification->getObjectId());
+ /** @var File $file */
+ $file = array_shift($files);
+ $relativePath = $userFolder->getRelativePath($file->getPath());
+ } catch (\Throwable $th) {
+ $this->logger->error($th->getMessage(), ['exception' => $th]);
+ throw new AlreadyProcessedException();
+ }
+
+ return [
+ 'file' => [
+ 'type' => 'file',
+ 'id' => $file->getId(),
+ 'name' => $file->getName(),
+ 'path' => $relativePath,
+ 'link' => $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $notification->getObjectId()])
+ ]
+ ];
+ }
+}
diff --git a/lib/Service/EventService.php b/lib/Service/EventService.php
index e94a21d..92f57e0 100644
--- a/lib/Service/EventService.php
+++ b/lib/Service/EventService.php
@@ -38,6 +38,9 @@ public function __construct(IEventDispatcher $eventDispatcher) {
$this->eventDispatcher = $eventDispatcher;
}
+ /**
+ * @return void
+ */
public function textRecognized(OcrProcessorResult $result, File $node) {
$event = new TextRecognizedEvent($result->getRecognizedText(), $node);
$this->eventDispatcher->dispatchTyped($event);
diff --git a/lib/Service/INotificationService.php b/lib/Service/INotificationService.php
new file mode 100644
index 0000000..5d0f159
--- /dev/null
+++ b/lib/Service/INotificationService.php
@@ -0,0 +1,38 @@
+
+ *
+ * @author Robin Windey
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\WorkflowOcr\Service;
+
+interface INotificationService {
+
+ /**
+ * Create a new notification for the given user if the OCR process of the given file failed.
+ * @param string $userId The user ID of the user that should receive the notification.
+ * @param string $message The error message that should be displayed in the notification.
+ * @param int $fileId Optional file ID of the file that failed to OCR. If given, user can jump to the file via link.
+ */
+ public function createErrorNotification(?string $userId, string $message, int $fileId = null);
+}
diff --git a/lib/Service/NotificationService.php b/lib/Service/NotificationService.php
new file mode 100644
index 0000000..0fdc880
--- /dev/null
+++ b/lib/Service/NotificationService.php
@@ -0,0 +1,67 @@
+
+ *
+ * @author Robin Windey
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\WorkflowOcr\Service;
+
+use OCA\WorkflowOcr\AppInfo\Application;
+use OCP\Notification\IManager;
+
+/*
+* This class is used to create new NC notifications.
+* They will be displayed later with the help of Nofification\Notifier.
+*/
+class NotificationService implements INotificationService {
+
+ private IManager $notificationManager;
+
+ public function __construct(IManager $notificationManager) {
+ $this->notificationManager = $notificationManager;
+ }
+
+ /**
+ * @return void
+ */
+ public function createErrorNotification(?string $userId, string $message, int $fileId = null) {
+ // We don't create unbound notifications
+ if (!$userId) {
+ return;
+ }
+
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp(Application::APP_NAME)
+ ->setUser($userId)
+ ->setDateTime(new \DateTime())
+ ->setSubject('ocr_error', ['message' => $message]);
+
+ if ($fileId) {
+ $notification->setObject('file', strval($fileId));
+ } else {
+ $notification->setObject('ocr', 'ocr');
+ }
+
+ $this->notificationManager->notify($notification);
+ }
+}
diff --git a/tests/Integration/AppTest.php b/tests/Integration/AppTest.php
index c3d12cd..67f4171 100644
--- a/tests/Integration/AppTest.php
+++ b/tests/Integration/AppTest.php
@@ -1,14 +1,21 @@
*
+ * @license GNU AGPL version 3 or any later version
*
- * This file is licensed under the Affero General Public License version 3 or
- * later. See the COPYING file.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * @author Robin Windey
+ * 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.
*
- * @copyright Robin Windey 2020
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
namespace OCA\WorkflowOcr\Tests\Integration;
diff --git a/tests/Integration/Notification/AppFake.php b/tests/Integration/Notification/AppFake.php
new file mode 100644
index 0000000..14e79b6
--- /dev/null
+++ b/tests/Integration/Notification/AppFake.php
@@ -0,0 +1,49 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace OCA\WorkflowOcr\Tests\Integration\Notification;
+
+use OCP\Notification\IApp;
+use OCP\Notification\INotification;
+
+class AppFake implements IApp {
+ private $notifications = [];
+ private $processed = [];
+
+ public function notify(INotification $notification): void {
+ $this->notifications[] = $notification;
+ }
+
+ public function markProcessed(INotification $notification): void {
+ $this->processed[] = $notification;
+ }
+
+ public function getCount(INotification $notification): int {
+ return 0;
+ }
+
+ public function getNotifications() {
+ return $this->notifications;
+ }
+
+ public function getProcessed() {
+ return $this->processed;
+ }
+}
diff --git a/tests/Integration/Notification/NotificationTest.php b/tests/Integration/Notification/NotificationTest.php
new file mode 100644
index 0000000..226d097
--- /dev/null
+++ b/tests/Integration/Notification/NotificationTest.php
@@ -0,0 +1,203 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace OCA\WorkflowOcr\Tests\Integration\Notification;
+
+use OC\BackgroundJob\JobList;
+use OCA\WorkflowOcr\BackgroundJobs\ProcessFileJob;
+use OCA\WorkflowOcr\Exception\OcrNotPossibleException;
+use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
+use OCA\WorkflowOcr\Service\IEventService;
+use OCA\WorkflowOcr\Service\INotificationService;
+use OCA\WorkflowOcr\Service\IOcrService;
+use OCA\WorkflowOcr\Service\NotificationService;
+use OCA\WorkflowOcr\Wrapper\IFilesystem;
+use OCA\WorkflowOcr\Wrapper\IViewFactory;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\DB\QueryBuilder\IExpressionBuilder;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\IRootFolder;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class NotificationTest extends TestCase {
+ /** @var AppFake */
+ private $appFake;
+ /** @var LoggerInterface|MockObject */
+ private $logger;
+ /** @var IRootFolder|MockObject */
+ private $rootFolder;
+ /** @var IOcrService|MockObject */
+ private $ocrService;
+ /** @var IEventService|MockObject */
+ private $eventService;
+ /** @var IViewFactory|MockObject */
+ private $viewFactory;
+ /** @var IFilesystem|MockObject */
+ private $filesystem;
+ /** @var IUserSession|MockObject */
+ private $userSession;
+ /** @var IUserManager|MockObject */
+ private $userManager;
+ /** @var IUser|MockObject */
+ private $user;
+ /** @var IProcessingFileAccessor|MockObject */
+ private $processingFileAccessor;
+ /** @var INotificationService|MockObject */
+ private $notificationService;
+ /** @var JobList */
+ private $jobList;
+ /** @var ProcessFileJob */
+ private $processFileJob;
+
+ protected function setUp() : void {
+ parent::setUp();
+ // We use a faked notification receiver app to keep track of any notifications created
+ \OC::$server->get(\OCP\Notification\IManager::class)->registerApp(AppFake::class);
+
+ // Use real Notification service to be able to check if notifications get created
+ $this->notificationService = new NotificationService(\OC::$server->get(\OCP\Notification\IManager::class));
+
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->rootFolder = $this->createMock(IRootFolder::class);
+ $this->ocrService = $this->createMock(IOcrService::class);
+ $this->eventService = $this->createMock(IEventService::class);
+ $this->viewFactory = $this->createMock(IViewFactory::class);
+ $this->filesystem = $this->createMock(IFilesystem::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->processingFileAccessor = $this->createMock(IProcessingFileAccessor::class);
+
+ /** @var MockObject|IUserManager */
+ $userManager = $this->createMock(IUserManager::class);
+ $user = $this->createMock(IUser::class);
+ $userManager->method('get')
+ ->withAnyParameters()
+ ->willReturn($user);
+
+ $this->userManager = $userManager;
+ $this->user = $user;
+
+ $this->processFileJob = new ProcessFileJob(
+ $this->logger,
+ $this->rootFolder,
+ $this->ocrService,
+ $this->eventService,
+ $this->viewFactory,
+ $this->filesystem,
+ $this->userManager,
+ $this->userSession,
+ $this->processingFileAccessor,
+ $this->notificationService,
+ $this->createMock(ITimeFactory::class)
+ );
+
+ /** @var IConfig */
+ $configMock = $this->createMock(IConfig::class);
+ /** @var ITimeFactory */
+ $timeFactoryMock = $this->createMock(ITimeFactory::class);
+ /** @var MockObject|IDbConnection */
+ $connectionMock = $this->createMock(IDBConnection::class);
+ /** @var MockObject|IQueryBuilder */
+ $queryBuilderMock = $this->createMock(IQueryBuilder::class);
+ $expressionBuilderMock = $this->createMock(IExpressionBuilder::class);
+
+ $queryBuilderMock->method('delete')
+ ->withAnyParameters()
+ ->willReturn($queryBuilderMock);
+ $queryBuilderMock->method('set')
+ ->withAnyParameters()
+ ->willReturn($queryBuilderMock);
+ $queryBuilderMock->method('update')
+ ->withAnyParameters()
+ ->willReturn($queryBuilderMock);
+ $queryBuilderMock->method('expr')
+ ->withAnyParameters()
+ ->willReturn($expressionBuilderMock);
+ $connectionMock->method('getQueryBuilder')
+ ->withAnyParameters()
+ ->willReturn($queryBuilderMock);
+
+ $this->jobList = new JobList(
+ $connectionMock,
+ $configMock,
+ $timeFactoryMock,
+ $this->logger
+ );
+
+ $this->processFileJob->setId(111);
+ $this->processFileJob->setArgument([
+ 'filePath' => '/admin/files/somefile.pdf',
+ 'uid' => 'someuser',
+ 'settings' => '{}'
+ ]);
+ }
+
+ public function testBackgroundJobCreatesErrorNotificationIfOcrFailed() {
+ $fileMock = $this->createValidFileMock();
+ $this->rootFolder->method('get')
+ ->with('/admin/files/somefile.pdf')
+ ->willReturn($fileMock);
+
+ $this->ocrService->expects($this->once())
+ ->method('ocrFile')
+ ->withAnyParameters()
+ ->willThrowException(new OcrNotPossibleException('Some error'));
+ $appFake = \OC::$server->get(AppFake::class);
+
+ $this->processFileJob->start($this->jobList);
+
+ $notifications = $appFake->getNotifications();
+ $this->assertCount(1, $notifications);
+
+ $notification = $notifications[0];
+ $this->assertEquals('workflow_ocr', $notification->getApp());
+ $this->assertEquals('ocr_error', $notification->getSubject());
+ $this->assertEquals('OCR for file /admin/files/somefile.pdf not possible. Message: Some error', $notification->getSubjectParameters()['message']);
+ }
+
+ /**
+ * @return File|MockObject
+ */
+ private function createValidFileMock(string $mimeType = 'application/pdf', string $content = 'someFileContent', string $fileExtension = "pdf", string $path = "/admin/files/somefile.pdf") {
+ /** @var MockObject|File */
+ $fileMock = $this->createMock(File::class);
+ $fileMock->method('getType')
+ ->willReturn(FileInfo::TYPE_FILE);
+ $fileMock->method('getMimeType')
+ ->willReturn($mimeType);
+ $fileMock->method('getContent')
+ ->willReturn($content);
+ $fileMock->method('getId')
+ ->willReturn(42);
+ $fileMock->method('getExtension')
+ ->willReturn($fileExtension);
+ $fileMock->method('getPath')
+ ->willReturn($path);
+ return $fileMock;
+ }
+}
diff --git a/tests/Unit/BackgroundJobs/ProcessFileJobTest.php b/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
index 653139f..07f78f9 100644
--- a/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
+++ b/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
@@ -32,6 +32,7 @@
use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
use OCA\WorkflowOcr\OcrProcessors\OcrProcessorResult;
use OCA\WorkflowOcr\Service\IEventService;
+use OCA\WorkflowOcr\Service\INotificationService;
use OCA\WorkflowOcr\Service\IOcrService;
use OCA\WorkflowOcr\Wrapper\IFilesystem;
use OCA\WorkflowOcr\Wrapper\IView;
@@ -74,6 +75,8 @@ class ProcessFileJobTest extends TestCase {
private $user;
/** @var IProcessingFileAccessor|MockObject */
private $processingFileAccessor;
+ /** @var INotificationService|MockObject */
+ private $notificationService;
/** @var JobList */
private $jobList;
/** @var ProcessFileJob */
@@ -90,6 +93,7 @@ public function setUp() : void {
$this->filesystem = $this->createMock(IFilesystem::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->processingFileAccessor = $this->createMock(IProcessingFileAccessor::class);
+ $this->notificationService = $this->createMock(INotificationService::class);
/** @var MockObject|IUserManager */
$userManager = $this->createMock(IUserManager::class);
@@ -111,6 +115,7 @@ public function setUp() : void {
$this->userManager,
$this->userSession,
$this->processingFileAccessor,
+ $this->notificationService,
$this->createMock(ITimeFactory::class)
);
$this->processFileJob->setId(111);
@@ -171,7 +176,7 @@ public function testCatchesExceptionAndResetsUserEnvironment() {
->method('setUser')
->withConsecutive([$this->user], [null]);
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -191,7 +196,7 @@ public function testDoesNothingOnInvalidArguments($argument, $invalidCount) {
$this->logger->expects($this->exactly($invalidCount))
->method('warning');
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -203,7 +208,7 @@ public function testCallsInitFilesystem(array $arguments, string $user, string $
->method('init')
->with($user, $rootFolderPath);
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -215,7 +220,7 @@ public function testCallsGetOnRootFolder(array $arguments, string $user, string
->method('get')
->with($arguments['filePath']);
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -235,7 +240,7 @@ public function testCallsOcr_IfIsFile(array $arguments, string $user, string $ro
->method('ocrFile')
->with($fileMock);
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -273,7 +278,7 @@ public function testCreatesNewFileVersionAndEmitsTextRecognizedEvent(array $argu
->method('textRecognized')
->with($ocrResult, $fileMock);
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
public function testNotFoundLogsWarning_AndDoesNothingAfterwards() {
@@ -286,7 +291,7 @@ public function testNotFoundLogsWarning_AndDoesNothingAfterwards() {
$this->ocrService->expects($this->never())
->method('ocrFile');
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -300,7 +305,7 @@ public function testDoesNotCallOcr_OnNonFile($invalidNode) {
$this->ocrService->expects($this->never())
->method('ocrFile');
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
/**
@@ -321,7 +326,7 @@ public function testLogsError_OnOcrException(Exception $exception) {
$this->viewFactory->expects($this->never())
->method('create');
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
}
public function testThrowsNoUserException_OnNonExistingUser() {
@@ -349,13 +354,14 @@ public function testThrowsNoUserException_OnNonExistingUser() {
$userManager,
$this->userSession,
$this->processingFileAccessor,
+ $this->notificationService,
$this->createMock(ITimeFactory::class)
);
$processFileJob->setId(111);
$arguments = ['filePath' => '/nonexistinguser/files/someInvalidStuff', 'settings' => '{}'];
$processFileJob->setArgument($arguments);
- $processFileJob->execute($this->jobList);
+ $processFileJob->start($this->jobList);
}
/**
@@ -397,7 +403,7 @@ public function testCallsProcessingFileAccessor(array $arguments, string $user,
return true;
}));
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
$this->assertEquals(1, $calledWith42);
$this->assertEquals(1, $calledWithNull);
@@ -432,7 +438,30 @@ public function testDoesNotCreateNewFileVersionIfOcrContentWasEmpty(array $argum
$this->eventService->expects($this->once())
->method('textRecognized');
- $this->processFileJob->execute($this->jobList);
+ $this->processFileJob->start($this->jobList);
+ }
+
+ public function testLogsNonOcrExceptionsFromOcrService() {
+ $this->processFileJob->setArgument(['filePath' => '/admin/files/somefile.pdf', 'settings' => '{}']);
+ $mimeType = 'application/pdf';
+ $content = 'someFileContent';
+ $exception = new \Exception('someException');
+
+ $fileMock = $this->createValidFileMock($mimeType, $content);
+ $this->rootFolder->method('get')
+ ->willReturn($fileMock);
+
+ $this->ocrService->expects($this->once())
+ ->method('ocrFile')
+ ->willThrowException($exception);
+
+ $this->logger->expects($this->once())
+ ->method('error');
+
+ $this->viewFactory->expects($this->never())
+ ->method('create');
+
+ $this->processFileJob->start($this->jobList);
}
public function dataProvider_InvalidArguments() {
diff --git a/tests/Unit/Notification/NotifierTest.php b/tests/Unit/Notification/NotifierTest.php
new file mode 100644
index 0000000..b5df259
--- /dev/null
+++ b/tests/Unit/Notification/NotifierTest.php
@@ -0,0 +1,266 @@
+
+ *
+ * @author Robin Windey
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\WorkflowOcr\Tests\Unit\Notification;
+
+use OC\Notification\Notification;
+use OCA\WorkflowOcr\Notification\Notifier;
+use OCP\Files\File;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory;
+use OCP\Notification\AlreadyProcessedException;
+use OCP\Notification\INotification;
+use OCP\RichObjectStrings\IValidator;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class NotifierTest extends TestCase {
+ /** @var IFactory|MockObject */
+ private $l10nFactory;
+
+ /** @var IRootFolder|MockObject */
+ private $rootFolder;
+
+ /** @var LoggerInterface|MockObject */
+ private $logger;
+
+ /** @var IURLGenerator|MockObject */
+ private $urlGenerator;
+
+ /** @var Notifier */
+ private $notifier;
+
+ public function setUp() : void {
+ $this->l10nFactory = $this->createMock(IFactory::class);
+ $this->rootFolder = $this->createMock(IRootFolder::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+
+ $this->notifier = new Notifier(
+ $this->l10nFactory,
+ $this->urlGenerator,
+ $this->rootFolder,
+ $this->logger
+ );
+ }
+
+ public function testGetIdReturnsWorkflowOcrName() {
+ $this->assertEquals('workflow_ocr', $this->notifier->getId());
+ }
+
+ public function testGetDisplayNameReturnsWorkflowOcrName() {
+ /** @var IL10N|MockObject */
+ $l10n = $this->createMock(IL10N::class);
+ $l10n->expects($this->once())
+ ->method('t')
+ ->with('Workflow OCR')
+ ->willReturn('Workflow OCR');
+ $this->l10nFactory->expects($this->once())
+ ->method('get')
+ ->with('workflow_ocr')
+ ->willReturn($l10n);
+ $this->assertEquals('Workflow OCR', $this->notifier->getName());
+ }
+
+ public function testPrepareThrowsInvalidArgumentExceptionOnAppNotWorkflowOcr() {
+ /** @var INotification|MockObject */
+ $notification = $this->createMock(INotification::class);
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn('not_workflow_ocr');
+ $this->expectException(\InvalidArgumentException::class);
+ $this->notifier->prepare($notification, 'en');
+ }
+
+ public function testPrepareThrowsInvalidArgumentExceptionIfNotificationSubjectNotOcrError() {
+ /** @var INotification|MockObject */
+ $notification = $this->createMock(INotification::class);
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn('workflow_ocr');
+ $notification->expects($this->once())
+ ->method('getSubject')
+ ->willReturn('not_ocr_error');
+ $this->expectException(\InvalidArgumentException::class);
+ $this->notifier->prepare($notification, 'en');
+ }
+
+ public function testPrepareConstructsOcrErrorCorrectlyWithFileId() {
+ /** @var IValidator|MockObject */
+ $validator = $this->createMock(IValidator::class);
+ /** @var IL10N|MockObject */
+ $l10n = $this->createMock(IL10N::class);
+ $l10n->expects($this->exactly(2))
+ ->method('t')
+ ->withConsecutive(
+ ['Workflow OCR error'],
+ ['Workflow OCR error for file {file}']
+ )
+ ->willReturnOnConsecutiveCalls(
+ 'Workflow OCR',
+ 'Workflow OCR error for file {file}'
+ );
+ $this->l10nFactory->expects($this->once())
+ ->method('get')
+ ->with('workflow_ocr')
+ ->willReturn($l10n);
+
+ $notification = new Notification($validator);
+ $notification->setUser('user');
+ $notification->setApp('workflow_ocr');
+ $notification->setSubject('ocr_error', ['message' => 'mymessage']);
+ $notification->setObject('file', '123');
+
+ /** @var File|MockObject */
+ $file = $this->createMock(File::class);
+ $file->expects($this->once())
+ ->method('getPath')
+ ->willReturn('admin/files/file.txt');
+ $file->expects($this->once())
+ ->method('getName')
+ ->willReturn('file.txt');
+ $file->expects($this->once())
+ ->method('getId')
+ ->willReturn('123');
+ /** @var Folder|MockObject */
+ $userFolder = $this->createMock(Folder::class);
+ $userFolder->expects($this->once())
+ ->method('getById')
+ ->with('123')
+ ->willReturn(['file' => $file]);
+ $userFolder->expects($this->once())
+ ->method('getRelativePath')
+ ->with('admin/files/file.txt')
+ ->willReturn('files/file.txt');
+ $this->rootFolder->expects($this->once())
+ ->method('getUserFolder')
+ ->with('user')
+ ->willReturn($userFolder);
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->with('workflow_ocr', 'app-dark.svg')
+ ->willReturn('http://localhost/index.php/apps/workflow_ocr/app-dark.svg');
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with('files.viewcontroller.showFile', ['fileid' => '123'])
+ ->willReturn('http://localhost/index.php/apps/files/?file=123');
+
+ $preparedNotification = $this->notifier->prepare($notification, 'en');
+
+ $richSubject = $preparedNotification->getRichSubject();
+ $richSubjectParams = $preparedNotification->getRichSubjectParameters();
+
+ $this->assertEquals('Workflow OCR error for file {file}', $richSubject);
+ $this->assertEquals(['file' => [
+ 'type' => 'file',
+ 'id' => '123',
+ 'name' => 'file.txt',
+ 'path' => 'files/file.txt',
+ 'link' => 'http://localhost/index.php/apps/files/?file=123'
+ ]], $richSubjectParams);
+ }
+
+ public function testPrepareConstructsOcrErrorCorrectlyWithoutFile() {
+ /** @var IValidator|MockObject */
+ $validator = $this->createMock(IValidator::class);
+ /** @var IL10N|MockObject */
+ $l10n = $this->createMock(IL10N::class);
+ $l10n->expects($this->once())
+ ->method('t')
+ ->with('Workflow OCR error')
+ ->willReturn('Workflow OCR error');
+ $this->l10nFactory->expects($this->once())
+ ->method('get')
+ ->with('workflow_ocr')
+ ->willReturn($l10n);
+
+ $notification = new Notification($validator);
+ $notification->setUser('user');
+ $notification->setApp('workflow_ocr');
+ $notification->setSubject('ocr_error', ['message' => 'mymessage']);
+ $notification->setObject('ocr', 'ocr');
+
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->with('workflow_ocr', 'app-dark.svg')
+ ->willReturn('http://localhost/index.php/apps/workflow_ocr/app-dark.svg');
+ $this->urlGenerator->expects($this->never())
+ ->method('linkToRouteAbsolute');
+
+ $preparedNotification = $this->notifier->prepare($notification, 'en');
+
+ $richSubject = $preparedNotification->getRichSubject();
+ $richSubjectParams = $preparedNotification->getRichSubjectParameters();
+
+ $this->assertEquals('', $richSubject);
+ $this->assertEmpty($richSubjectParams);
+ }
+
+ public function testThrowsAlreadyProcessedExceptionIfFileCannotBeRead() {
+ /** @var IValidator|MockObject */
+ $validator = $this->createMock(IValidator::class);
+ /** @var IL10N|MockObject */
+ $l10n = $this->createMock(IL10N::class);
+ $l10n->expects($this->once())
+ ->method('t')
+ ->with('Workflow OCR error')
+ ->willReturn('Workflow OCR');
+ $this->l10nFactory->expects($this->once())
+ ->method('get')
+ ->with('workflow_ocr')
+ ->willReturn($l10n);
+
+ $notification = new Notification($validator);
+ $notification->setUser('user');
+ $notification->setApp('workflow_ocr');
+ $notification->setSubject('ocr_error', ['message' => 'mymessage']);
+ $notification->setObject('file', '123');
+
+ /** @var Folder|MockObject */
+ $userFolder = $this->createMock(Folder::class);
+ $userFolder->expects($this->once())
+ ->method('getById')
+ ->with('123')
+ ->willThrowException(new \OCP\Files\NotFoundException());
+ $userFolder->expects($this->never())
+ ->method('getRelativePath');
+ $this->rootFolder->expects($this->once())
+ ->method('getUserFolder')
+ ->with('user')
+ ->willReturn($userFolder);
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->with('workflow_ocr', 'app-dark.svg')
+ ->willReturn('http://localhost/index.php/apps/workflow_ocr/app-dark.svg');
+
+ $this->expectException(AlreadyProcessedException::class);
+ $this->notifier->prepare($notification, 'en');
+ }
+}
diff --git a/tests/Unit/Service/NotificationServiceTest.php b/tests/Unit/Service/NotificationServiceTest.php
new file mode 100644
index 0000000..1f5f839
--- /dev/null
+++ b/tests/Unit/Service/NotificationServiceTest.php
@@ -0,0 +1,129 @@
+
+ *
+ * @author Robin Windey
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\WorkflowOcr\Tests\Unit\Service;
+
+use OC\Notification\Notification;
+use OCA\WorkflowOcr\Service\NotificationService;
+use OCP\Notification\IManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+
+class NotificationServiceTest extends TestCase {
+ /** @var IManager|MockObject */
+ private $notificationManager;
+
+ /** @var Notification|MockObject */
+ private $notification;
+
+ /** @var NotificationService */
+ private $service;
+
+ public function setUp() : void {
+ $this->notificationManager = $this->createMock(IManager::class);
+ $this->notification = $this->createMock(Notification::class);
+ $this->service = new NotificationService($this->notificationManager);
+ parent::setUp();
+ }
+
+ public function testCreateErrorNotificationWithFileId() {
+ $this->notificationManager->expects($this->once())
+ ->method('createNotification')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setApp')
+ ->with('workflow_ocr')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setUser')
+ ->with('user1')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setDateTime')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setSubject')
+ ->with('ocr_error', ['message' => 'testnotification'])
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setObject')
+ ->with('file', '123')
+ ->willReturn($this->notification);
+ $this->notificationManager->expects($this->once())
+ ->method('notify')
+ ->with($this->notification);
+
+ $this->service->createErrorNotification("user1", "testnotification", 123);
+ }
+
+ public function testCreateErrorNotificationWithoutFileId() {
+ $this->notificationManager->expects($this->once())
+ ->method('createNotification')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setApp')
+ ->with('workflow_ocr')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setUser')
+ ->with('user1')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setDateTime')
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setSubject')
+ ->with('ocr_error', ['message' => 'testnotification'])
+ ->willReturn($this->notification);
+ $this->notification->expects($this->once())
+ ->method('setObject')
+ ->with('ocr', 'ocr');
+ $this->notificationManager->expects($this->once())
+ ->method('notify')
+ ->with($this->notification);
+
+ $this->service->createErrorNotification("user1", "testnotification");
+ }
+
+ public function testCreateErrorNotificationDoesNothingIfUserIdIsNotSet() {
+ $this->notificationManager->expects($this->never())
+ ->method('createNotification');
+ $this->notificationManager->expects($this->never())
+ ->method('notify');
+ $this->notification->expects($this->never())
+ ->method('setApp');
+ $this->notification->expects($this->never())
+ ->method('setUser');
+ $this->notification->expects($this->never())
+ ->method('setDateTime');
+ $this->notification->expects($this->never())
+ ->method('setSubject');
+ $this->notification->expects($this->never())
+ ->method('setObject');
+
+ $this->service->createErrorNotification(null, "testnotification", 123);
+ }
+}
diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml
index 92aff0b..8b9d31b 100644
--- a/tests/psalm-baseline.xml
+++ b/tests/psalm-baseline.xml
@@ -1,5 +1,5 @@
-
+
$this->rootFolder
@@ -10,6 +10,13 @@
NoUserException
+
+
+ $this->rootFolder
+ IRootFolder
+ IRootFolder
+
+
$this->rootFolder