From 8700ae7db5409b8e454daf1776f395416badc9a0 Mon Sep 17 00:00:00 2001 From: Anna Larch Date: Thu, 20 Jul 2023 16:43:40 +0200 Subject: [PATCH] fixup! enh: add notification for quota depletion --- lib/Account.php | 4 + lib/BackgroundJob/QuotaJob.php | 28 ++- tests/Integration/Db/MailAccountTest.php | 3 + tests/Unit/BackgroundJob/QuotaJobTest.php | 286 ++++++++++++++++++++++ 4 files changed, 312 insertions(+), 9 deletions(-) create mode 100644 tests/Unit/BackgroundJob/QuotaJobTest.php diff --git a/lib/Account.php b/lib/Account.php index 0c53731940..4abac984ec 100644 --- a/lib/Account.php +++ b/lib/Account.php @@ -239,4 +239,8 @@ public function calculateAndSetQuotaPercentage(Service\Quota $quota): void { $percentage = (int)round($quota->getUsage() / $quota->getLimit() * 100); $this->account->setQuotaPercentage($percentage); } + + public function getQuotaPercentage(): int { + return $this->account->getQuotaPercentage(); + } } diff --git a/lib/BackgroundJob/QuotaJob.php b/lib/BackgroundJob/QuotaJob.php index ae88d2d943..2de893ba5f 100644 --- a/lib/BackgroundJob/QuotaJob.php +++ b/lib/BackgroundJob/QuotaJob.php @@ -26,6 +26,7 @@ namespace OCA\Mail\BackgroundJob; use Horde_Imap_Client_Exception; +use OCA\Mail\Account; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Db\MailAccountMapper; use OCA\Mail\Exception\IncompleteSyncException; @@ -79,7 +80,15 @@ public function __construct(ITimeFactory $time, */ protected function run($argument): void { $accountId = (int)$argument['accountId']; - $account = $this->accountService->findById($accountId); + try { + /** @var Account $account */ + $account = $this->accountService->findById($accountId); + } catch (DoesNotExistException $e) { + $this->logger->debug('Could not find account <' . $accountId . '> removing from jobs'); + $this->jobList->remove(self::class, $argument); + return; + } + $user = $this->userManager->get($account->getUserId()); if ($user === null || !$user->isEnabled()) { $this->logger->debug(sprintf( @@ -90,20 +99,21 @@ protected function run($argument): void { return; } + /** @var Quota $quota */ $quota = $this->mailManager->getQuota($account); if($quota === null) { $this->logger->debug('Could not get quota information for account <' . $account->getEmail() . '>', ['app' => 'mail']); return; } - $previous = $account->getMailAccount()->getQuotaPercentage(); + $previous = $account->getQuotaPercentage(); $account->calculateAndSetQuotaPercentage($quota); - $account = $this->accountService->update($account->getMailAccount()); - + $this->accountService->update($account->getMailAccount()); + $current = $account->getQuotaPercentage(); // Only notify if we've reached the rising edge - if($previous < $account->getQuotaPercentage() && $previous <= 90 && $account->getQuotaPercentage() > 90) { - $this->logger->debug('New quota information for <' . $account->getEmail() . '> - previous: ' . $previous . ', current: ' . $account->getQuotaPercentage(), ['app' => 'mail']); - + if($previous < $current && $previous <= 90 && $current > 90) { + $this->logger->debug('New quota information for <' . $account->getEmail() . '> - previous: ' . $previous . ', current: ' . $current); + $time = $this->time->getDateTime('now'); $notification = $this->notificationManager->createNotification(); $notification ->setApp('mail') @@ -113,10 +123,10 @@ protected function run($argument): void { 'id' => $accountId, 'account_email' => $account->getEmail() ]) - ->setDateTime(new \DateTime()) + ->setDateTime($time) ->setMessage('percentage', [ 'id' => $accountId, - 'quota_percentage' => (string)$account->getQuotaPercentage(), + 'quota_percentage' => $current, ] ); $this->notificationManager->notify($notification); diff --git a/tests/Integration/Db/MailAccountTest.php b/tests/Integration/Db/MailAccountTest.php index 1453620dd3..a45f1b299b 100644 --- a/tests/Integration/Db/MailAccountTest.php +++ b/tests/Integration/Db/MailAccountTest.php @@ -46,6 +46,7 @@ public function testToAPI() { $a->setEditorMode('html'); $a->setProvisioningId(null); $a->setOrder(13); + $a->setQuotaPercentage(10); $this->assertEquals([ 'id' => 12345, @@ -74,6 +75,7 @@ public function testToAPI() { 'signatureAboveQuote' => false, 'signatureMode' => null, 'smimeCertificateId' => null, + 'quotaPercentage' => 10, ], $a->toJson()); } @@ -105,6 +107,7 @@ public function testMailAccountConstruct() { 'signatureAboveQuote' => false, 'signatureMode' => null, 'smimeCertificateId' => null, + 'quotaPercentage' => null, ]; $a = new MailAccount($expected); // TODO: fix inconsistency diff --git a/tests/Unit/BackgroundJob/QuotaJobTest.php b/tests/Unit/BackgroundJob/QuotaJobTest.php new file mode 100644 index 0000000000..9a5b8aa9c2 --- /dev/null +++ b/tests/Unit/BackgroundJob/QuotaJobTest.php @@ -0,0 +1,286 @@ + + * + * @author 2021 Christoph Wurst + * + * @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\Mail\Tests\Unit\BackgroundJob; + +use ChristophWurst\Nextcloud\Testing\ServiceMockObject; +use ChristophWurst\Nextcloud\Testing\TestCase; +use OC\BackgroundJob\JobList; +use OCA\Mail\Account; +use OCA\Mail\BackgroundJob\QuotaJob; +use OCA\Mail\BackgroundJob\SyncJob; +use OCA\Mail\Db\MailAccount; +use OCA\Mail\Service\Quota; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\ILogger; +use OCP\IUser; +use OCP\Notification\INotification; + +class QuotaJobTest extends TestCase { + /** @var ServiceMockObject*/ + private $serviceMock; + + /** @var SyncJob */ + private $job; + + protected function setUp(): void { + parent::setUp(); + + $this->serviceMock = $this->createServiceMock(QuotaJob::class); + $this->job = $this->serviceMock->getService(); + + // Make sure the job is actually run + $this->serviceMock->getParameter('time') + ->method('getTime') + ->willReturn(500000); + + // Set our common argument + $this->job->setArgument([ + 'accountId' => 123, + ]); + // Set a fake ID + $this->job->setId(99); + } + + public function testAccountDoesntExist(): void { + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('findById') + ->with(123) + ->willThrowException(new DoesNotExistException('')); + $this->serviceMock->getParameter('logger') + ->expects(self::once()) + ->method('debug') + ->with('Could not find account <123> removing from jobs'); + $this->serviceMock->getParameter('jobList') + ->expects(self::once()) + ->method('remove') + ->with(QuotaJob::class, ['accountId' => 123]); + $this->serviceMock->getParameter('mailManager') + ->expects(self::never()) + ->method('getQuota'); + + $this->job->setArgument([ + 'accountId' => 123, + ]); + $this->job->setLastRun(0); + $this->job->execute( + $this->createMock(JobList::class), + $this->createMock(ILogger::class) + ); + } + + public function testUserDoesntExist(): void { + $account = $this->createMock(Account::class); + $account->method('getId')->willReturn(123); + $account->method('getUserId')->willReturn('user123'); + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('findById') + ->with(123) + ->willReturn($account); + $user = $this->createMock(IUser::class); + $this->serviceMock->getParameter('userManager') + ->expects(self::once()) + ->method('get') + ->with('user123') + ->willReturn($user); + $this->serviceMock->getParameter('logger') + ->expects(self::once()) + ->method('debug') + ->with('Account 123 of user user123 could not be found or was disabled, skipping quota query'); + $this->serviceMock->getParameter('mailManager') + ->expects(self::never()) + ->method('getQuota'); + + $this->job->setArgument([ + 'accountId' => 123, + ]); + $this->job->execute( + $this->createMock(JobList::class), + $this->createMock(ILogger::class) + ); + } + + public function testQuotaTooLow(): void { + $oldQuota = 10; + $newQuota = 20; + $quotaDTO = new Quota(20, 100); + $mailAccount = $this->createMock(MailAccount::class); + $account = $this->createConfiguredMock(Account::class, [ + 'getId' => 123, + 'getUserId' => 'user123', + 'getMailAccount' => $mailAccount, + ]); + $user = $this->createConfiguredMock(IUser::class, [ + 'isEnabled' => true, + ]); + + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('findById') + ->with(123) + ->willReturn($account); + $this->serviceMock->getParameter('userManager') + ->expects(self::once()) + ->method('get') + ->with('user123') + ->willReturn($user); + $this->serviceMock->getParameter('logger') + ->expects(self::never()) + ->method('debug'); + $this->serviceMock->getParameter('mailManager') + ->expects(self::once()) + ->method('getQuota') + ->willReturn($quotaDTO); + $account->expects(self::once()) + ->method('calculateAndSetQuotaPercentage') + ->with($quotaDTO); + $account->expects(self::once()) + ->method('getMailAccount') + ->willReturn($mailAccount); + $account->expects(self::exactly(2)) + ->method('getQuotaPercentage') + ->willReturnOnConsecutiveCalls($oldQuota, $newQuota); + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('update') + ->with($mailAccount); + + $this->job->setArgument([ + 'accountId' => 123, + ]); + $this->job->execute( + $this->createMock(JobList::class), + $this->createMock(ILogger::class) + ); + } + + public function testQuotaWithNotification(): void { + $oldQuota = 85; + $newQuota = 95; + $quotaDTO = new Quota(95, 100); + $mailAccount = $this->createMock(MailAccount::class); + $account = $this->createConfiguredMock(Account::class, [ + 'getId' => 123, + 'getUserId' => 'user123', + 'getMailAccount' => $mailAccount, + 'getEmail' => 'user123@test.com', + ]); + $user = $this->createConfiguredMock(IUser::class, [ + 'isEnabled' => true, + ]); + $notification = $this->createMock(INotification::class); + + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('findById') + ->with(123) + ->willReturn($account); + $this->serviceMock->getParameter('userManager') + ->expects(self::once()) + ->method('get') + ->with('user123') + ->willReturn($user); + $this->serviceMock->getParameter('mailManager') + ->expects(self::once()) + ->method('getQuota') + ->willReturn($quotaDTO); + $account->expects(self::once()) + ->method('calculateAndSetQuotaPercentage') + ->with($quotaDTO); + $account->expects(self::once()) + ->method('getMailAccount') + ->willReturn($mailAccount); + $account->expects(self::exactly(2)) + ->method('getQuotaPercentage') + ->willReturnOnConsecutiveCalls($oldQuota, $newQuota); + $account->expects(self::exactly(2)) + ->method('getUserId') + ->willReturn('user123'); + $account->expects(self::exactly(2)) + ->method('getEmail') + ->willReturn('user123@test.com'); + $this->serviceMock->getParameter('accountService') + ->expects(self::once()) + ->method('update') + ->with($mailAccount); + $this->serviceMock->getParameter('logger') + ->expects(self::once()) + ->method('debug') + ->with('New quota information for - previous: ' . $oldQuota . ', current: ' . $newQuota); + $this->serviceMock->getParameter('notificationManager') + ->expects(self::once()) + ->method('createNotification') + ->willReturn($notification); + $time = new \DateTime('now'); + $this->serviceMock->getParameter('time') + ->expects(self::once()) + ->method('getDateTime') + ->willReturn($time); + $notification->expects(self::once()) + ->method('setApp') + ->with('mail') + ->willReturn($notification); + $notification->expects(self::once()) + ->method('setUser') + ->with('user123') + ->willReturn($notification); + $notification->expects(self::once()) + ->method('setObject') + ->with('quota', 123) + ->willReturn($notification); + $notification->expects(self::once()) + ->method('setSubject') + ->with('quota_depleted', [ + 'id' => 123, + 'account_email' => 'user123@test.com' + ]) + ->willReturn($notification); + $notification->expects(self::once()) + ->method('setDateTime') + ->with($time) + ->willReturn($notification); + $notification->expects(self::once()) + ->method('setMessage') + ->with('percentage', [ + 'id' => 123, + 'quota_percentage' => $newQuota, + ]) + ->willReturn($notification); + $this->serviceMock->getParameter('notificationManager') + ->expects(self::once()) + ->method('notify') + ->with($notification); + + $this->job->setArgument([ + 'accountId' => 123, + ]); + $this->job->execute( + $this->createMock(JobList::class), + $this->createMock(ILogger::class) + ); + } +}