diff options
author | Anna Larch <anna@nextcloud.com> | 2021-05-06 23:36:20 +0300 |
---|---|---|
committer | Anna Larch <anna@nextcloud.com> | 2021-05-26 16:38:18 +0300 |
commit | 540ca4b1ef164f1a05e8c4f457ed8aca7ccebabd (patch) | |
tree | 20a536d98ca52f81010478c620208a2cbe5e6b90 | |
parent | 4f5692a41c64d7878c9e6a167c72665cf70b8a33 (diff) |
Add bg job for label sync
Signed-off-by: Anna Larch <anna@nextcloud.com>
-rw-r--r-- | lib/BackgroundJob/MigrateImportantJob.php | 111 | ||||
-rw-r--r-- | lib/Db/MailboxMapper.php | 38 | ||||
-rw-r--r-- | lib/IMAP/MessageMapper.php | 26 | ||||
-rw-r--r-- | lib/Listener/DeleteDraftListener.php | 2 | ||||
-rw-r--r-- | lib/Listener/FlagRepliedMessageListener.php | 2 | ||||
-rw-r--r-- | lib/Migration/MigrateImportantFromImapAndDb.php | 98 | ||||
-rw-r--r-- | lib/Migration/Version1100Date20210512142306.php | 38 | ||||
-rw-r--r-- | lib/Service/MailManager.php | 8 | ||||
-rw-r--r-- | lib/Service/Sync/SyncService.php | 6 | ||||
-rw-r--r-- | tests/Integration/Framework/ImapTest.php | 2 | ||||
-rw-r--r-- | tests/Integration/IMAP/MessageMapperTest.php | 203 | ||||
-rw-r--r-- | tests/Integration/Migration/MigrateImportantFromImapAndDbTest.php | 239 | ||||
-rw-r--r-- | tests/Unit/IMAP/MessageMapperTest.php | 65 | ||||
-rw-r--r-- | tests/Unit/Job/TestMigrateImportantJob.php | 322 | ||||
-rw-r--r-- | tests/Unit/Listener/DeleteDraftListenerTest.php | 2 | ||||
-rw-r--r-- | tests/Unit/Listener/FlagRepliedMessageListenerTest.php | 2 | ||||
-rw-r--r-- | tests/Unit/Migration/MigrateImportantFromImapAndDbTest.php | 239 | ||||
-rw-r--r-- | tests/Unit/Service/MailManagerTest.php | 6 |
18 files changed, 1391 insertions, 18 deletions
diff --git a/lib/BackgroundJob/MigrateImportantJob.php b/lib/BackgroundJob/MigrateImportantJob.php new file mode 100644 index 000000000..f04127435 --- /dev/null +++ b/lib/BackgroundJob/MigrateImportantJob.php @@ -0,0 +1,111 @@ +<?php +/** + * @copyright 2021 Anna Larch <anna.larch@nextcloud.com> + * + * @author Anna Larch <anna.larch@nextcloud.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Mail\BackgroundJob; + +use OCA\Mail\Account; +use OCA\Mail\Db\MailAccountMapper; +use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\MailboxMapper; + +use OCA\Mail\Exception\ServiceException; +use OCA\Mail\Migration\MigrateImportantFromImapAndDb; +use OCA\Mail\Service\MailManager; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; +use Psr\Log\LoggerInterface; + +class MigrateImportantJob extends QueuedJob { + + /** @var MailboxMapper */ + private $mailboxMapper; + + /** @var MailAccountMapper */ + private $mailAccountMapper; + + /** @var MailManager */ + private $mailManager; + + /** @var MigrateImportantFromImapAndDb */ + private $migration; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(MailboxMapper $mailboxMapper, + MailAccountMapper $mailAccountMapper, + MailManager $mailManager, + MigrateImportantFromImapAndDb $migration, + LoggerInterface $logger, + ITimeFactory $timeFactory + ) { + parent::__construct($timeFactory); + $this->mailboxMapper = $mailboxMapper; + $this->mailAccountMapper = $mailAccountMapper; + $this->mailManager = $mailManager; + $this->migration = $migration; + $this->logger = $logger; + } + + /** + * @param array $argument + */ + public function run($argument) { + $mailboxId = (int)$argument['mailboxId']; + try { + /** @var Mailbox $mailbox*/ + $mailbox = $this->mailboxMapper->findById($mailboxId); + } catch (DoesNotExistException $e) { + $this->logger->debug('Could not find mailbox <' . $mailboxId . '>'); + return; + } + + $accountId = $mailbox->getAccountId(); + try { + $mailAccount = $this->mailAccountMapper->findById($accountId); + } catch (DoesNotExistException $e) { + $this->logger->debug('Could not find account <' . $accountId . '>'); + return; + } + + $account = new Account($mailAccount); + if ($this->mailManager->isPermflagsEnabled($account, $mailbox->getName()) === false) { + $this->logger->debug('Permflags not enabled for <' . $accountId . '>'); + return; + } + + try { + $this->migration->migrateImportantOnImap($account, $mailbox); + } catch (ServiceException $e) { + $this->logger->debug('Could not flag messages on IMAP for mailbox <' . $mailboxId . '>.'); + } + + try { + $this->migration->migrateImportantFromDb($account, $mailbox); + } catch (ServiceException $e) { + $this->logger->debug('Could not flag messages from DB on IMAP for mailbox <' . $mailboxId . '>.'); + } + } +} diff --git a/lib/Db/MailboxMapper.php b/lib/Db/MailboxMapper.php index 801fd64ca..5e3e74c4e 100644 --- a/lib/Db/MailboxMapper.php +++ b/lib/Db/MailboxMapper.php @@ -66,6 +66,21 @@ class MailboxMapper extends QBMapper { } /** + * @return \Generator<int> + */ + public function findAllIds(): \Generator { + $qb = $this->db->getQueryBuilder(); + $qb->select('id') + ->from($this->getTableName()); + + $cursor = $qb->execute(); + while ($row = $cursor->fetch()) { + yield (int)$row['id']; + } + $cursor->closeCursor(); + } + + /** * @throws DoesNotExistException * @throws ServiceException */ @@ -246,4 +261,27 @@ class MailboxMapper extends QBMapper { ->where($qb2->expr()->in('id', $qb2->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY)); $query->execute(); } + + /** + * Get all UIDS for mail_messages.flag_important = true + * + * @return int[] + */ + public function findFlaggedImportantUids(int $mailboxId) : array { + $qb = $this->db->getQueryBuilder(); + $query = $qb->select('uid') + ->from('mail_messages') + ->where( + $qb->expr()->eq('mailbox_id', $qb->createNamedParameter($mailboxId)), + $qb->expr()->eq('flag_important', $qb->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)) + ); + + $cursor = $query->execute(); + $uids = array_map(function (array $row) { + return (int)$row['uid']; + }, $cursor->fetchAll()); + $cursor->closeCursor(); + + return $uids; + } } diff --git a/lib/IMAP/MessageMapper.php b/lib/IMAP/MessageMapper.php index 95161fd8a..112d93c6c 100644 --- a/lib/IMAP/MessageMapper.php +++ b/lib/IMAP/MessageMapper.php @@ -28,6 +28,7 @@ use Horde_Imap_Client_Base; use Horde_Imap_Client_Data_Fetch; use Horde_Imap_Client_Exception; use Horde_Imap_Client_Fetch_Query; +use Horde_Imap_Client_Search_Query; use Horde_Imap_Client_Ids; use Horde_Imap_Client_Socket; use Horde_Mime_Mail; @@ -327,12 +328,12 @@ class MessageMapper { */ public function addFlag(Horde_Imap_Client_Socket $client, Mailbox $mailbox, - int $uid, + array $uids, string $flag): void { $client->store( $mailbox->getName(), [ - 'ids' => new Horde_Imap_Client_Ids($uid), + 'ids' => new Horde_Imap_Client_Ids($uids), 'add' => [$flag], ] ); @@ -343,12 +344,12 @@ class MessageMapper { */ public function removeFlag(Horde_Imap_Client_Socket $client, Mailbox $mailbox, - int $uid, + array $uids, string $flag): void { $client->store( $mailbox->getName(), [ - 'ids' => new Horde_Imap_Client_Ids($uid), + 'ids' => new Horde_Imap_Client_Ids($uids), 'remove' => [$flag], ] ); @@ -356,6 +357,23 @@ class MessageMapper { /** * @param Horde_Imap_Client_Socket $client + * @param Mailbox $mailbox + * @param string $flag + * @return int[] + * + * @throws Horde_Imap_Client_Exception + */ + public function getFlagged(Horde_Imap_Client_Socket $client, + Mailbox $mailbox, + string $flag): array { + $query = new Horde_Imap_Client_Search_Query(); + $query->flag($flag, true); + $messages = $client->search($mailbox->getName(), $query); + return $messages['match']->ids ?? []; + } + + /** + * @param Horde_Imap_Client_Socket $client * @param string $mailbox * @param int $uid * diff --git a/lib/Listener/DeleteDraftListener.php b/lib/Listener/DeleteDraftListener.php index 469ef070b..8d89d229b 100644 --- a/lib/Listener/DeleteDraftListener.php +++ b/lib/Listener/DeleteDraftListener.php @@ -96,7 +96,7 @@ class DeleteDraftListener implements IEventListener { $this->messageMapper->addFlag( $client, $draftsMailbox, - $draft->getUid(), // TODO: the UID could be from another mailbox + [$draft->getUid()], // TODO: the UID could be from another mailbox Horde_Imap_Client::FLAG_DELETED ); } catch (Horde_Imap_Client_Exception $e) { diff --git a/lib/Listener/FlagRepliedMessageListener.php b/lib/Listener/FlagRepliedMessageListener.php index 823732a02..977b95faf 100644 --- a/lib/Listener/FlagRepliedMessageListener.php +++ b/lib/Listener/FlagRepliedMessageListener.php @@ -83,7 +83,7 @@ class FlagRepliedMessageListener implements IEventListener { $this->messageMapper->addFlag( $client, $mailbox, - $event->getRepliedMessageData()->getMessage()->getUid(), + [$event->getRepliedMessageData()->getMessage()->getUid()], Horde_Imap_Client::FLAG_ANSWERED ); } catch (Horde_Imap_Client_Exception $e) { diff --git a/lib/Migration/MigrateImportantFromImapAndDb.php b/lib/Migration/MigrateImportantFromImapAndDb.php new file mode 100644 index 000000000..17f13e912 --- /dev/null +++ b/lib/Migration/MigrateImportantFromImapAndDb.php @@ -0,0 +1,98 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2021 Anna Larch <anna@nextcloud.com> + * + * @author 2021 Anna Larch <anna@nextcloud.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @link https://github.com/nextcloud/mail/issues/25 + * @link https://github.com/nextcloud/mail/issues/4780 + */ + +namespace OCA\Mail\Migration; + +use Horde_Imap_Client_Exception; +use OCA\Mail\Account; +use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\Db\Tag; +use OCA\Mail\Exception\ServiceException; +use OCA\Mail\IMAP\IMAPClientFactory; +use OCA\Mail\IMAP\MessageMapper; +use Psr\Log\LoggerInterface; + +class MigrateImportantFromImapAndDb { + + /** @var IMAPClientFactory */ + private $clientFactory; + + /** @var MessageMapper */ + private $messageMapper; + + /** @var MailboxMapper */ + private $mailboxMapper; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(IMAPClientFactory $clientFactory, + MessageMapper $messageMapper, + MailboxMapper $mailboxMapper, + LoggerInterface $logger + ) { + $this->clientFactory = $clientFactory; + $this->messageMapper = $messageMapper; + $this->mailboxMapper = $mailboxMapper; + $this->logger = $logger; + } + + public function migrateImportantOnImap(Account $account, Mailbox $mailbox): void { + $client = $this->clientFactory->getClient($account); + //get all messages that have an $important label from IMAP + try { + $uids = $this->messageMapper->getFlagged($client, $mailbox, '$important'); + } catch (Horde_Imap_Client_Exception $e) { + throw new ServiceException("Could not fetch UIDs of important messages: " . $e->getMessage(), 0, $e); + } + // add $label1 for all that are tagged on IMAP + if (!empty($uids)) { + try { + $this->messageMapper->addFlag($client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + } catch (Horde_Imap_Client_Exception $e) { + $this->logger->debug('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + throw new ServiceException($e->getMessage(), 0, $e); + } + } + } + + public function migrateImportantFromDb(Account $account, Mailbox $mailbox): void { + $client = $this->clientFactory->getClient($account); + $uids = $this->mailboxMapper->findFlaggedImportantUids($mailbox->getId()); + // store our data on imap + if (!empty($uids)) { + try { + $this->messageMapper->addFlag($client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + } catch (Horde_Imap_Client_Exception $e) { + $this->logger->debug('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + throw new ServiceException($e->getMessage(), 0, $e); + } + } + } +} diff --git a/lib/Migration/Version1100Date20210512142306.php b/lib/Migration/Version1100Date20210512142306.php new file mode 100644 index 000000000..38d0de51c --- /dev/null +++ b/lib/Migration/Version1100Date20210512142306.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types=1); + +namespace OCA\Mail\Migration; + +use Closure; +use OCA\Mail\BackgroundJob\MigrateImportantJob; +use OCA\Mail\Db\MailboxMapper; +use OCP\BackgroundJob\IJobList; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version1100Date20210512142306 extends SimpleMigrationStep { + + /** @var MailboxMapper */ + private $mailboxMapper; + + /** @var IJobList */ + private $jobList; + + public function __construct(MailboxMapper $mailboxMapper, IJobList $jobList) { + $this->mailboxMapper = $mailboxMapper; + $this->jobList = $jobList; + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { + foreach ($this->mailboxMapper->findAllIds() as $mailboxId) { + $this->jobList->add(MigrateImportantJob::class, ['mailboxId' => $mailboxId]); + } + } +} diff --git a/lib/Service/MailManager.php b/lib/Service/MailManager.php index bd87e3b13..29030a000 100644 --- a/lib/Service/MailManager.php +++ b/lib/Service/MailManager.php @@ -388,9 +388,9 @@ class MailManager implements IMailManager { continue; } if ($value) { - $this->imapMessageMapper->addFlag($client, $mb, $uid, $imapFlag); + $this->imapMessageMapper->addFlag($client, $mb, [$uid], $imapFlag); } else { - $this->imapMessageMapper->removeFlag($client, $mb, $uid, $imapFlag); + $this->imapMessageMapper->removeFlag($client, $mb, [$uid], $imapFlag); } } } catch (Horde_Imap_Client_Exception $e) { @@ -438,9 +438,9 @@ class MailManager implements IMailManager { try { if ($value) { // imap keywords and flags work the same way - $this->imapMessageMapper->addFlag($client, $mb, $message->getUid(), $tag->getImapLabel()); + $this->imapMessageMapper->addFlag($client, $mb, [$message->getUid()], $tag->getImapLabel()); } else { - $this->imapMessageMapper->removeFlag($client, $mb, $message->getUid(), $tag->getImapLabel()); + $this->imapMessageMapper->removeFlag($client, $mb, [$message->getUid()], $tag->getImapLabel()); } } catch (Horde_Imap_Client_Exception $e) { throw new ServiceException( diff --git a/lib/Service/Sync/SyncService.php b/lib/Service/Sync/SyncService.php index f5ea294c5..9f56e383a 100644 --- a/lib/Service/Sync/SyncService.php +++ b/lib/Service/Sync/SyncService.php @@ -111,7 +111,7 @@ class SyncService { public function syncMailbox(Account $account, Mailbox $mailbox, int $criteria, - array $knownIds, + array $knownIds = null, bool $partialOnly, string $filter = null): Response { if ($partialOnly && !$mailbox->isCached()) { @@ -123,7 +123,7 @@ class SyncService { $mailbox, $this->logger, $criteria, - $this->messageMapper->findUidsForIds($mailbox, $knownIds), + $knownIds === null ? null : $this->messageMapper->findUidsForIds($mailbox, $knownIds), !$partialOnly ); @@ -133,7 +133,7 @@ class SyncService { return $this->getDatabaseSyncChanges( $account, $mailbox, - $knownIds, + $knownIds ?? [], $query ); } diff --git a/tests/Integration/Framework/ImapTest.php b/tests/Integration/Framework/ImapTest.php index c0b252fb1..1a68ad415 100644 --- a/tests/Integration/Framework/ImapTest.php +++ b/tests/Integration/Framework/ImapTest.php @@ -27,6 +27,7 @@ use Horde_Imap_Client_Fetch_Query; use Horde_Imap_Client_Ids; use Horde_Imap_Client_Socket; use Horde_Mail_Rfc822_Address; +use Horde_Mime_Headers_MessageId; use Horde_Mime_Mail; use Horde_Mime_Part; use OCA\Mail\Account; @@ -145,6 +146,7 @@ trait ImapTest { $mail = new Horde_Mime_Mail(); $mail->addHeaders($headers); + $mail->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $body = new Horde_Mime_Part(); $body->setType('text/plain'); $body->setContents($message->getBody()); diff --git a/tests/Integration/IMAP/MessageMapperTest.php b/tests/Integration/IMAP/MessageMapperTest.php new file mode 100644 index 000000000..c1faa022c --- /dev/null +++ b/tests/Integration/IMAP/MessageMapperTest.php @@ -0,0 +1,203 @@ +<?php + +declare(strict_types=1); + +/** + * @author Anna Larch <anna.larch@nextcloud.com> + * + * Mail + * + * 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\Mail\Tests\Integration\IMAP; + +use ChristophWurst\Nextcloud\Testing\TestCase; +use Horde_Imap_Client; +use Horde_Imap_Client_Exception; +use OC; +use OCA\Mail\Account; +use OCA\Mail\Contracts\IMailManager; +use OCA\Mail\IMAP\MessageMapper as ImapMessageMapper; +use OCA\Mail\Db\MessageMapper; +use OCA\Mail\Service\Sync\SyncService; +use OCA\Mail\Tests\Integration\Framework\ImapTest; +use OCA\Mail\Tests\Integration\Framework\ImapTestAccount; + +class MessageMapperTest extends TestCase { + use ImapTest, + ImapTestAccount; + + public function setUp():void { + parent::setUp(); + } + + public function testTagging() { + // First, set up account and retrieve sync token + $this->resetImapAccount(); + + $account = $this->createTestAccount(); + /** @var SyncService $syncService */ + $syncService = OC::$server->query(SyncService::class); + /** @var ImapMessageMapper $messageMapper */ + $imapMessageMapper = OC::$server->query(ImapMessageMapper::class); + /** @var MessageMapper $messageMapper */ + $messageMapper = OC::$server->query(MessageMapper::class); + /** @var IMailManager $mailManager */ + $mailManager = OC::$server->query(IMailManager::class); + $mailBoxes = $mailManager->getMailboxes(new Account($account)); + $inbox = null; + foreach ($mailBoxes as $mailBox) { + if ($mailBox->getName() === 'INBOX') { + $inbox = $mailBox; + break; + } + } + + // Second, put a new message into the mailbox + $message = $this->getMessageBuilder() + ->from('buffington@domain.tld') + ->to('user@domain.tld') + ->finish(); + $newUid = $this->saveMessage($inbox->getName(), $message, $account); + + // now we tag this message! + try { + $imapMessageMapper->addFlag($this->getClient($account), $mailBox, [$newUid], '$label1'); + } catch (Horde_Imap_Client_Exception $e) { + $this->fail('Could not tag message'); + } + + // sync + $syncService->syncMailbox( + new Account($account), + $inbox, + Horde_Imap_Client::SYNC_NEWMSGSUIDS | Horde_Imap_Client::SYNC_FLAGSUIDS | Horde_Imap_Client::SYNC_VANISHEDUIDS, + null, + false + ); + + // Let's retrieve the DB to see if we have this tag! + $messages = $messageMapper->findByUids($mailBox, [$newUid]); + $related = $messageMapper->findRelatedData($messages, $account->getUserId()); + foreach ($related as $message) { + $tags = $message->getTags(); + $this->assertEquals('$label1', $tags[0]->getImapLabel()); + } + + + // now we untag this message! + try { + $imapMessageMapper->removeFlag($this->getClient($account), $mailBox, [$newUid], '$label1'); + } catch (Horde_Imap_Client_Exception $e) { + $this->fail('Could not untag message'); + } + + // sync again + $syncService->syncMailbox( + new Account($account), + $inbox, + Horde_Imap_Client::SYNC_NEWMSGSUIDS | Horde_Imap_Client::SYNC_FLAGSUIDS | Horde_Imap_Client::SYNC_VANISHEDUIDS, + null, + true + ); + + $messages = $messageMapper->findByUids($mailBox, [$newUid]); + $related = $messageMapper->findRelatedData($messages, $account->getUserId()); + foreach ($related as $message) { + $tags = $message->getTags(); + $this->assertEmpty($tags); + } + } + + public function testGetFlagged() { + // First, set up account and retrieve sync token + $this->resetImapAccount(); + + $account = $this->createTestAccount(); + /** @var ImapMessageMapper $messageMapper */ + $imapMessageMapper = OC::$server->query(ImapMessageMapper::class); + /** @var IMailManager $mailManager */ + $mailManager = OC::$server->query(IMailManager::class); + $mailBoxes = $mailManager->getMailboxes(new Account($account)); + $inbox = null; + foreach ($mailBoxes as $mailBox) { + if ($mailBox->getName() === 'INBOX') { + $inbox = $mailBox; + break; + } + } + + // Put a second new message into the mailbox + $message = $this->getMessageBuilder() + ->from('buffington@domain.tld') + ->to('user@domain.tld') + ->finish(); + $newUid = $this->saveMessage($inbox->getName(), $message, $account); + + + // Put another new message into the mailbox + $message = $this->getMessageBuilder() + ->from('fluffington@domain.tld') + ->to('user@domain.tld') + ->finish(); + $newUid2 = $this->saveMessage($inbox->getName(), $message, $account); + + // Thirdly, create a message that will not be tagged + $message = $this->getMessageBuilder() + ->from('scruffington@domain.tld') + ->to('user@domain.tld') + ->finish(); + $this->saveMessage($inbox->getName(), $message, $account); + + // now we tag this message with $label1 + try { + $imapMessageMapper->addFlag($this->getClient($account), $mailBox, [$newUid], '$label1'); + } catch (Horde_Imap_Client_Exception $e) { + $this->fail('Could not tag message'); + } + + + // now we tag this and the previous message with $label2 + try { + $imapMessageMapper->addFlag($this->getClient($account), $mailBox, [$newUid, $newUid2], '$label2'); + } catch (Horde_Imap_Client_Exception $e) { + $this->fail('Could not tag message'); + } + + // test for labels + $tagged = $imapMessageMapper->getFlagged($this->getClient($account), $mailBox, '$label1'); + $this->assertNotEmpty($tagged); + // are the counts correct? + $this->assertCount(1, $tagged); + + $tagged = $imapMessageMapper->getFlagged($this->getClient($account), $mailBox, '$label2'); + $this->assertNotEmpty($tagged); + $this->assertCount(2, $tagged); + + // test for labels that wasn't set + $tagged = $imapMessageMapper->getFlagged($this->getClient($account), $mailBox, '$notAvailable'); + $this->assertEmpty($tagged); + + // test for regular flag - recent + $tagged = $imapMessageMapper->getFlagged($this->getClient($account), $mailBox, Horde_Imap_Client::FLAG_RECENT); + $this->assertNotEmpty($tagged); + // should return all messages + $this->assertCount(3, $tagged); + } + + public function tearDown(): void { + $this->resetImapAccount(); + } +} diff --git a/tests/Integration/Migration/MigrateImportantFromImapAndDbTest.php b/tests/Integration/Migration/MigrateImportantFromImapAndDbTest.php new file mode 100644 index 000000000..548d63cef --- /dev/null +++ b/tests/Integration/Migration/MigrateImportantFromImapAndDbTest.php @@ -0,0 +1,239 @@ +<?php + +/** + * @copyright 2021 Anna Larch <anna.larch@nextcloud.com> + * + * @author 2021 Anna Larch <anna.larch@nextcloud.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Mail\Tests\Integration\Service; + +use ChristophWurst\Nextcloud\Testing\TestCase; +use Horde_Imap_Client_Exception; +use Horde_Imap_Client_Socket; +use OCA\Mail\Account; +use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\IMAP\MessageMapper; +use OCA\Mail\Db\Tag; +use OCA\Mail\Exception\ServiceException; +use OCA\Mail\IMAP\IMAPClientFactory; +use OCA\Mail\Migration\MigrateImportantFromImapAndDb; +use Psr\Log\LoggerInterface; + +class MigrateImportantFromImapAndDbTest extends TestCase { + + /** @var MockObject */ + private $clientFactory; + + /** @var MockObject */ + private $client; + + /** @var MockObject */ + private $messageMapper; + + /** @var MockObject */ + private $mailboxMapper; + + /** @var MockObject */ + private $logger; + + /** @var MigrateImportantFromImapAndDb */ + private $migration; + + protected function setUp(): void { + $this->clientFactory = $this->createMock(IMAPClientFactory::class); + $this->client = $this->createMock(Horde_Imap_Client_Socket::class); + $this->messageMapper = $this->createMock(MessageMapper::class); + $this->mailboxMapper = $this->createMock(MailboxMapper::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->migration = new MigrateImportantFromImapAndDb( + $this->clientFactory, + $this->messageMapper, + $this->mailboxMapper, + $this->logger + ); + parent::setUp(); + } + + public function testMigrateImportantOnImap() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapNoUids() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $uids = []; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapExceptionGetFlagged() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $e = new Horde_Imap_Client_Exception(''); + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willThrowException($e); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapExceptionOnFlag() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setName('INBOX'); + $e = new Horde_Imap_Client_Exception(''); + $uids = [1,2,3,4]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function migrateImportantFromDb() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } + + public function testMigrateImportantFromDbNoUids() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $uids = []; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } + + public function testMigrateImportantFromDbExceptionOnFlag() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $mailbox->setName('INBOX'); + $e = new Horde_Imap_Client_Exception(''); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } +} diff --git a/tests/Unit/IMAP/MessageMapperTest.php b/tests/Unit/IMAP/MessageMapperTest.php index 02625c302..80011fe20 100644 --- a/tests/Unit/IMAP/MessageMapperTest.php +++ b/tests/Unit/IMAP/MessageMapperTest.php @@ -30,6 +30,7 @@ use Horde_Imap_Client_Fetch_Query; use Horde_Imap_Client_Fetch_Results; use Horde_Imap_Client_Ids; use Horde_Imap_Client_Socket; +use OCA\Mail\Db\Mailbox; use OCA\Mail\IMAP\MessageMapper; use OCA\Mail\Model\IMAPMessage; use PHPUnit\Framework\MockObject\MockObject; @@ -236,4 +237,68 @@ class MessageMapperTest extends TestCase { 300 ); } + + public function testGetFlagged() { + /** @var Horde_Imap_Client_Socket|MockObject $imapClient */ + $imapClient = $this->createMock(Horde_Imap_Client_Socket::class); + $mailbox = new Mailbox(); + $mailbox->setName('inbox'); + $flag = '$label1'; + $idsObject = new Horde_Imap_Client_Ids(1); + + $searchResult = [ + 'count' => 1, + 'match' => $idsObject, + 'max' => 1, + 'min' => 1, + 'relevancy' => [] + ]; + + $imapClient->expects($this->once()) + ->method('search') + ->willReturn($searchResult); + + $result = $this->mapper->getFlagged($imapClient, $mailbox, $flag); + $this->assertEquals($result, [1]); + } + + public function testGetFlaggedNoMatches() { + /** @var Horde_Imap_Client_Socket|MockObject $imapClient */ + $imapClient = $this->createMock(Horde_Imap_Client_Socket::class); + $mailbox = new Mailbox(); + $mailbox->setName('inbox'); + $flag = '$label1'; + + $searchResult = [ + 'count' => 0, + 'match' => [], + 'max' => 0, + 'min' => 0, + 'relevancy' => [] + ]; + + $imapClient->expects($this->once()) + ->method('search') + ->willReturn($searchResult); + + $result = $this->mapper->getFlagged($imapClient, $mailbox, $flag); + $this->assertEquals($result, []); + } + + public function testGetFlaggedSearchResultUnexpectedStructure() { + /** @var Horde_Imap_Client_Socket|MockObject $imapClient */ + $imapClient = $this->createMock(Horde_Imap_Client_Socket::class); + $mailbox = new Mailbox(); + $mailbox->setName('inbox'); + $flag = '$label1'; + + $searchResult = [[]]; + + $imapClient->expects($this->once()) + ->method('search') + ->willReturn($searchResult); + + $result = $this->mapper->getFlagged($imapClient, $mailbox, $flag); + $this->assertEquals($result, []); + } } diff --git a/tests/Unit/Job/TestMigrateImportantJob.php b/tests/Unit/Job/TestMigrateImportantJob.php new file mode 100644 index 000000000..095625065 --- /dev/null +++ b/tests/Unit/Job/TestMigrateImportantJob.php @@ -0,0 +1,322 @@ +<?php + +declare(strict_types=1); + +/** + * @author Anna Larch <anna.larch@nextcloud.com> + * + * Mail + * + * 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\Mail\Tests\Unit\Job; + +use ChristophWurst\Nextcloud\Testing\TestCase; +use OCA\Mail\Account; +use OCA\Mail\BackgroundJob\MigrateImportantJob; +use OCA\Mail\Db\MailAccount; +use OCA\Mail\Db\MailAccountMapper; +use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\Exception\ServiceException; +use OCA\Mail\Migration\MigrateImportantFromImapAndDb; +use OCA\Mail\Service\MailManager; +use OCA\Mail\Tests\Integration\Framework\ImapTest; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + +class TestMigrateImportantJob extends TestCase { + use ImapTest; + + /** @var MailboxMapper|MockObject */ + private $mailboxMapper; + + /** @var MailAccountMapper|MockObject */ + private $mailAccountMapper; + + /** @var MailManager|MockObject */ + private $mailManager; + + /** @var MigrateImportantFromImapAndDb|MockObject */ + private $migration; + + /** @var LoggerInterface|MockObject */ + private $logger; + + /** @var IJobList|MockObject */ + private $jobList; + + /** @var MigrateImportantJob */ + private $job; + + /** @var [] */ + private static $argument; + + protected function setUp(): void { + parent::setUp(); + $this->mailboxMapper = $this->createMock(MailboxMapper::class); + $this->mailAccountMapper = $this->createMock(MailAccountMapper::class); + $this->mailManager = $this->createMock(MailManager::class); + $this->migration = $this->createMock(MigrateImportantFromImapAndDb::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->job = new MigrateImportantJob( + $this->mailboxMapper, + $this->mailAccountMapper, + $this->mailManager, + $this->migration, + $this->logger, + $this->jobList, + $this->createMock(ITimeFactory::class) + ); + + self::$argument = ['mailboxId' => 1]; + } + + public function testRun() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willReturn($mailAccount); + $this->mailManager->expects($this->once()) + ->method('isPermflagsEnabled') + ->with($account, $mailbox->getName()) + ->willReturn(true); + $this->migration->expects($this->once()) + ->method('migrateImportantOnImap') + ->with($account, $mailbox); + $this->migration->expects($this->once()) + ->method('migrateImportantFromDb') + ->with($account, $mailbox); + $this->jobList->expects($this->never()) + ->method('remove'); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->job->run(self::$argument); + } + + public function testRunNoMailbox() { + $e = new DoesNotExistException('does not exist'); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not find mailbox <' . self::$argument['mailboxId'] . '>, removing from jobs'); + $this->jobList->expects($this->once()) + ->method('remove') + ->with(MigrateImportantJob::class, self::$argument); + $this->mailAccountMapper->expects($this->never()) + ->method('findById'); + $this->mailManager->expects($this->never()) + ->method('isPermflagsEnabled'); + $this->migration->expects($this->never()) + ->method('migrateImportantOnImap'); + $this->migration->expects($this->never()) + ->method('migrateImportantFromDb'); + + $this->job->run(self::$argument); + } + + public function testRunNoAccount() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $e = new DoesNotExistException('does not exist'); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not find account <' . $mailbox->getAccountId() . '>, removing from jobs'); + $this->jobList->expects($this->once()) + ->method('remove') + ->with(MigrateImportantJob::class, self::$argument); + $this->mailManager->expects($this->never()) + ->method('isPermflagsEnabled'); + $this->migration->expects($this->never()) + ->method('migrateImportantOnImap'); + $this->migration->expects($this->never()) + ->method('migrateImportantFromDb'); + + $this->job->run(self::$argument); + } + + public function testNoPermflags() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willReturn($mailAccount); + $this->mailManager->expects($this->once()) + ->method('isPermflagsEnabled') + ->with($account, $mailbox->getName()) + ->willReturn(false); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Permflags not enabled for <' . $mailbox->getAccountId() . '>, removing from jobs'); + $this->jobList->expects($this->once()) + ->method('remove') + ->with(MigrateImportantJob::class, self::$argument); + + $this->job->run(self::$argument); + } + + public function testErrorOnMigrateOnImap() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + $e = new ServiceException(''); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willReturn($mailAccount); + $this->mailManager->expects($this->once()) + ->method('isPermflagsEnabled') + ->with($account, $mailbox->getName()) + ->willReturn(true); + $this->migration->expects($this->once()) + ->method('migrateImportantOnImap') + ->with($account, $mailbox) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages on IMAP for mailbox <' . $mailbox->getId() . '>.'); + $this->migration->expects($this->once()) + ->method('migrateImportantFromDb') + ->with($account, $mailbox); + $this->jobList->expects($this->never()) + ->method('remove'); + + $this->job->run(self::$argument); + } + + public function testErrorOnMigrateDbToImap() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + $e = new ServiceException(''); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willReturn($mailAccount); + $this->mailManager->expects($this->once()) + ->method('isPermflagsEnabled') + ->with($account, $mailbox->getName()) + ->willReturn(true); + $this->migration->expects($this->once()) + ->method('migrateImportantOnImap') + ->with($account, $mailbox); + $this->migration->expects($this->once()) + ->method('migrateImportantFromDb') + ->with($account, $mailbox) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages from DB on IMAP for mailbox <' . $mailbox->getId() . '>.'); + $this->jobList->expects($this->never()) + ->method('remove'); + + $this->job->run(self::$argument); + } + + public function testErrorOnOnBoth() { + $mailbox = new Mailbox(); + $mailbox->setId(self::$argument['mailboxId']); + $mailbox->setName('INBOX'); + $mailbox->setAccountId(1); + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + $e = new ServiceException(''); + + $this->mailboxMapper->expects($this->once()) + ->method('findById') + ->with(self::$argument['mailboxId']) + ->willReturn($mailbox); + $this->mailAccountMapper->expects($this->once()) + ->method('findById') + ->with($mailbox->getAccountId()) + ->willReturn($mailAccount); + $this->mailManager->expects($this->once()) + ->method('isPermflagsEnabled') + ->with($account, $mailbox->getName()) + ->willReturn(true); + $this->migration->expects($this->once()) + ->method('migrateImportantOnImap') + ->with($account, $mailbox) + ->willThrowException($e); + $this->migration->expects($this->once()) + ->method('migrateImportantFromDb') + ->with($account, $mailbox) + ->willThrowException($e); + $this->logger->expects($this->exactly(2)) + ->method('debug'); + $this->jobList->expects($this->never()) + ->method('remove'); + + $this->job->run(self::$argument); + } +} diff --git a/tests/Unit/Listener/DeleteDraftListenerTest.php b/tests/Unit/Listener/DeleteDraftListenerTest.php index 96503bdf2..fef2852f6 100644 --- a/tests/Unit/Listener/DeleteDraftListenerTest.php +++ b/tests/Unit/Listener/DeleteDraftListenerTest.php @@ -238,7 +238,7 @@ class DeleteDraftListenerTest extends TestCase { ->with( $client, $mailbox, - $uid, + [$uid], \Horde_Imap_Client::FLAG_DELETED ); $client->expects($this->once()) diff --git a/tests/Unit/Listener/FlagRepliedMessageListenerTest.php b/tests/Unit/Listener/FlagRepliedMessageListenerTest.php index 76433984d..20756a3d9 100644 --- a/tests/Unit/Listener/FlagRepliedMessageListenerTest.php +++ b/tests/Unit/Listener/FlagRepliedMessageListenerTest.php @@ -150,7 +150,7 @@ class FlagRepliedMessageListenerTest extends TestCase { ->with( $client, $mailbox, - 321, + [321], \Horde_Imap_Client::FLAG_ANSWERED ); $this->logger->expects($this->never()) diff --git a/tests/Unit/Migration/MigrateImportantFromImapAndDbTest.php b/tests/Unit/Migration/MigrateImportantFromImapAndDbTest.php new file mode 100644 index 000000000..58b799000 --- /dev/null +++ b/tests/Unit/Migration/MigrateImportantFromImapAndDbTest.php @@ -0,0 +1,239 @@ +<?php + +/** + * @copyright 2021 Anna Larch <anna.larch@nextcloud.com> + * + * @author 2021 Anna Larch <anna.larch@nextcloud.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Mail\Tests\Unit\Migration; + +use ChristophWurst\Nextcloud\Testing\TestCase; +use Horde_Imap_Client_Exception; +use Horde_Imap_Client_Socket; +use OCA\Mail\Account; +use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\IMAP\MessageMapper; +use OCA\Mail\Db\Tag; +use OCA\Mail\Exception\ServiceException; +use OCA\Mail\IMAP\IMAPClientFactory; +use OCA\Mail\Migration\MigrateImportantFromImapAndDb; +use Psr\Log\LoggerInterface; + +class MigrateImportantFromImapAndDbTest extends TestCase { + + /** @var MockObject */ + private $clientFactory; + + /** @var MockObject */ + private $client; + + /** @var MockObject */ + private $messageMapper; + + /** @var MockObject */ + private $mailboxMapper; + + /** @var MockObject */ + private $logger; + + /** @var MigrateImportantFromImapAndDb */ + private $migration; + + protected function setUp(): void { + $this->clientFactory = $this->createMock(IMAPClientFactory::class); + $this->client = $this->createMock(Horde_Imap_Client_Socket::class); + $this->messageMapper = $this->createMock(MessageMapper::class); + $this->mailboxMapper = $this->createMock(MailboxMapper::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->migration = new MigrateImportantFromImapAndDb( + $this->clientFactory, + $this->messageMapper, + $this->mailboxMapper, + $this->logger + ); + parent::setUp(); + } + + public function testMigrateImportantOnImap() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapNoUids() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $uids = []; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapExceptionGetFlagged() { + $account = $this->createMock(Account::class); + $mailbox = $this->createMock(Mailbox::class); + $e = new Horde_Imap_Client_Exception(''); + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willThrowException($e); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function testMigrateImportantOnImapExceptionOnFlag() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setName('INBOX'); + $e = new Horde_Imap_Client_Exception(''); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->messageMapper->expects($this->once()) + ->method('getFlagged') + ->with($this->client, $mailbox, '$important') + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantOnImap($account, $mailbox); + } + + public function migrateImportantFromDb() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } + + public function testMigrateImportantFromDbNoUids() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $uids = []; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->never()) + ->method('addFlag'); + $this->logger->expects($this->never()) + ->method('debug'); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } + + public function testMigrateImportantFromDbExceptionOnFlag() { + $account = $this->createMock(Account::class); + $mailbox = new Mailbox(); + $mailbox->setId(1); + $mailbox->setName('INBOX'); + $e = new Horde_Imap_Client_Exception(''); + $uids = [1,2,3]; + + $this->clientFactory->expects($this->once()) + ->method('getClient') + ->with($account) + ->willReturn($this->client); + $this->mailboxMapper->expects($this->once()) + ->method('findFlaggedImportantUids') + ->with($mailbox->getId()) + ->willReturn($uids); + $this->messageMapper->expects($this->once()) + ->method('addFlag') + ->with($this->client, $mailbox, $uids, Tag::LABEL_IMPORTANT) + ->willThrowException($e); + $this->logger->expects($this->once()) + ->method('debug') + ->with('Could not flag messages in mailbox <' . $mailbox->getId() . '>'); + $this->expectException(ServiceException::class); + + $this->migration->migrateImportantFromDb($account, $mailbox); + } +} diff --git a/tests/Unit/Service/MailManagerTest.php b/tests/Unit/Service/MailManagerTest.php index f42b586d7..3557027a1 100644 --- a/tests/Unit/Service/MailManagerTest.php +++ b/tests/Unit/Service/MailManagerTest.php @@ -422,7 +422,7 @@ class MailManagerTest extends TestCase { ->method('addFlag'); $this->imapMessageMapper->expects($this->once()) ->method('removeFlag') - ->with($client, $mb, 123, '\\seen'); + ->with($client, $mb, [123], '\\seen'); $this->manager->flagMessage($account, 'INBOX', 123, 'seen', false); } @@ -448,7 +448,7 @@ class MailManagerTest extends TestCase { ->willReturn(['permflags' => [ "11" => "\*"] ]); $this->imapMessageMapper->expects($this->once()) ->method('addFlag') - ->with($client, $mb, 123, Tag::LABEL_IMPORTANT); + ->with($client, $mb, [123], Tag::LABEL_IMPORTANT); $account->expects($this->once()) ->method('getUserId') ->willReturn('test'); @@ -476,7 +476,7 @@ class MailManagerTest extends TestCase { ->willReturn(['permflags' => [ "11" => "\*"] ]); $this->imapMessageMapper->expects($this->once()) ->method('removeFlag') - ->with($client, $mb, 123, Tag::LABEL_IMPORTANT); + ->with($client, $mb, [123], Tag::LABEL_IMPORTANT); $this->imapMessageMapper->expects($this->never()) ->method('addFlag'); $account->expects($this->never()) |